From 680c74026d1f12036875c3ee945ff6439027d7eb Mon Sep 17 00:00:00 2001 From: Damien Cauquil Date: Tue, 11 Apr 2023 17:32:20 +0200 Subject: [PATCH 001/263] Ported Qemu RH850 architecture to Unicorn (untested code, compilation OK). --- CMakeLists.txt | 44 +- bindings/const_generator.py | 8 +- bindings/python/unicorn/rh850_const.py | 91 + bindings/python/unicorn/unicorn_const.py | 1 + include/unicorn/rh850.h | 111 + include/unicorn/unicorn.h | 2 + qemu/configure | 7 +- qemu/rh850.h | 1280 ++++++ qemu/target/rh850/Makefile.objs | 1 + qemu/target/rh850/cpu-param.h | 11 + qemu/target/rh850/cpu.c | 639 +++ qemu/target/rh850/cpu.h | 322 ++ qemu/target/rh850/cpu_bits.h | 418 ++ qemu/target/rh850/cpu_user.h | 13 + qemu/target/rh850/fpu_helper.c | 371 ++ qemu/target/rh850/gdbstub.c | 179 + qemu/target/rh850/helper.c | 500 +++ qemu/target/rh850/helper.h | 82 + qemu/target/rh850/instmap.h | 420 ++ qemu/target/rh850/op_helper.c | 669 +++ qemu/target/rh850/pmp.c | 379 ++ qemu/target/rh850/pmp.h | 64 + qemu/target/rh850/register_indices.h | 63 + qemu/target/rh850/translate.c | 5219 ++++++++++++++++++++++ qemu/target/rh850/unicorn.c | 195 + qemu/target/rh850/unicorn.h | 19 + symbols.sh | 2 +- uc.c | 4 + 28 files changed, 11110 insertions(+), 4 deletions(-) create mode 100644 bindings/python/unicorn/rh850_const.py create mode 100644 include/unicorn/rh850.h create mode 100644 qemu/rh850.h create mode 100644 qemu/target/rh850/Makefile.objs create mode 100644 qemu/target/rh850/cpu-param.h create mode 100644 qemu/target/rh850/cpu.c create mode 100644 qemu/target/rh850/cpu.h create mode 100644 qemu/target/rh850/cpu_bits.h create mode 100644 qemu/target/rh850/cpu_user.h create mode 100644 qemu/target/rh850/fpu_helper.c create mode 100644 qemu/target/rh850/gdbstub.c create mode 100644 qemu/target/rh850/helper.c create mode 100644 qemu/target/rh850/helper.h create mode 100644 qemu/target/rh850/instmap.h create mode 100644 qemu/target/rh850/op_helper.c create mode 100644 qemu/target/rh850/pmp.c create mode 100644 qemu/target/rh850/pmp.h create mode 100644 qemu/target/rh850/register_indices.h create mode 100644 qemu/target/rh850/translate.c create mode 100644 qemu/target/rh850/unicorn.c create mode 100644 qemu/target/rh850/unicorn.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 4110ae5356..dcc27afcd3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,7 +58,7 @@ option(BUILD_SHARED_LIBS "Build shared instead of static library" ${PROJECT_IS_T option(UNICORN_FUZZ "Enable fuzzing" OFF) option(UNICORN_BUILD_TESTS "Build unicorn tests" ${PROJECT_IS_TOP_LEVEL}) option(UNICORN_INSTALL "Enable unicorn installation" ${PROJECT_IS_TOP_LEVEL}) -set(UNICORN_ARCH "x86;arm;aarch64;riscv;mips;sparc;m68k;ppc;s390x;tricore" CACHE STRING "Enabled unicorn architectures") +set(UNICORN_ARCH "x86;arm;aarch64;riscv;mips;sparc;m68k;ppc;rh850;s390x;tricore" CACHE STRING "Enabled unicorn architectures") option(UNICORN_TRACER "Trace unicorn execution" OFF) foreach(ARCH_LOOP ${UNICORN_ARCH}) @@ -254,6 +254,9 @@ else() if(UNICORN_HAS_PPC) set(EXTRA_CFLAGS "${EXTRA_CFLAGS}-DUNICORN_HAS_PPC ") endif() + if(UNICORN_HAS_RH850) + set(EXTRA_CFLAGS "${EXTRA_CFLAGS}-DUNICORN_HAS_RH850 ") + endif() if(UNICORN_HAS_RISCV) set(EXTRA_CFLAGS "${EXTRA_CFLAGS}-DUNICORN_HAS_RISCV ") endif() @@ -298,6 +301,9 @@ else() if(UNICORN_HAS_PPC) set(TARGET_LIST "${TARGET_LIST}ppc-softmmu, ppc64-softmmu, ") endif() + if(UNICORN_HAS_RH850) + set(TARGET_LIST "${TARGET_LIST}rh850-softmmu, ") + endif() if(UNICORN_HAS_RISCV) set(TARGET_LIST "${TARGET_LIST}riscv32-softmmu, riscv64-softmmu, ") endif() @@ -382,6 +388,12 @@ else() OUTPUT_FILE ${CMAKE_BINARY_DIR}/ppc64-softmmu/config-target.h ) endif() + if(UNICORN_HAS_RH850) + execute_process(COMMAND sh ${CMAKE_CURRENT_SOURCE_DIR}/qemu/scripts/create_config + INPUT_FILE ${CMAKE_BINARY_DIR}/rh850-softmmu/config-target.mak + OUTPUT_FILE ${CMAKE_BINARY_DIR}/rh850-softmmu/config-target.h + ) + endif() if(UNICORN_HAS_RISCV) execute_process(COMMAND sh ${CMAKE_CURRENT_SOURCE_DIR}/qemu/scripts/create_config INPUT_FILE ${CMAKE_BINARY_DIR}/riscv32-softmmu/config-target.mak @@ -1095,6 +1107,36 @@ endif() endif() +if (UNICORN_HAS_RH850) +add_library(rh850-softmmu STATIC + ${UNICORN_ARCH_COMMON} + + qemu/target/rh850/cpu.c + qemu/target/rh850/fpu_helper.c + qemu/target/rh850/helper.c + qemu/target/rh850/op_helper.c + qemu/target/rh850/translate.c + qemu/target/rh850/pmp.c + qemu/target/rh850/unicorn.c +) + +if(MSVC) + target_compile_options(rh850-softmmu PRIVATE + -DNEED_CPU_H + /FIrh850.h + /I${CMAKE_CURRENT_SOURCE_DIR}/msvc/rh850-softmmu + /I${CMAKE_CURRENT_SOURCE_DIR}/qemu/target/rh850 + ) +else() + target_compile_options(rh850-softmmu PRIVATE + -DNEED_CPU_H + -include rh850.h + -I${CMAKE_BINARY_DIR}/rh850-softmmu + -I${CMAKE_CURRENT_SOURCE_DIR}/qemu/target/rh850 + ) +endif() +endif() + set(UNICORN_SRCS uc.c diff --git a/bindings/const_generator.py b/bindings/const_generator.py index 2a74c86425..793ab3925c 100644 --- a/bindings/const_generator.py +++ b/bindings/const_generator.py @@ -6,7 +6,7 @@ INCL_DIR = os.path.join('..', 'include', 'unicorn') -include = [ 'arm.h', 'arm64.h', 'mips.h', 'x86.h', 'sparc.h', 'm68k.h', 'ppc.h', 'riscv.h', 's390x.h', 'tricore.h', 'unicorn.h' ] +include = [ 'arm.h', 'arm64.h', 'mips.h', 'x86.h', 'sparc.h', 'm68k.h', 'ppc.h', 'rh850.h', 'riscv.h', 's390x.h', 'tricore.h', 'unicorn.h' ] template = { 'python': { @@ -22,6 +22,7 @@ 'sparc.h': 'sparc', 'm68k.h': 'm68k', 'ppc.h': 'ppc', + 'rh850.h': 'rh850', 'riscv.h': 'riscv', 's390x.h' : 's390x', 'tricore.h' : 'tricore', @@ -43,6 +44,7 @@ 'm68k.h': 'm68k', 'ppc.h': 'ppc', 'riscv.h': 'riscv', + 'rh850.h': 'rh850', 's390x.h' : 's390x', 'tricore.h' : 'tricore', 'unicorn.h': 'unicorn', @@ -62,6 +64,7 @@ 'sparc.h': 'sparc', 'm68k.h': 'm68k', 'ppc.h': 'ppc', + 'rh850.h': 'rh850', 'riscv.h': 'riscv', 's390x.h' : 's390x', 'tricore.h' : 'tricore', @@ -82,6 +85,7 @@ 'sparc.h': 'Sparc', 'm68k.h': 'M68k', 'ppc.h': 'Ppc', + 'rh850.h': 'Rh850', 'riscv.h': 'Riscv', 's390x.h' : 'S390x', 'tricore.h' : 'TriCore', @@ -102,6 +106,7 @@ 'sparc.h': 'Sparc', 'm68k.h': 'M68k', 'ppc.h': 'Ppc', + 'rh850.h': 'Rh850', 'riscv.h': 'Riscv', 's390x.h' : 'S390x', 'tricore.h' : 'TriCore', @@ -122,6 +127,7 @@ 'sparc.h': 'Sparc', 'm68k.h': 'M68k', 'ppc.h': 'Ppc', + 'rh850.h': 'Rh850', 'riscv.h': 'Riscv', 's390x.h' : 'S390x', 'tricore.h' : 'TriCore', diff --git a/bindings/python/unicorn/rh850_const.py b/bindings/python/unicorn/rh850_const.py new file mode 100644 index 0000000000..663cb3eda7 --- /dev/null +++ b/bindings/python/unicorn/rh850_const.py @@ -0,0 +1,91 @@ +# For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT [rh850_const.py] +UC_RH850_SYSREG_SELID0 = 32 +UC_RH850_SYSREG_SELID1 = 64 +UC_RH850_SYSREG_SELID2 = 96 +UC_RH850_SYSREG_SELID3 = 128 +UC_RH850_SYSREG_SELID4 = 160 +UC_RH850_SYSREG_SELID5 = 192 +UC_RH850_SYSREG_SELID6 = 224 +UC_RH850_SYSREG_SELID7 = 256 + +# RH850 global purpose registers + +UC_RH850_REG_R0 = 0 +UC_RH850_REG_R1 = 1 +UC_RH850_REG_R2 = 2 +UC_RH850_REG_R3 = 3 +UC_RH850_REG_R4 = 4 +UC_RH850_REG_R5 = 5 +UC_RH850_REG_R6 = 6 +UC_RH850_REG_R7 = 7 +UC_RH850_REG_R8 = 8 +UC_RH850_REG_R9 = 9 +UC_RH850_REG_R10 = 10 +UC_RH850_REG_R11 = 11 +UC_RH850_REG_R12 = 12 +UC_RH850_REG_R13 = 13 +UC_RH850_REG_R14 = 14 +UC_RH850_REG_R15 = 15 +UC_RH850_REG_R16 = 16 +UC_RH850_REG_R17 = 17 +UC_RH850_REG_R18 = 18 +UC_RH850_REG_R19 = 19 +UC_RH850_REG_R20 = 20 +UC_RH850_REG_R21 = 21 +UC_RH850_REG_R22 = 22 +UC_RH850_REG_R23 = 23 +UC_RH850_REG_R24 = 24 +UC_RH850_REG_R25 = 25 +UC_RH850_REG_R26 = 26 +UC_RH850_REG_R27 = 27 +UC_RH850_REG_R28 = 28 +UC_RH850_REG_R29 = 29 +UC_RH850_REG_R30 = 30 +UC_RH850_REG_R31 = 31 + +# RH850 system registers, selection ID 0 +UC_RH850_REG_EIPC = 32 +UC_RH850_REG_EIPSW = 33 +UC_RH850_REG_FEPC = 34 +UC_RH850_REG_FEPSW = 35 +UC_RH850_REG_ECR = 36 +UC_RH850_REG_PSW = 37 +UC_RH850_REG_FPSR = 38 +UC_RH850_REG_FPEPC = 39 +UC_RH850_REG_FPST = 40 +UC_RH850_REG_FPCC = 41 +UC_RH850_REG_FPCFG = 42 +UC_RH850_REG_FPEC = 43 +UC_RH850_REG_EIIC = 45 +UC_RH850_REG_FEIC = 46 +UC_RH850_REG_CTPC = 48 +UC_RH850_REG_CTPSW = 49 +UC_RH850_REG_CTBP = 52 +UC_RH850_REG_EIWR = 60 +UC_RH850_REG_FEWR = 61 +UC_RH850_REG_BSEL = 63 + +# RH850 system regusters, selection ID 1 +UC_RH850_REG_MCFG0 = 64 +UC_RH850_REG_RBASE = 65 +UC_RH850_REG_EBASE = 66 +UC_RH850_REG_INTBP = 67 +UC_RH850_REG_MCTL = 68 +UC_RH850_REG_PID = 69 +UC_RH850_REG_SCCFG = 75 +UC_RH850_REG_SCBP = 76 + +# RH850 system registers, selection ID 2 +UC_RH850_REG_HTCFG0 = 96 +UC_RH850_REG_MEA = 102 +UC_RH850_REG_ASID = 103 +UC_RH850_REG_MEI = 104 +UC_RH850_REG_PC = 288 +UC_RH850_REG_ENDING = 289 + +# RH8509 Registers aliases. + +UC_RH850_REG_ZERO = 0 +UC_RH850_REG_SP = 2 +UC_RH850_REG_EP = 30 +UC_RH850_REG_LP = 31 diff --git a/bindings/python/unicorn/unicorn_const.py b/bindings/python/unicorn/unicorn_const.py index f491dd26eb..d1dbd1c1b3 100644 --- a/bindings/python/unicorn/unicorn_const.py +++ b/bindings/python/unicorn/unicorn_const.py @@ -22,6 +22,7 @@ UC_ARCH_S390X = 9 UC_ARCH_TRICORE = 10 UC_ARCH_MAX = 11 +UC_ARCH_RH850 = 12 UC_MODE_LITTLE_ENDIAN = 0 UC_MODE_BIG_ENDIAN = 1073741824 diff --git a/include/unicorn/rh850.h b/include/unicorn/rh850.h new file mode 100644 index 0000000000..64b03e6f44 --- /dev/null +++ b/include/unicorn/rh850.h @@ -0,0 +1,111 @@ +/* Unicorn Engine */ +/* By Damien Cauquil , 2023 */ + +#ifndef UNICORN_RH850_H +#define UNICORN_RH850_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _MSRH850 +#pragma warRH850disable : 4201) +#endif + +#define UC_RH850_SYSREG_SELID0 32 +#define UC_RH850_SYSREG_SELID1 64 +#define UC_RH850_SYSREG_SELID2 96 +#define UC_RH850_SYSREG_SELID3 128 +#define UC_RH850_SYSREG_SELID4 160 +#define UC_RH850_SYSREG_SELID5 192 +#define UC_RH850_SYSREG_SELID6 224 +#define UC_RH850_SYSREG_SELID7 256 + +//> RH850 global purpose registers +typedef enum uc_rh850_reg { + UC_RH850_REG_R0 = 0, + UC_RH850_REG_R1, + UC_RH850_REG_R2, + UC_RH850_REG_R3, + UC_RH850_REG_R4, + UC_RH850_REG_R5, + UC_RH850_REG_R6, + UC_RH850_REG_R7, + UC_RH850_REG_R8, + UC_RH850_REG_R9, + UC_RH850_REG_R10, + UC_RH850_REG_R11, + UC_RH850_REG_R12, + UC_RH850_REG_R13, + UC_RH850_REG_R14, + UC_RH850_REG_R15, + UC_RH850_REG_R16, + UC_RH850_REG_R17, + UC_RH850_REG_R18, + UC_RH850_REG_R19, + UC_RH850_REG_R20, + UC_RH850_REG_R21, + UC_RH850_REG_R22, + UC_RH850_REG_R23, + UC_RH850_REG_R24, + UC_RH850_REG_R25, + UC_RH850_REG_R26, + UC_RH850_REG_R27, + UC_RH850_REG_R28, + UC_RH850_REG_R29, + UC_RH850_REG_R30, + UC_RH850_REG_R31, + + //> RH850 system registers, selection ID 0 + UC_RH850_REG_EIPC = UC_RH850_SYSREG_SELID0, + UC_RH850_REG_EIPSW, + UC_RH850_REG_FEPC, + UC_RH850_REG_FEPSW, + UC_RH850_REG_ECR, + UC_RH850_REG_PSW, + UC_RH850_REG_FPSR, + UC_RH850_REG_FPEPC, + UC_RH850_REG_FPST, + UC_RH850_REG_FPCC, + UC_RH850_REG_FPCFG, + UC_RH850_REG_FPEC, + UC_RH850_REG_EIIC = UC_RH850_SYSREG_SELID0 + 13, + UC_RH850_REG_FEIC, + UC_RH850_REG_CTPC = UC_RH850_SYSREG_SELID0 + 16, + UC_RH850_REG_CTPSW, + UC_RH850_REG_CTBP = UC_RH850_SYSREG_SELID0 + 20, + UC_RH850_REG_EIWR = UC_RH850_SYSREG_SELID0 + 28, + UC_RH850_REG_FEWR = UC_RH850_SYSREG_SELID0 + 29, + UC_RH850_REG_BSEL = UC_RH850_SYSREG_SELID0 + 31, + + //> RH850 system regusters, selection ID 1 + UC_RH850_REG_MCFG0 = UC_RH850_SYSREG_SELID1, + UC_RH850_REG_RBASE, + UC_RH850_REG_EBASE, + UC_RH850_REG_INTBP, + UC_RH850_REG_MCTL, + UC_RH850_REG_PID, + UC_RH850_REG_SCCFG = UC_RH850_SYSREG_SELID1 + 11, + UC_RH850_REG_SCBP, + + //> RH850 system registers, selection ID 2 + UC_RH850_REG_HTCFG0 = UC_RH850_SYSREG_SELID2, + UC_RH850_REG_MEA = UC_RH850_SYSREG_SELID2 + 6, + UC_RH850_REG_ASID, + UC_RH850_REG_MEI, + + UC_RH850_REG_PC = UC_RH850_SYSREG_SELID7 + 32, + UC_RH850_REG_ENDING +} uc_cpu_rh850; + +//> RH8509 Registers aliases. +#define UC_RH850_REG_ZERO UC_RH850_REG_R0 +#define UC_RH850_REG_SP UC_RH850_REG_R2 +#define UC_RH850_REG_EP UC_RH850_REG_R30 +#define UC_RH850_REG_LP UC_RH850_REG_R31 + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/include/unicorn/unicorn.h b/include/unicorn/unicorn.h index 4b169d761f..ca7535645b 100644 --- a/include/unicorn/unicorn.h +++ b/include/unicorn/unicorn.h @@ -33,6 +33,7 @@ typedef size_t uc_hook; #include "mips.h" #include "sparc.h" #include "ppc.h" +#include "rh850.h" #include "riscv.h" #include "s390x.h" #include "tricore.h" @@ -107,6 +108,7 @@ typedef enum uc_arch { UC_ARCH_S390X, // S390X architecture UC_ARCH_TRICORE, // TriCore architecture UC_ARCH_MAX, + UC_ARCH_RH850, // Renesas RH850 architecture (V850e3v2) } uc_arch; // Mode type diff --git a/qemu/configure b/qemu/configure index 80080d0d1d..cfe87a8e08 100755 --- a/qemu/configure +++ b/qemu/configure @@ -858,7 +858,7 @@ QEMU_CFLAGS="$CPU_CFLAGS $QEMU_CFLAGS" default_target_list="aarch64-softmmu \ arm-softmmu m68k-softmmu mips64el-softmmu mips64-softmmu mipsel-softmmu \ mips-softmmu ppc64-softmmu ppc-softmmu sparc64-softmmu sparc-softmmu \ - x86_64-softmmu riscv32-softmmu riscv64-softmmu s390x-softmmu \ + x86_64-softmmu rh850-softmmu riscv32-softmmu riscv64-softmmu s390x-softmmu \ tricore-softmmu" if test x"$show_help" = x"yes" ; then @@ -2715,6 +2715,11 @@ case "$target_name" in TARGET_SYSTBL_ABI=common,nospu,32 echo "TARGET_ABI32=y" >> $config_target_mak ;; + rh850) + TARGET_BASE_ARCH=rh850 + TARGET_ABI_DIR=rh850 + mttcg=no # system emulation is not supported for RH850 + ;; riscv32) TARGET_BASE_ARCH=riscv TARGET_ABI_DIR=riscv diff --git a/qemu/rh850.h b/qemu/rh850.h new file mode 100644 index 0000000000..02909f809b --- /dev/null +++ b/qemu/rh850.h @@ -0,0 +1,1280 @@ +/* Autogen header for Unicorn Engine - DONOT MODIFY */ +#ifndef UNICORN_AUTOGEN_rh850_H +#define UNICORN_AUTOGEN_rh850_H +#ifndef UNICORN_ARCH_POSTFIX +#define UNICORN_ARCH_POSTFIX _rh850 +#endif +#define uc_add_inline_hook uc_add_inline_hook_rh850 +#define uc_del_inline_hook uc_del_inline_hook_rh850 +#define tb_invalidate_phys_range tb_invalidate_phys_range_rh850 +#define use_idiv_instructions use_idiv_instructions_rh850 +#define arm_arch arm_arch_rh850 +#define tb_target_set_jmp_target tb_target_set_jmp_target_rh850 +#define have_bmi1 have_bmi1_rh850 +#define have_popcnt have_popcnt_rh850 +#define have_avx1 have_avx1_rh850 +#define have_avx2 have_avx2_rh850 +#define have_isa have_isa_rh850 +#define have_altivec have_altivec_rh850 +#define have_vsx have_vsx_rh850 +#define flush_icache_range flush_icache_range_rh850 +#define s390_facilities s390_facilities_rh850 +#define tcg_dump_op tcg_dump_op_rh850 +#define tcg_dump_ops tcg_dump_ops_rh850 +#define tcg_gen_and_i64 tcg_gen_and_i64_rh850 +#define tcg_gen_discard_i64 tcg_gen_discard_i64_rh850 +#define tcg_gen_ld16s_i64 tcg_gen_ld16s_i64_rh850 +#define tcg_gen_ld16u_i64 tcg_gen_ld16u_i64_rh850 +#define tcg_gen_ld32s_i64 tcg_gen_ld32s_i64_rh850 +#define tcg_gen_ld32u_i64 tcg_gen_ld32u_i64_rh850 +#define tcg_gen_ld8s_i64 tcg_gen_ld8s_i64_rh850 +#define tcg_gen_ld8u_i64 tcg_gen_ld8u_i64_rh850 +#define tcg_gen_ld_i64 tcg_gen_ld_i64_rh850 +#define tcg_gen_mov_i64 tcg_gen_mov_i64_rh850 +#define tcg_gen_movi_i64 tcg_gen_movi_i64_rh850 +#define tcg_gen_mul_i64 tcg_gen_mul_i64_rh850 +#define tcg_gen_or_i64 tcg_gen_or_i64_rh850 +#define tcg_gen_sar_i64 tcg_gen_sar_i64_rh850 +#define tcg_gen_shl_i64 tcg_gen_shl_i64_rh850 +#define tcg_gen_shr_i64 tcg_gen_shr_i64_rh850 +#define tcg_gen_st_i64 tcg_gen_st_i64_rh850 +#define tcg_gen_xor_i64 tcg_gen_xor_i64_rh850 +#define cpu_icount_to_ns cpu_icount_to_ns_rh850 +#define cpu_is_stopped cpu_is_stopped_rh850 +#define cpu_get_ticks cpu_get_ticks_rh850 +#define cpu_get_clock cpu_get_clock_rh850 +#define cpu_resume cpu_resume_rh850 +#define qemu_init_vcpu qemu_init_vcpu_rh850 +#define cpu_stop_current cpu_stop_current_rh850 +#define resume_all_vcpus resume_all_vcpus_rh850 +#define vm_start vm_start_rh850 +#define address_space_dispatch_compact address_space_dispatch_compact_rh850 +#define flatview_translate flatview_translate_rh850 +#define address_space_translate_for_iotlb address_space_translate_for_iotlb_rh850 +#define qemu_get_cpu qemu_get_cpu_rh850 +#define cpu_address_space_init cpu_address_space_init_rh850 +#define cpu_get_address_space cpu_get_address_space_rh850 +#define cpu_exec_unrealizefn cpu_exec_unrealizefn_rh850 +#define cpu_exec_initfn cpu_exec_initfn_rh850 +#define cpu_exec_realizefn cpu_exec_realizefn_rh850 +#define tb_invalidate_phys_addr tb_invalidate_phys_addr_rh850 +#define cpu_watchpoint_insert cpu_watchpoint_insert_rh850 +#define cpu_watchpoint_remove_by_ref cpu_watchpoint_remove_by_ref_rh850 +#define cpu_watchpoint_remove_all cpu_watchpoint_remove_all_rh850 +#define cpu_watchpoint_address_matches cpu_watchpoint_address_matches_rh850 +#define cpu_breakpoint_insert cpu_breakpoint_insert_rh850 +#define cpu_breakpoint_remove cpu_breakpoint_remove_rh850 +#define cpu_breakpoint_remove_by_ref cpu_breakpoint_remove_by_ref_rh850 +#define cpu_breakpoint_remove_all cpu_breakpoint_remove_all_rh850 +#define cpu_abort cpu_abort_rh850 +#define cpu_physical_memory_test_and_clear_dirty cpu_physical_memory_test_and_clear_dirty_rh850 +#define memory_region_section_get_iotlb memory_region_section_get_iotlb_rh850 +#define flatview_add_to_dispatch flatview_add_to_dispatch_rh850 +#define qemu_ram_get_host_addr qemu_ram_get_host_addr_rh850 +#define qemu_ram_get_offset qemu_ram_get_offset_rh850 +#define qemu_ram_get_used_length qemu_ram_get_used_length_rh850 +#define qemu_ram_is_shared qemu_ram_is_shared_rh850 +#define qemu_ram_pagesize qemu_ram_pagesize_rh850 +#define qemu_ram_alloc_from_ptr qemu_ram_alloc_from_ptr_rh850 +#define qemu_ram_alloc qemu_ram_alloc_rh850 +#define qemu_ram_free qemu_ram_free_rh850 +#define qemu_map_ram_ptr qemu_map_ram_ptr_rh850 +#define qemu_ram_block_host_offset qemu_ram_block_host_offset_rh850 +#define qemu_ram_block_from_host qemu_ram_block_from_host_rh850 +#define qemu_ram_addr_from_host qemu_ram_addr_from_host_rh850 +#define cpu_check_watchpoint cpu_check_watchpoint_rh850 +#define iotlb_to_section iotlb_to_section_rh850 +#define address_space_dispatch_new address_space_dispatch_new_rh850 +#define address_space_dispatch_free address_space_dispatch_free_rh850 +#define flatview_read_continue flatview_read_continue_rh850 +#define address_space_read_full address_space_read_full_rh850 +#define address_space_write address_space_write_rh850 +#define address_space_rw address_space_rw_rh850 +#define cpu_physical_memory_rw cpu_physical_memory_rw_rh850 +#define address_space_write_rom address_space_write_rom_rh850 +#define cpu_flush_icache_range cpu_flush_icache_range_rh850 +#define cpu_exec_init_all cpu_exec_init_all_rh850 +#define address_space_access_valid address_space_access_valid_rh850 +#define address_space_map address_space_map_rh850 +#define address_space_unmap address_space_unmap_rh850 +#define cpu_physical_memory_map cpu_physical_memory_map_rh850 +#define cpu_physical_memory_unmap cpu_physical_memory_unmap_rh850 +#define cpu_memory_rw_debug cpu_memory_rw_debug_rh850 +#define qemu_target_page_size qemu_target_page_size_rh850 +#define qemu_target_page_bits qemu_target_page_bits_rh850 +#define qemu_target_page_bits_min qemu_target_page_bits_min_rh850 +#define target_words_bigendian target_words_bigendian_rh850 +#define cpu_physical_memory_is_io cpu_physical_memory_is_io_rh850 +#define ram_block_discard_range ram_block_discard_range_rh850 +#define ramblock_is_pmem ramblock_is_pmem_rh850 +#define page_size_init page_size_init_rh850 +#define set_preferred_target_page_bits set_preferred_target_page_bits_rh850 +#define finalize_target_page_bits finalize_target_page_bits_rh850 +#define cpu_outb cpu_outb_rh850 +#define cpu_outw cpu_outw_rh850 +#define cpu_outl cpu_outl_rh850 +#define cpu_inb cpu_inb_rh850 +#define cpu_inw cpu_inw_rh850 +#define cpu_inl cpu_inl_rh850 +#define memory_map memory_map_rh850 +#define memory_map_io memory_map_io_rh850 +#define memory_map_ptr memory_map_ptr_rh850 +#define memory_unmap memory_unmap_rh850 +#define memory_free memory_free_rh850 +#define flatview_unref flatview_unref_rh850 +#define address_space_get_flatview address_space_get_flatview_rh850 +#define memory_region_transaction_begin memory_region_transaction_begin_rh850 +#define memory_region_transaction_commit memory_region_transaction_commit_rh850 +#define memory_region_init memory_region_init_rh850 +#define memory_region_access_valid memory_region_access_valid_rh850 +#define memory_region_dispatch_read memory_region_dispatch_read_rh850 +#define memory_region_dispatch_write memory_region_dispatch_write_rh850 +#define memory_region_init_io memory_region_init_io_rh850 +#define memory_region_init_ram_ptr memory_region_init_ram_ptr_rh850 +#define memory_region_size memory_region_size_rh850 +#define memory_region_set_readonly memory_region_set_readonly_rh850 +#define memory_region_get_ram_ptr memory_region_get_ram_ptr_rh850 +#define memory_region_from_host memory_region_from_host_rh850 +#define memory_region_get_ram_addr memory_region_get_ram_addr_rh850 +#define memory_region_add_subregion memory_region_add_subregion_rh850 +#define memory_region_del_subregion memory_region_del_subregion_rh850 +#define memory_region_find memory_region_find_rh850 +#define memory_listener_register memory_listener_register_rh850 +#define memory_listener_unregister memory_listener_unregister_rh850 +#define address_space_remove_listeners address_space_remove_listeners_rh850 +#define address_space_init address_space_init_rh850 +#define address_space_destroy address_space_destroy_rh850 +#define memory_region_init_ram memory_region_init_ram_rh850 +#define memory_mapping_list_add_merge_sorted memory_mapping_list_add_merge_sorted_rh850 +#define exec_inline_op exec_inline_op_rh850 +#define floatx80_default_nan floatx80_default_nan_rh850 +#define float_raise float_raise_rh850 +#define float16_is_quiet_nan float16_is_quiet_nan_rh850 +#define float16_is_signaling_nan float16_is_signaling_nan_rh850 +#define float32_is_quiet_nan float32_is_quiet_nan_rh850 +#define float32_is_signaling_nan float32_is_signaling_nan_rh850 +#define float64_is_quiet_nan float64_is_quiet_nan_rh850 +#define float64_is_signaling_nan float64_is_signaling_nan_rh850 +#define floatx80_is_quiet_nan floatx80_is_quiet_nan_rh850 +#define floatx80_is_signaling_nan floatx80_is_signaling_nan_rh850 +#define floatx80_silence_nan floatx80_silence_nan_rh850 +#define propagateFloatx80NaN propagateFloatx80NaN_rh850 +#define float128_is_quiet_nan float128_is_quiet_nan_rh850 +#define float128_is_signaling_nan float128_is_signaling_nan_rh850 +#define float128_silence_nan float128_silence_nan_rh850 +#define float16_add float16_add_rh850 +#define float16_sub float16_sub_rh850 +#define float32_add float32_add_rh850 +#define float32_sub float32_sub_rh850 +#define float64_add float64_add_rh850 +#define float64_sub float64_sub_rh850 +#define float16_mul float16_mul_rh850 +#define float32_mul float32_mul_rh850 +#define float64_mul float64_mul_rh850 +#define float16_muladd float16_muladd_rh850 +#define float32_muladd float32_muladd_rh850 +#define float64_muladd float64_muladd_rh850 +#define float16_div float16_div_rh850 +#define float32_div float32_div_rh850 +#define float64_div float64_div_rh850 +#define float16_to_float32 float16_to_float32_rh850 +#define float16_to_float64 float16_to_float64_rh850 +#define float32_to_float16 float32_to_float16_rh850 +#define float32_to_float64 float32_to_float64_rh850 +#define float64_to_float16 float64_to_float16_rh850 +#define float64_to_float32 float64_to_float32_rh850 +#define float16_round_to_int float16_round_to_int_rh850 +#define float32_round_to_int float32_round_to_int_rh850 +#define float64_round_to_int float64_round_to_int_rh850 +#define float16_to_int16_scalbn float16_to_int16_scalbn_rh850 +#define float16_to_int32_scalbn float16_to_int32_scalbn_rh850 +#define float16_to_int64_scalbn float16_to_int64_scalbn_rh850 +#define float32_to_int16_scalbn float32_to_int16_scalbn_rh850 +#define float32_to_int32_scalbn float32_to_int32_scalbn_rh850 +#define float32_to_int64_scalbn float32_to_int64_scalbn_rh850 +#define float64_to_int16_scalbn float64_to_int16_scalbn_rh850 +#define float64_to_int32_scalbn float64_to_int32_scalbn_rh850 +#define float64_to_int64_scalbn float64_to_int64_scalbn_rh850 +#define float16_to_int16 float16_to_int16_rh850 +#define float16_to_int32 float16_to_int32_rh850 +#define float16_to_int64 float16_to_int64_rh850 +#define float32_to_int16 float32_to_int16_rh850 +#define float32_to_int32 float32_to_int32_rh850 +#define float32_to_int64 float32_to_int64_rh850 +#define float64_to_int16 float64_to_int16_rh850 +#define float64_to_int32 float64_to_int32_rh850 +#define float64_to_int64 float64_to_int64_rh850 +#define float16_to_int16_round_to_zero float16_to_int16_round_to_zero_rh850 +#define float16_to_int32_round_to_zero float16_to_int32_round_to_zero_rh850 +#define float16_to_int64_round_to_zero float16_to_int64_round_to_zero_rh850 +#define float32_to_int16_round_to_zero float32_to_int16_round_to_zero_rh850 +#define float32_to_int32_round_to_zero float32_to_int32_round_to_zero_rh850 +#define float32_to_int64_round_to_zero float32_to_int64_round_to_zero_rh850 +#define float64_to_int16_round_to_zero float64_to_int16_round_to_zero_rh850 +#define float64_to_int32_round_to_zero float64_to_int32_round_to_zero_rh850 +#define float64_to_int64_round_to_zero float64_to_int64_round_to_zero_rh850 +#define float16_to_uint16_scalbn float16_to_uint16_scalbn_rh850 +#define float16_to_uint32_scalbn float16_to_uint32_scalbn_rh850 +#define float16_to_uint64_scalbn float16_to_uint64_scalbn_rh850 +#define float32_to_uint16_scalbn float32_to_uint16_scalbn_rh850 +#define float32_to_uint32_scalbn float32_to_uint32_scalbn_rh850 +#define float32_to_uint64_scalbn float32_to_uint64_scalbn_rh850 +#define float64_to_uint16_scalbn float64_to_uint16_scalbn_rh850 +#define float64_to_uint32_scalbn float64_to_uint32_scalbn_rh850 +#define float64_to_uint64_scalbn float64_to_uint64_scalbn_rh850 +#define float16_to_uint16 float16_to_uint16_rh850 +#define float16_to_uint32 float16_to_uint32_rh850 +#define float16_to_uint64 float16_to_uint64_rh850 +#define float32_to_uint16 float32_to_uint16_rh850 +#define float32_to_uint32 float32_to_uint32_rh850 +#define float32_to_uint64 float32_to_uint64_rh850 +#define float64_to_uint16 float64_to_uint16_rh850 +#define float64_to_uint32 float64_to_uint32_rh850 +#define float64_to_uint64 float64_to_uint64_rh850 +#define float16_to_uint16_round_to_zero float16_to_uint16_round_to_zero_rh850 +#define float16_to_uint32_round_to_zero float16_to_uint32_round_to_zero_rh850 +#define float16_to_uint64_round_to_zero float16_to_uint64_round_to_zero_rh850 +#define float32_to_uint16_round_to_zero float32_to_uint16_round_to_zero_rh850 +#define float32_to_uint32_round_to_zero float32_to_uint32_round_to_zero_rh850 +#define float32_to_uint64_round_to_zero float32_to_uint64_round_to_zero_rh850 +#define float64_to_uint16_round_to_zero float64_to_uint16_round_to_zero_rh850 +#define float64_to_uint32_round_to_zero float64_to_uint32_round_to_zero_rh850 +#define float64_to_uint64_round_to_zero float64_to_uint64_round_to_zero_rh850 +#define int64_to_float16_scalbn int64_to_float16_scalbn_rh850 +#define int32_to_float16_scalbn int32_to_float16_scalbn_rh850 +#define int16_to_float16_scalbn int16_to_float16_scalbn_rh850 +#define int64_to_float16 int64_to_float16_rh850 +#define int32_to_float16 int32_to_float16_rh850 +#define int16_to_float16 int16_to_float16_rh850 +#define int64_to_float32_scalbn int64_to_float32_scalbn_rh850 +#define int32_to_float32_scalbn int32_to_float32_scalbn_rh850 +#define int16_to_float32_scalbn int16_to_float32_scalbn_rh850 +#define int64_to_float32 int64_to_float32_rh850 +#define int32_to_float32 int32_to_float32_rh850 +#define int16_to_float32 int16_to_float32_rh850 +#define int64_to_float64_scalbn int64_to_float64_scalbn_rh850 +#define int32_to_float64_scalbn int32_to_float64_scalbn_rh850 +#define int16_to_float64_scalbn int16_to_float64_scalbn_rh850 +#define int64_to_float64 int64_to_float64_rh850 +#define int32_to_float64 int32_to_float64_rh850 +#define int16_to_float64 int16_to_float64_rh850 +#define uint64_to_float16_scalbn uint64_to_float16_scalbn_rh850 +#define uint32_to_float16_scalbn uint32_to_float16_scalbn_rh850 +#define uint16_to_float16_scalbn uint16_to_float16_scalbn_rh850 +#define uint64_to_float16 uint64_to_float16_rh850 +#define uint32_to_float16 uint32_to_float16_rh850 +#define uint16_to_float16 uint16_to_float16_rh850 +#define uint64_to_float32_scalbn uint64_to_float32_scalbn_rh850 +#define uint32_to_float32_scalbn uint32_to_float32_scalbn_rh850 +#define uint16_to_float32_scalbn uint16_to_float32_scalbn_rh850 +#define uint64_to_float32 uint64_to_float32_rh850 +#define uint32_to_float32 uint32_to_float32_rh850 +#define uint16_to_float32 uint16_to_float32_rh850 +#define uint64_to_float64_scalbn uint64_to_float64_scalbn_rh850 +#define uint32_to_float64_scalbn uint32_to_float64_scalbn_rh850 +#define uint16_to_float64_scalbn uint16_to_float64_scalbn_rh850 +#define uint64_to_float64 uint64_to_float64_rh850 +#define uint32_to_float64 uint32_to_float64_rh850 +#define uint16_to_float64 uint16_to_float64_rh850 +#define float16_min float16_min_rh850 +#define float16_minnum float16_minnum_rh850 +#define float16_minnummag float16_minnummag_rh850 +#define float16_max float16_max_rh850 +#define float16_maxnum float16_maxnum_rh850 +#define float16_maxnummag float16_maxnummag_rh850 +#define float32_min float32_min_rh850 +#define float32_minnum float32_minnum_rh850 +#define float32_minnummag float32_minnummag_rh850 +#define float32_max float32_max_rh850 +#define float32_maxnum float32_maxnum_rh850 +#define float32_maxnummag float32_maxnummag_rh850 +#define float64_min float64_min_rh850 +#define float64_minnum float64_minnum_rh850 +#define float64_minnummag float64_minnummag_rh850 +#define float64_max float64_max_rh850 +#define float64_maxnum float64_maxnum_rh850 +#define float64_maxnummag float64_maxnummag_rh850 +#define float16_compare float16_compare_rh850 +#define float16_compare_quiet float16_compare_quiet_rh850 +#define float32_compare float32_compare_rh850 +#define float32_compare_quiet float32_compare_quiet_rh850 +#define float64_compare float64_compare_rh850 +#define float64_compare_quiet float64_compare_quiet_rh850 +#define float16_scalbn float16_scalbn_rh850 +#define float32_scalbn float32_scalbn_rh850 +#define float64_scalbn float64_scalbn_rh850 +#define float16_sqrt float16_sqrt_rh850 +#define float32_sqrt float32_sqrt_rh850 +#define float64_sqrt float64_sqrt_rh850 +#define float16_default_nan float16_default_nan_rh850 +#define float32_default_nan float32_default_nan_rh850 +#define float64_default_nan float64_default_nan_rh850 +#define float128_default_nan float128_default_nan_rh850 +#define float16_silence_nan float16_silence_nan_rh850 +#define float32_silence_nan float32_silence_nan_rh850 +#define float64_silence_nan float64_silence_nan_rh850 +#define float16_squash_input_denormal float16_squash_input_denormal_rh850 +#define float32_squash_input_denormal float32_squash_input_denormal_rh850 +#define float64_squash_input_denormal float64_squash_input_denormal_rh850 +#define normalizeFloatx80Subnormal normalizeFloatx80Subnormal_rh850 +#define roundAndPackFloatx80 roundAndPackFloatx80_rh850 +#define normalizeRoundAndPackFloatx80 normalizeRoundAndPackFloatx80_rh850 +#define int32_to_floatx80 int32_to_floatx80_rh850 +#define int32_to_float128 int32_to_float128_rh850 +#define int64_to_floatx80 int64_to_floatx80_rh850 +#define int64_to_float128 int64_to_float128_rh850 +#define uint64_to_float128 uint64_to_float128_rh850 +#define float32_to_floatx80 float32_to_floatx80_rh850 +#define float32_to_float128 float32_to_float128_rh850 +#define float32_rem float32_rem_rh850 +#define float32_exp2 float32_exp2_rh850 +#define float32_log2 float32_log2_rh850 +#define float32_eq float32_eq_rh850 +#define float32_le float32_le_rh850 +#define float32_lt float32_lt_rh850 +#define float32_unordered float32_unordered_rh850 +#define float32_eq_quiet float32_eq_quiet_rh850 +#define float32_le_quiet float32_le_quiet_rh850 +#define float32_lt_quiet float32_lt_quiet_rh850 +#define float32_unordered_quiet float32_unordered_quiet_rh850 +#define float64_to_floatx80 float64_to_floatx80_rh850 +#define float64_to_float128 float64_to_float128_rh850 +#define float64_rem float64_rem_rh850 +#define float64_log2 float64_log2_rh850 +#define float64_eq float64_eq_rh850 +#define float64_le float64_le_rh850 +#define float64_lt float64_lt_rh850 +#define float64_unordered float64_unordered_rh850 +#define float64_eq_quiet float64_eq_quiet_rh850 +#define float64_le_quiet float64_le_quiet_rh850 +#define float64_lt_quiet float64_lt_quiet_rh850 +#define float64_unordered_quiet float64_unordered_quiet_rh850 +#define floatx80_to_int32 floatx80_to_int32_rh850 +#define floatx80_to_int32_round_to_zero floatx80_to_int32_round_to_zero_rh850 +#define floatx80_to_int64 floatx80_to_int64_rh850 +#define floatx80_to_int64_round_to_zero floatx80_to_int64_round_to_zero_rh850 +#define floatx80_to_float32 floatx80_to_float32_rh850 +#define floatx80_to_float64 floatx80_to_float64_rh850 +#define floatx80_to_float128 floatx80_to_float128_rh850 +#define floatx80_round floatx80_round_rh850 +#define floatx80_round_to_int floatx80_round_to_int_rh850 +#define floatx80_add floatx80_add_rh850 +#define floatx80_sub floatx80_sub_rh850 +#define floatx80_mul floatx80_mul_rh850 +#define floatx80_div floatx80_div_rh850 +#define floatx80_rem floatx80_rem_rh850 +#define floatx80_sqrt floatx80_sqrt_rh850 +#define floatx80_eq floatx80_eq_rh850 +#define floatx80_le floatx80_le_rh850 +#define floatx80_lt floatx80_lt_rh850 +#define floatx80_unordered floatx80_unordered_rh850 +#define floatx80_eq_quiet floatx80_eq_quiet_rh850 +#define floatx80_le_quiet floatx80_le_quiet_rh850 +#define floatx80_lt_quiet floatx80_lt_quiet_rh850 +#define floatx80_unordered_quiet floatx80_unordered_quiet_rh850 +#define float128_to_int32 float128_to_int32_rh850 +#define float128_to_int32_round_to_zero float128_to_int32_round_to_zero_rh850 +#define float128_to_int64 float128_to_int64_rh850 +#define float128_to_int64_round_to_zero float128_to_int64_round_to_zero_rh850 +#define float128_to_uint64 float128_to_uint64_rh850 +#define float128_to_uint64_round_to_zero float128_to_uint64_round_to_zero_rh850 +#define float128_to_uint32_round_to_zero float128_to_uint32_round_to_zero_rh850 +#define float128_to_uint32 float128_to_uint32_rh850 +#define float128_to_float32 float128_to_float32_rh850 +#define float128_to_float64 float128_to_float64_rh850 +#define float128_to_floatx80 float128_to_floatx80_rh850 +#define float128_round_to_int float128_round_to_int_rh850 +#define float128_add float128_add_rh850 +#define float128_sub float128_sub_rh850 +#define float128_mul float128_mul_rh850 +#define float128_div float128_div_rh850 +#define float128_rem float128_rem_rh850 +#define float128_sqrt float128_sqrt_rh850 +#define float128_eq float128_eq_rh850 +#define float128_le float128_le_rh850 +#define float128_lt float128_lt_rh850 +#define float128_unordered float128_unordered_rh850 +#define float128_eq_quiet float128_eq_quiet_rh850 +#define float128_le_quiet float128_le_quiet_rh850 +#define float128_lt_quiet float128_lt_quiet_rh850 +#define float128_unordered_quiet float128_unordered_quiet_rh850 +#define floatx80_compare floatx80_compare_rh850 +#define floatx80_compare_quiet floatx80_compare_quiet_rh850 +#define float128_compare float128_compare_rh850 +#define float128_compare_quiet float128_compare_quiet_rh850 +#define floatx80_scalbn floatx80_scalbn_rh850 +#define float128_scalbn float128_scalbn_rh850 +#define softfloat_init softfloat_init_rh850 +#define tcg_optimize tcg_optimize_rh850 +#define gen_new_label gen_new_label_rh850 +#define tcg_can_emit_vec_op tcg_can_emit_vec_op_rh850 +#define tcg_expand_vec_op tcg_expand_vec_op_rh850 +#define tcg_register_jit tcg_register_jit_rh850 +#define tcg_tb_insert tcg_tb_insert_rh850 +#define tcg_tb_remove tcg_tb_remove_rh850 +#define tcg_tb_lookup tcg_tb_lookup_rh850 +#define tcg_tb_foreach tcg_tb_foreach_rh850 +#define tcg_nb_tbs tcg_nb_tbs_rh850 +#define tcg_region_reset_all tcg_region_reset_all_rh850 +#define tcg_region_init tcg_region_init_rh850 +#define tcg_code_size tcg_code_size_rh850 +#define tcg_code_capacity tcg_code_capacity_rh850 +#define tcg_tb_phys_invalidate_count tcg_tb_phys_invalidate_count_rh850 +#define tcg_malloc_internal tcg_malloc_internal_rh850 +#define tcg_pool_reset tcg_pool_reset_rh850 +#define tcg_context_init tcg_context_init_rh850 +#define tcg_tb_alloc tcg_tb_alloc_rh850 +#define tcg_prologue_init tcg_prologue_init_rh850 +#define tcg_func_start tcg_func_start_rh850 +#define tcg_set_frame tcg_set_frame_rh850 +#define tcg_global_mem_new_internal tcg_global_mem_new_internal_rh850 +#define tcg_temp_new_internal tcg_temp_new_internal_rh850 +#define tcg_temp_new_vec tcg_temp_new_vec_rh850 +#define tcg_temp_new_vec_matching tcg_temp_new_vec_matching_rh850 +#define tcg_temp_free_internal tcg_temp_free_internal_rh850 +#define tcg_const_i32 tcg_const_i32_rh850 +#define tcg_const_i64 tcg_const_i64_rh850 +#define tcg_const_local_i32 tcg_const_local_i32_rh850 +#define tcg_const_local_i64 tcg_const_local_i64_rh850 +#define tcg_op_supported tcg_op_supported_rh850 +#define tcg_gen_callN tcg_gen_callN_rh850 +#define tcg_op_remove tcg_op_remove_rh850 +#define tcg_emit_op tcg_emit_op_rh850 +#define tcg_op_insert_before tcg_op_insert_before_rh850 +#define tcg_op_insert_after tcg_op_insert_after_rh850 +#define tcg_cpu_exec_time tcg_cpu_exec_time_rh850 +#define tcg_gen_code tcg_gen_code_rh850 +#define tcg_gen_op1 tcg_gen_op1_rh850 +#define tcg_gen_op2 tcg_gen_op2_rh850 +#define tcg_gen_op3 tcg_gen_op3_rh850 +#define tcg_gen_op4 tcg_gen_op4_rh850 +#define tcg_gen_op5 tcg_gen_op5_rh850 +#define tcg_gen_op6 tcg_gen_op6_rh850 +#define tcg_gen_mb tcg_gen_mb_rh850 +#define tcg_gen_addi_i32 tcg_gen_addi_i32_rh850 +#define tcg_gen_subfi_i32 tcg_gen_subfi_i32_rh850 +#define tcg_gen_subi_i32 tcg_gen_subi_i32_rh850 +#define tcg_gen_andi_i32 tcg_gen_andi_i32_rh850 +#define tcg_gen_ori_i32 tcg_gen_ori_i32_rh850 +#define tcg_gen_xori_i32 tcg_gen_xori_i32_rh850 +#define tcg_gen_shli_i32 tcg_gen_shli_i32_rh850 +#define tcg_gen_shri_i32 tcg_gen_shri_i32_rh850 +#define tcg_gen_sari_i32 tcg_gen_sari_i32_rh850 +#define tcg_gen_brcond_i32 tcg_gen_brcond_i32_rh850 +#define tcg_gen_brcondi_i32 tcg_gen_brcondi_i32_rh850 +#define tcg_gen_setcond_i32 tcg_gen_setcond_i32_rh850 +#define tcg_gen_setcondi_i32 tcg_gen_setcondi_i32_rh850 +#define tcg_gen_muli_i32 tcg_gen_muli_i32_rh850 +#define tcg_gen_div_i32 tcg_gen_div_i32_rh850 +#define tcg_gen_rem_i32 tcg_gen_rem_i32_rh850 +#define tcg_gen_divu_i32 tcg_gen_divu_i32_rh850 +#define tcg_gen_remu_i32 tcg_gen_remu_i32_rh850 +#define tcg_gen_andc_i32 tcg_gen_andc_i32_rh850 +#define tcg_gen_eqv_i32 tcg_gen_eqv_i32_rh850 +#define tcg_gen_nand_i32 tcg_gen_nand_i32_rh850 +#define tcg_gen_nor_i32 tcg_gen_nor_i32_rh850 +#define tcg_gen_orc_i32 tcg_gen_orc_i32_rh850 +#define tcg_gen_clz_i32 tcg_gen_clz_i32_rh850 +#define tcg_gen_clzi_i32 tcg_gen_clzi_i32_rh850 +#define tcg_gen_ctz_i32 tcg_gen_ctz_i32_rh850 +#define tcg_gen_ctzi_i32 tcg_gen_ctzi_i32_rh850 +#define tcg_gen_clrsb_i32 tcg_gen_clrsb_i32_rh850 +#define tcg_gen_ctpop_i32 tcg_gen_ctpop_i32_rh850 +#define tcg_gen_rotl_i32 tcg_gen_rotl_i32_rh850 +#define tcg_gen_rotli_i32 tcg_gen_rotli_i32_rh850 +#define tcg_gen_rotr_i32 tcg_gen_rotr_i32_rh850 +#define tcg_gen_rotri_i32 tcg_gen_rotri_i32_rh850 +#define tcg_gen_deposit_i32 tcg_gen_deposit_i32_rh850 +#define tcg_gen_deposit_z_i32 tcg_gen_deposit_z_i32_rh850 +#define tcg_gen_extract_i32 tcg_gen_extract_i32_rh850 +#define tcg_gen_sextract_i32 tcg_gen_sextract_i32_rh850 +#define tcg_gen_extract2_i32 tcg_gen_extract2_i32_rh850 +#define tcg_gen_movcond_i32 tcg_gen_movcond_i32_rh850 +#define tcg_gen_add2_i32 tcg_gen_add2_i32_rh850 +#define tcg_gen_sub2_i32 tcg_gen_sub2_i32_rh850 +#define tcg_gen_mulu2_i32 tcg_gen_mulu2_i32_rh850 +#define tcg_gen_muls2_i32 tcg_gen_muls2_i32_rh850 +#define tcg_gen_mulsu2_i32 tcg_gen_mulsu2_i32_rh850 +#define tcg_gen_ext8s_i32 tcg_gen_ext8s_i32_rh850 +#define tcg_gen_ext16s_i32 tcg_gen_ext16s_i32_rh850 +#define tcg_gen_ext8u_i32 tcg_gen_ext8u_i32_rh850 +#define tcg_gen_ext16u_i32 tcg_gen_ext16u_i32_rh850 +#define tcg_gen_bswap16_i32 tcg_gen_bswap16_i32_rh850 +#define tcg_gen_bswap32_i32 tcg_gen_bswap32_i32_rh850 +#define tcg_gen_smin_i32 tcg_gen_smin_i32_rh850 +#define tcg_gen_umin_i32 tcg_gen_umin_i32_rh850 +#define tcg_gen_smax_i32 tcg_gen_smax_i32_rh850 +#define tcg_gen_umax_i32 tcg_gen_umax_i32_rh850 +#define tcg_gen_abs_i32 tcg_gen_abs_i32_rh850 +#define tcg_gen_addi_i64 tcg_gen_addi_i64_rh850 +#define tcg_gen_subfi_i64 tcg_gen_subfi_i64_rh850 +#define tcg_gen_subi_i64 tcg_gen_subi_i64_rh850 +#define tcg_gen_andi_i64 tcg_gen_andi_i64_rh850 +#define tcg_gen_ori_i64 tcg_gen_ori_i64_rh850 +#define tcg_gen_xori_i64 tcg_gen_xori_i64_rh850 +#define tcg_gen_shli_i64 tcg_gen_shli_i64_rh850 +#define tcg_gen_shri_i64 tcg_gen_shri_i64_rh850 +#define tcg_gen_sari_i64 tcg_gen_sari_i64_rh850 +#define tcg_gen_brcond_i64 tcg_gen_brcond_i64_rh850 +#define tcg_gen_brcondi_i64 tcg_gen_brcondi_i64_rh850 +#define tcg_gen_setcond_i64 tcg_gen_setcond_i64_rh850 +#define tcg_gen_setcondi_i64 tcg_gen_setcondi_i64_rh850 +#define tcg_gen_muli_i64 tcg_gen_muli_i64_rh850 +#define tcg_gen_div_i64 tcg_gen_div_i64_rh850 +#define tcg_gen_rem_i64 tcg_gen_rem_i64_rh850 +#define tcg_gen_divu_i64 tcg_gen_divu_i64_rh850 +#define tcg_gen_remu_i64 tcg_gen_remu_i64_rh850 +#define tcg_gen_ext8s_i64 tcg_gen_ext8s_i64_rh850 +#define tcg_gen_ext16s_i64 tcg_gen_ext16s_i64_rh850 +#define tcg_gen_ext32s_i64 tcg_gen_ext32s_i64_rh850 +#define tcg_gen_ext8u_i64 tcg_gen_ext8u_i64_rh850 +#define tcg_gen_ext16u_i64 tcg_gen_ext16u_i64_rh850 +#define tcg_gen_ext32u_i64 tcg_gen_ext32u_i64_rh850 +#define tcg_gen_bswap16_i64 tcg_gen_bswap16_i64_rh850 +#define tcg_gen_bswap32_i64 tcg_gen_bswap32_i64_rh850 +#define tcg_gen_bswap64_i64 tcg_gen_bswap64_i64_rh850 +#define tcg_gen_not_i64 tcg_gen_not_i64_rh850 +#define tcg_gen_andc_i64 tcg_gen_andc_i64_rh850 +#define tcg_gen_eqv_i64 tcg_gen_eqv_i64_rh850 +#define tcg_gen_nand_i64 tcg_gen_nand_i64_rh850 +#define tcg_gen_nor_i64 tcg_gen_nor_i64_rh850 +#define tcg_gen_orc_i64 tcg_gen_orc_i64_rh850 +#define tcg_gen_clz_i64 tcg_gen_clz_i64_rh850 +#define tcg_gen_clzi_i64 tcg_gen_clzi_i64_rh850 +#define tcg_gen_ctz_i64 tcg_gen_ctz_i64_rh850 +#define tcg_gen_ctzi_i64 tcg_gen_ctzi_i64_rh850 +#define tcg_gen_clrsb_i64 tcg_gen_clrsb_i64_rh850 +#define tcg_gen_ctpop_i64 tcg_gen_ctpop_i64_rh850 +#define tcg_gen_rotl_i64 tcg_gen_rotl_i64_rh850 +#define tcg_gen_rotli_i64 tcg_gen_rotli_i64_rh850 +#define tcg_gen_rotr_i64 tcg_gen_rotr_i64_rh850 +#define tcg_gen_rotri_i64 tcg_gen_rotri_i64_rh850 +#define tcg_gen_deposit_i64 tcg_gen_deposit_i64_rh850 +#define tcg_gen_deposit_z_i64 tcg_gen_deposit_z_i64_rh850 +#define tcg_gen_extract_i64 tcg_gen_extract_i64_rh850 +#define tcg_gen_sextract_i64 tcg_gen_sextract_i64_rh850 +#define tcg_gen_extract2_i64 tcg_gen_extract2_i64_rh850 +#define tcg_gen_movcond_i64 tcg_gen_movcond_i64_rh850 +#define tcg_gen_add2_i64 tcg_gen_add2_i64_rh850 +#define tcg_gen_sub2_i64 tcg_gen_sub2_i64_rh850 +#define tcg_gen_mulu2_i64 tcg_gen_mulu2_i64_rh850 +#define tcg_gen_muls2_i64 tcg_gen_muls2_i64_rh850 +#define tcg_gen_mulsu2_i64 tcg_gen_mulsu2_i64_rh850 +#define tcg_gen_smin_i64 tcg_gen_smin_i64_rh850 +#define tcg_gen_umin_i64 tcg_gen_umin_i64_rh850 +#define tcg_gen_smax_i64 tcg_gen_smax_i64_rh850 +#define tcg_gen_umax_i64 tcg_gen_umax_i64_rh850 +#define tcg_gen_abs_i64 tcg_gen_abs_i64_rh850 +#define tcg_gen_extrl_i64_i32 tcg_gen_extrl_i64_i32_rh850 +#define tcg_gen_extrh_i64_i32 tcg_gen_extrh_i64_i32_rh850 +#define tcg_gen_extu_i32_i64 tcg_gen_extu_i32_i64_rh850 +#define tcg_gen_ext_i32_i64 tcg_gen_ext_i32_i64_rh850 +#define tcg_gen_concat_i32_i64 tcg_gen_concat_i32_i64_rh850 +#define tcg_gen_extr_i64_i32 tcg_gen_extr_i64_i32_rh850 +#define tcg_gen_extr32_i64 tcg_gen_extr32_i64_rh850 +#define tcg_gen_exit_tb tcg_gen_exit_tb_rh850 +#define tcg_gen_goto_tb tcg_gen_goto_tb_rh850 +#define tcg_gen_lookup_and_goto_ptr tcg_gen_lookup_and_goto_ptr_rh850 +#define check_exit_request check_exit_request_rh850 +#define tcg_gen_qemu_ld_i32 tcg_gen_qemu_ld_i32_rh850 +#define tcg_gen_qemu_st_i32 tcg_gen_qemu_st_i32_rh850 +#define tcg_gen_qemu_ld_i64 tcg_gen_qemu_ld_i64_rh850 +#define tcg_gen_qemu_st_i64 tcg_gen_qemu_st_i64_rh850 +#define tcg_gen_atomic_cmpxchg_i32 tcg_gen_atomic_cmpxchg_i32_rh850 +#define tcg_gen_atomic_cmpxchg_i64 tcg_gen_atomic_cmpxchg_i64_rh850 +#define tcg_gen_atomic_fetch_add_i32 tcg_gen_atomic_fetch_add_i32_rh850 +#define tcg_gen_atomic_fetch_add_i64 tcg_gen_atomic_fetch_add_i64_rh850 +#define tcg_gen_atomic_fetch_and_i32 tcg_gen_atomic_fetch_and_i32_rh850 +#define tcg_gen_atomic_fetch_and_i64 tcg_gen_atomic_fetch_and_i64_rh850 +#define tcg_gen_atomic_fetch_or_i32 tcg_gen_atomic_fetch_or_i32_rh850 +#define tcg_gen_atomic_fetch_or_i64 tcg_gen_atomic_fetch_or_i64_rh850 +#define tcg_gen_atomic_fetch_xor_i32 tcg_gen_atomic_fetch_xor_i32_rh850 +#define tcg_gen_atomic_fetch_xor_i64 tcg_gen_atomic_fetch_xor_i64_rh850 +#define tcg_gen_atomic_fetch_smin_i32 tcg_gen_atomic_fetch_smin_i32_rh850 +#define tcg_gen_atomic_fetch_smin_i64 tcg_gen_atomic_fetch_smin_i64_rh850 +#define tcg_gen_atomic_fetch_umin_i32 tcg_gen_atomic_fetch_umin_i32_rh850 +#define tcg_gen_atomic_fetch_umin_i64 tcg_gen_atomic_fetch_umin_i64_rh850 +#define tcg_gen_atomic_fetch_smax_i32 tcg_gen_atomic_fetch_smax_i32_rh850 +#define tcg_gen_atomic_fetch_smax_i64 tcg_gen_atomic_fetch_smax_i64_rh850 +#define tcg_gen_atomic_fetch_umax_i32 tcg_gen_atomic_fetch_umax_i32_rh850 +#define tcg_gen_atomic_fetch_umax_i64 tcg_gen_atomic_fetch_umax_i64_rh850 +#define tcg_gen_atomic_add_fetch_i32 tcg_gen_atomic_add_fetch_i32_rh850 +#define tcg_gen_atomic_add_fetch_i64 tcg_gen_atomic_add_fetch_i64_rh850 +#define tcg_gen_atomic_and_fetch_i32 tcg_gen_atomic_and_fetch_i32_rh850 +#define tcg_gen_atomic_and_fetch_i64 tcg_gen_atomic_and_fetch_i64_rh850 +#define tcg_gen_atomic_or_fetch_i32 tcg_gen_atomic_or_fetch_i32_rh850 +#define tcg_gen_atomic_or_fetch_i64 tcg_gen_atomic_or_fetch_i64_rh850 +#define tcg_gen_atomic_xor_fetch_i32 tcg_gen_atomic_xor_fetch_i32_rh850 +#define tcg_gen_atomic_xor_fetch_i64 tcg_gen_atomic_xor_fetch_i64_rh850 +#define tcg_gen_atomic_smin_fetch_i32 tcg_gen_atomic_smin_fetch_i32_rh850 +#define tcg_gen_atomic_smin_fetch_i64 tcg_gen_atomic_smin_fetch_i64_rh850 +#define tcg_gen_atomic_umin_fetch_i32 tcg_gen_atomic_umin_fetch_i32_rh850 +#define tcg_gen_atomic_umin_fetch_i64 tcg_gen_atomic_umin_fetch_i64_rh850 +#define tcg_gen_atomic_smax_fetch_i32 tcg_gen_atomic_smax_fetch_i32_rh850 +#define tcg_gen_atomic_smax_fetch_i64 tcg_gen_atomic_smax_fetch_i64_rh850 +#define tcg_gen_atomic_umax_fetch_i32 tcg_gen_atomic_umax_fetch_i32_rh850 +#define tcg_gen_atomic_umax_fetch_i64 tcg_gen_atomic_umax_fetch_i64_rh850 +#define tcg_gen_atomic_xchg_i32 tcg_gen_atomic_xchg_i32_rh850 +#define tcg_gen_atomic_xchg_i64 tcg_gen_atomic_xchg_i64_rh850 +#define simd_desc simd_desc_rh850 +#define tcg_gen_gvec_2_ool tcg_gen_gvec_2_ool_rh850 +#define tcg_gen_gvec_2i_ool tcg_gen_gvec_2i_ool_rh850 +#define tcg_gen_gvec_3_ool tcg_gen_gvec_3_ool_rh850 +#define tcg_gen_gvec_4_ool tcg_gen_gvec_4_ool_rh850 +#define tcg_gen_gvec_5_ool tcg_gen_gvec_5_ool_rh850 +#define tcg_gen_gvec_2_ptr tcg_gen_gvec_2_ptr_rh850 +#define tcg_gen_gvec_3_ptr tcg_gen_gvec_3_ptr_rh850 +#define tcg_gen_gvec_4_ptr tcg_gen_gvec_4_ptr_rh850 +#define tcg_gen_gvec_5_ptr tcg_gen_gvec_5_ptr_rh850 +#define tcg_gen_gvec_2 tcg_gen_gvec_2_rh850 +#define tcg_gen_gvec_2i tcg_gen_gvec_2i_rh850 +#define tcg_gen_gvec_2s tcg_gen_gvec_2s_rh850 +#define tcg_gen_gvec_3 tcg_gen_gvec_3_rh850 +#define tcg_gen_gvec_3i tcg_gen_gvec_3i_rh850 +#define tcg_gen_gvec_4 tcg_gen_gvec_4_rh850 +#define tcg_gen_gvec_mov tcg_gen_gvec_mov_rh850 +#define tcg_gen_gvec_dup_i32 tcg_gen_gvec_dup_i32_rh850 +#define tcg_gen_gvec_dup_i64 tcg_gen_gvec_dup_i64_rh850 +#define tcg_gen_gvec_dup_mem tcg_gen_gvec_dup_mem_rh850 +#define tcg_gen_gvec_dup64i tcg_gen_gvec_dup64i_rh850 +#define tcg_gen_gvec_dup32i tcg_gen_gvec_dup32i_rh850 +#define tcg_gen_gvec_dup16i tcg_gen_gvec_dup16i_rh850 +#define tcg_gen_gvec_dup8i tcg_gen_gvec_dup8i_rh850 +#define tcg_gen_gvec_not tcg_gen_gvec_not_rh850 +#define tcg_gen_vec_add8_i64 tcg_gen_vec_add8_i64_rh850 +#define tcg_gen_vec_add16_i64 tcg_gen_vec_add16_i64_rh850 +#define tcg_gen_vec_add32_i64 tcg_gen_vec_add32_i64_rh850 +#define tcg_gen_gvec_add tcg_gen_gvec_add_rh850 +#define tcg_gen_gvec_adds tcg_gen_gvec_adds_rh850 +#define tcg_gen_gvec_addi tcg_gen_gvec_addi_rh850 +#define tcg_gen_gvec_subs tcg_gen_gvec_subs_rh850 +#define tcg_gen_vec_sub8_i64 tcg_gen_vec_sub8_i64_rh850 +#define tcg_gen_vec_sub16_i64 tcg_gen_vec_sub16_i64_rh850 +#define tcg_gen_vec_sub32_i64 tcg_gen_vec_sub32_i64_rh850 +#define tcg_gen_gvec_sub tcg_gen_gvec_sub_rh850 +#define tcg_gen_gvec_mul tcg_gen_gvec_mul_rh850 +#define tcg_gen_gvec_muls tcg_gen_gvec_muls_rh850 +#define tcg_gen_gvec_muli tcg_gen_gvec_muli_rh850 +#define tcg_gen_gvec_ssadd tcg_gen_gvec_ssadd_rh850 +#define tcg_gen_gvec_sssub tcg_gen_gvec_sssub_rh850 +#define tcg_gen_gvec_usadd tcg_gen_gvec_usadd_rh850 +#define tcg_gen_gvec_ussub tcg_gen_gvec_ussub_rh850 +#define tcg_gen_gvec_smin tcg_gen_gvec_smin_rh850 +#define tcg_gen_gvec_umin tcg_gen_gvec_umin_rh850 +#define tcg_gen_gvec_smax tcg_gen_gvec_smax_rh850 +#define tcg_gen_gvec_umax tcg_gen_gvec_umax_rh850 +#define tcg_gen_vec_neg8_i64 tcg_gen_vec_neg8_i64_rh850 +#define tcg_gen_vec_neg16_i64 tcg_gen_vec_neg16_i64_rh850 +#define tcg_gen_vec_neg32_i64 tcg_gen_vec_neg32_i64_rh850 +#define tcg_gen_gvec_neg tcg_gen_gvec_neg_rh850 +#define tcg_gen_gvec_abs tcg_gen_gvec_abs_rh850 +#define tcg_gen_gvec_and tcg_gen_gvec_and_rh850 +#define tcg_gen_gvec_or tcg_gen_gvec_or_rh850 +#define tcg_gen_gvec_xor tcg_gen_gvec_xor_rh850 +#define tcg_gen_gvec_andc tcg_gen_gvec_andc_rh850 +#define tcg_gen_gvec_orc tcg_gen_gvec_orc_rh850 +#define tcg_gen_gvec_nand tcg_gen_gvec_nand_rh850 +#define tcg_gen_gvec_nor tcg_gen_gvec_nor_rh850 +#define tcg_gen_gvec_eqv tcg_gen_gvec_eqv_rh850 +#define tcg_gen_gvec_ands tcg_gen_gvec_ands_rh850 +#define tcg_gen_gvec_andi tcg_gen_gvec_andi_rh850 +#define tcg_gen_gvec_xors tcg_gen_gvec_xors_rh850 +#define tcg_gen_gvec_xori tcg_gen_gvec_xori_rh850 +#define tcg_gen_gvec_ors tcg_gen_gvec_ors_rh850 +#define tcg_gen_gvec_ori tcg_gen_gvec_ori_rh850 +#define tcg_gen_vec_shl8i_i64 tcg_gen_vec_shl8i_i64_rh850 +#define tcg_gen_vec_shl16i_i64 tcg_gen_vec_shl16i_i64_rh850 +#define tcg_gen_gvec_shli tcg_gen_gvec_shli_rh850 +#define tcg_gen_vec_shr8i_i64 tcg_gen_vec_shr8i_i64_rh850 +#define tcg_gen_vec_shr16i_i64 tcg_gen_vec_shr16i_i64_rh850 +#define tcg_gen_gvec_shri tcg_gen_gvec_shri_rh850 +#define tcg_gen_vec_sar8i_i64 tcg_gen_vec_sar8i_i64_rh850 +#define tcg_gen_vec_sar16i_i64 tcg_gen_vec_sar16i_i64_rh850 +#define tcg_gen_gvec_sari tcg_gen_gvec_sari_rh850 +#define tcg_gen_gvec_shls tcg_gen_gvec_shls_rh850 +#define tcg_gen_gvec_shrs tcg_gen_gvec_shrs_rh850 +#define tcg_gen_gvec_sars tcg_gen_gvec_sars_rh850 +#define tcg_gen_gvec_shlv tcg_gen_gvec_shlv_rh850 +#define tcg_gen_gvec_shrv tcg_gen_gvec_shrv_rh850 +#define tcg_gen_gvec_sarv tcg_gen_gvec_sarv_rh850 +#define tcg_gen_gvec_cmp tcg_gen_gvec_cmp_rh850 +#define tcg_gen_gvec_bitsel tcg_gen_gvec_bitsel_rh850 +#define tcg_can_emit_vecop_list tcg_can_emit_vecop_list_rh850 +#define vec_gen_2 vec_gen_2_rh850 +#define vec_gen_3 vec_gen_3_rh850 +#define vec_gen_4 vec_gen_4_rh850 +#define tcg_gen_mov_vec tcg_gen_mov_vec_rh850 +#define tcg_const_zeros_vec tcg_const_zeros_vec_rh850 +#define tcg_const_ones_vec tcg_const_ones_vec_rh850 +#define tcg_const_zeros_vec_matching tcg_const_zeros_vec_matching_rh850 +#define tcg_const_ones_vec_matching tcg_const_ones_vec_matching_rh850 +#define tcg_gen_dup64i_vec tcg_gen_dup64i_vec_rh850 +#define tcg_gen_dup32i_vec tcg_gen_dup32i_vec_rh850 +#define tcg_gen_dup16i_vec tcg_gen_dup16i_vec_rh850 +#define tcg_gen_dup8i_vec tcg_gen_dup8i_vec_rh850 +#define tcg_gen_dupi_vec tcg_gen_dupi_vec_rh850 +#define tcg_gen_dup_i64_vec tcg_gen_dup_i64_vec_rh850 +#define tcg_gen_dup_i32_vec tcg_gen_dup_i32_vec_rh850 +#define tcg_gen_dup_mem_vec tcg_gen_dup_mem_vec_rh850 +#define tcg_gen_ld_vec tcg_gen_ld_vec_rh850 +#define tcg_gen_st_vec tcg_gen_st_vec_rh850 +#define tcg_gen_stl_vec tcg_gen_stl_vec_rh850 +#define tcg_gen_and_vec tcg_gen_and_vec_rh850 +#define tcg_gen_or_vec tcg_gen_or_vec_rh850 +#define tcg_gen_xor_vec tcg_gen_xor_vec_rh850 +#define tcg_gen_andc_vec tcg_gen_andc_vec_rh850 +#define tcg_gen_orc_vec tcg_gen_orc_vec_rh850 +#define tcg_gen_nand_vec tcg_gen_nand_vec_rh850 +#define tcg_gen_nor_vec tcg_gen_nor_vec_rh850 +#define tcg_gen_eqv_vec tcg_gen_eqv_vec_rh850 +#define tcg_gen_not_vec tcg_gen_not_vec_rh850 +#define tcg_gen_neg_vec tcg_gen_neg_vec_rh850 +#define tcg_gen_abs_vec tcg_gen_abs_vec_rh850 +#define tcg_gen_shli_vec tcg_gen_shli_vec_rh850 +#define tcg_gen_shri_vec tcg_gen_shri_vec_rh850 +#define tcg_gen_sari_vec tcg_gen_sari_vec_rh850 +#define tcg_gen_cmp_vec tcg_gen_cmp_vec_rh850 +#define tcg_gen_add_vec tcg_gen_add_vec_rh850 +#define tcg_gen_sub_vec tcg_gen_sub_vec_rh850 +#define tcg_gen_mul_vec tcg_gen_mul_vec_rh850 +#define tcg_gen_ssadd_vec tcg_gen_ssadd_vec_rh850 +#define tcg_gen_usadd_vec tcg_gen_usadd_vec_rh850 +#define tcg_gen_sssub_vec tcg_gen_sssub_vec_rh850 +#define tcg_gen_ussub_vec tcg_gen_ussub_vec_rh850 +#define tcg_gen_smin_vec tcg_gen_smin_vec_rh850 +#define tcg_gen_umin_vec tcg_gen_umin_vec_rh850 +#define tcg_gen_smax_vec tcg_gen_smax_vec_rh850 +#define tcg_gen_umax_vec tcg_gen_umax_vec_rh850 +#define tcg_gen_shlv_vec tcg_gen_shlv_vec_rh850 +#define tcg_gen_shrv_vec tcg_gen_shrv_vec_rh850 +#define tcg_gen_sarv_vec tcg_gen_sarv_vec_rh850 +#define tcg_gen_shls_vec tcg_gen_shls_vec_rh850 +#define tcg_gen_shrs_vec tcg_gen_shrs_vec_rh850 +#define tcg_gen_sars_vec tcg_gen_sars_vec_rh850 +#define tcg_gen_bitsel_vec tcg_gen_bitsel_vec_rh850 +#define tcg_gen_cmpsel_vec tcg_gen_cmpsel_vec_rh850 +#define tb_htable_lookup tb_htable_lookup_rh850 +#define tb_set_jmp_target tb_set_jmp_target_rh850 +#define cpu_exec cpu_exec_rh850 +#define cpu_loop_exit_noexc cpu_loop_exit_noexc_rh850 +#define cpu_reloading_memory_map cpu_reloading_memory_map_rh850 +#define cpu_loop_exit cpu_loop_exit_rh850 +#define cpu_loop_exit_restore cpu_loop_exit_restore_rh850 +#define cpu_loop_exit_atomic cpu_loop_exit_atomic_rh850 +#define tlb_init tlb_init_rh850 +#define tlb_flush_by_mmuidx tlb_flush_by_mmuidx_rh850 +#define tlb_flush tlb_flush_rh850 +#define tlb_flush_by_mmuidx_all_cpus tlb_flush_by_mmuidx_all_cpus_rh850 +#define tlb_flush_all_cpus tlb_flush_all_cpus_rh850 +#define tlb_flush_by_mmuidx_all_cpus_synced tlb_flush_by_mmuidx_all_cpus_synced_rh850 +#define tlb_flush_all_cpus_synced tlb_flush_all_cpus_synced_rh850 +#define tlb_flush_page_by_mmuidx tlb_flush_page_by_mmuidx_rh850 +#define tlb_flush_page tlb_flush_page_rh850 +#define tlb_flush_page_by_mmuidx_all_cpus tlb_flush_page_by_mmuidx_all_cpus_rh850 +#define tlb_flush_page_all_cpus tlb_flush_page_all_cpus_rh850 +#define tlb_flush_page_by_mmuidx_all_cpus_synced tlb_flush_page_by_mmuidx_all_cpus_synced_rh850 +#define tlb_flush_page_all_cpus_synced tlb_flush_page_all_cpus_synced_rh850 +#define tlb_protect_code tlb_protect_code_rh850 +#define tlb_unprotect_code tlb_unprotect_code_rh850 +#define tlb_reset_dirty tlb_reset_dirty_rh850 +#define tlb_set_dirty tlb_set_dirty_rh850 +#define tlb_set_page_with_attrs tlb_set_page_with_attrs_rh850 +#define tlb_set_page tlb_set_page_rh850 +#define get_page_addr_code_hostp get_page_addr_code_hostp_rh850 +#define get_page_addr_code get_page_addr_code_rh850 +#define probe_access probe_access_rh850 +#define tlb_vaddr_to_host tlb_vaddr_to_host_rh850 +#define helper_ret_ldub_mmu helper_ret_ldub_mmu_rh850 +#define helper_le_lduw_mmu helper_le_lduw_mmu_rh850 +#define helper_be_lduw_mmu helper_be_lduw_mmu_rh850 +#define helper_le_ldul_mmu helper_le_ldul_mmu_rh850 +#define helper_be_ldul_mmu helper_be_ldul_mmu_rh850 +#define helper_le_ldq_mmu helper_le_ldq_mmu_rh850 +#define helper_be_ldq_mmu helper_be_ldq_mmu_rh850 +#define helper_ret_ldsb_mmu helper_ret_ldsb_mmu_rh850 +#define helper_le_ldsw_mmu helper_le_ldsw_mmu_rh850 +#define helper_be_ldsw_mmu helper_be_ldsw_mmu_rh850 +#define helper_le_ldsl_mmu helper_le_ldsl_mmu_rh850 +#define helper_be_ldsl_mmu helper_be_ldsl_mmu_rh850 +#define cpu_ldub_mmuidx_ra cpu_ldub_mmuidx_ra_rh850 +#define cpu_ldsb_mmuidx_ra cpu_ldsb_mmuidx_ra_rh850 +#define cpu_lduw_mmuidx_ra cpu_lduw_mmuidx_ra_rh850 +#define cpu_ldsw_mmuidx_ra cpu_ldsw_mmuidx_ra_rh850 +#define cpu_ldl_mmuidx_ra cpu_ldl_mmuidx_ra_rh850 +#define cpu_ldq_mmuidx_ra cpu_ldq_mmuidx_ra_rh850 +#define cpu_ldub_data_ra cpu_ldub_data_ra_rh850 +#define cpu_ldsb_data_ra cpu_ldsb_data_ra_rh850 +#define cpu_lduw_data_ra cpu_lduw_data_ra_rh850 +#define cpu_ldsw_data_ra cpu_ldsw_data_ra_rh850 +#define cpu_ldl_data_ra cpu_ldl_data_ra_rh850 +#define cpu_ldq_data_ra cpu_ldq_data_ra_rh850 +#define cpu_ldub_data cpu_ldub_data_rh850 +#define cpu_ldsb_data cpu_ldsb_data_rh850 +#define cpu_lduw_data cpu_lduw_data_rh850 +#define cpu_ldsw_data cpu_ldsw_data_rh850 +#define cpu_ldl_data cpu_ldl_data_rh850 +#define cpu_ldq_data cpu_ldq_data_rh850 +#define helper_ret_stb_mmu helper_ret_stb_mmu_rh850 +#define helper_le_stw_mmu helper_le_stw_mmu_rh850 +#define helper_be_stw_mmu helper_be_stw_mmu_rh850 +#define helper_le_stl_mmu helper_le_stl_mmu_rh850 +#define helper_be_stl_mmu helper_be_stl_mmu_rh850 +#define helper_le_stq_mmu helper_le_stq_mmu_rh850 +#define helper_be_stq_mmu helper_be_stq_mmu_rh850 +#define cpu_stb_mmuidx_ra cpu_stb_mmuidx_ra_rh850 +#define cpu_stw_mmuidx_ra cpu_stw_mmuidx_ra_rh850 +#define cpu_stl_mmuidx_ra cpu_stl_mmuidx_ra_rh850 +#define cpu_stq_mmuidx_ra cpu_stq_mmuidx_ra_rh850 +#define cpu_stb_data_ra cpu_stb_data_ra_rh850 +#define cpu_stw_data_ra cpu_stw_data_ra_rh850 +#define cpu_stl_data_ra cpu_stl_data_ra_rh850 +#define cpu_stq_data_ra cpu_stq_data_ra_rh850 +#define cpu_stb_data cpu_stb_data_rh850 +#define cpu_stw_data cpu_stw_data_rh850 +#define cpu_stl_data cpu_stl_data_rh850 +#define cpu_stq_data cpu_stq_data_rh850 +#define helper_atomic_cmpxchgb_mmu helper_atomic_cmpxchgb_mmu_rh850 +#define helper_atomic_xchgb_mmu helper_atomic_xchgb_mmu_rh850 +#define helper_atomic_fetch_addb_mmu helper_atomic_fetch_addb_mmu_rh850 +#define helper_atomic_fetch_andb_mmu helper_atomic_fetch_andb_mmu_rh850 +#define helper_atomic_fetch_orb_mmu helper_atomic_fetch_orb_mmu_rh850 +#define helper_atomic_fetch_xorb_mmu helper_atomic_fetch_xorb_mmu_rh850 +#define helper_atomic_add_fetchb_mmu helper_atomic_add_fetchb_mmu_rh850 +#define helper_atomic_and_fetchb_mmu helper_atomic_and_fetchb_mmu_rh850 +#define helper_atomic_or_fetchb_mmu helper_atomic_or_fetchb_mmu_rh850 +#define helper_atomic_xor_fetchb_mmu helper_atomic_xor_fetchb_mmu_rh850 +#define helper_atomic_fetch_sminb_mmu helper_atomic_fetch_sminb_mmu_rh850 +#define helper_atomic_fetch_uminb_mmu helper_atomic_fetch_uminb_mmu_rh850 +#define helper_atomic_fetch_smaxb_mmu helper_atomic_fetch_smaxb_mmu_rh850 +#define helper_atomic_fetch_umaxb_mmu helper_atomic_fetch_umaxb_mmu_rh850 +#define helper_atomic_smin_fetchb_mmu helper_atomic_smin_fetchb_mmu_rh850 +#define helper_atomic_umin_fetchb_mmu helper_atomic_umin_fetchb_mmu_rh850 +#define helper_atomic_smax_fetchb_mmu helper_atomic_smax_fetchb_mmu_rh850 +#define helper_atomic_umax_fetchb_mmu helper_atomic_umax_fetchb_mmu_rh850 +#define helper_atomic_cmpxchgw_le_mmu helper_atomic_cmpxchgw_le_mmu_rh850 +#define helper_atomic_xchgw_le_mmu helper_atomic_xchgw_le_mmu_rh850 +#define helper_atomic_fetch_addw_le_mmu helper_atomic_fetch_addw_le_mmu_rh850 +#define helper_atomic_fetch_andw_le_mmu helper_atomic_fetch_andw_le_mmu_rh850 +#define helper_atomic_fetch_orw_le_mmu helper_atomic_fetch_orw_le_mmu_rh850 +#define helper_atomic_fetch_xorw_le_mmu helper_atomic_fetch_xorw_le_mmu_rh850 +#define helper_atomic_add_fetchw_le_mmu helper_atomic_add_fetchw_le_mmu_rh850 +#define helper_atomic_and_fetchw_le_mmu helper_atomic_and_fetchw_le_mmu_rh850 +#define helper_atomic_or_fetchw_le_mmu helper_atomic_or_fetchw_le_mmu_rh850 +#define helper_atomic_xor_fetchw_le_mmu helper_atomic_xor_fetchw_le_mmu_rh850 +#define helper_atomic_fetch_sminw_le_mmu helper_atomic_fetch_sminw_le_mmu_rh850 +#define helper_atomic_fetch_uminw_le_mmu helper_atomic_fetch_uminw_le_mmu_rh850 +#define helper_atomic_fetch_smaxw_le_mmu helper_atomic_fetch_smaxw_le_mmu_rh850 +#define helper_atomic_fetch_umaxw_le_mmu helper_atomic_fetch_umaxw_le_mmu_rh850 +#define helper_atomic_smin_fetchw_le_mmu helper_atomic_smin_fetchw_le_mmu_rh850 +#define helper_atomic_umin_fetchw_le_mmu helper_atomic_umin_fetchw_le_mmu_rh850 +#define helper_atomic_smax_fetchw_le_mmu helper_atomic_smax_fetchw_le_mmu_rh850 +#define helper_atomic_umax_fetchw_le_mmu helper_atomic_umax_fetchw_le_mmu_rh850 +#define helper_atomic_cmpxchgw_be_mmu helper_atomic_cmpxchgw_be_mmu_rh850 +#define helper_atomic_xchgw_be_mmu helper_atomic_xchgw_be_mmu_rh850 +#define helper_atomic_fetch_andw_be_mmu helper_atomic_fetch_andw_be_mmu_rh850 +#define helper_atomic_fetch_orw_be_mmu helper_atomic_fetch_orw_be_mmu_rh850 +#define helper_atomic_fetch_xorw_be_mmu helper_atomic_fetch_xorw_be_mmu_rh850 +#define helper_atomic_and_fetchw_be_mmu helper_atomic_and_fetchw_be_mmu_rh850 +#define helper_atomic_or_fetchw_be_mmu helper_atomic_or_fetchw_be_mmu_rh850 +#define helper_atomic_xor_fetchw_be_mmu helper_atomic_xor_fetchw_be_mmu_rh850 +#define helper_atomic_fetch_sminw_be_mmu helper_atomic_fetch_sminw_be_mmu_rh850 +#define helper_atomic_fetch_uminw_be_mmu helper_atomic_fetch_uminw_be_mmu_rh850 +#define helper_atomic_fetch_smaxw_be_mmu helper_atomic_fetch_smaxw_be_mmu_rh850 +#define helper_atomic_fetch_umaxw_be_mmu helper_atomic_fetch_umaxw_be_mmu_rh850 +#define helper_atomic_smin_fetchw_be_mmu helper_atomic_smin_fetchw_be_mmu_rh850 +#define helper_atomic_umin_fetchw_be_mmu helper_atomic_umin_fetchw_be_mmu_rh850 +#define helper_atomic_smax_fetchw_be_mmu helper_atomic_smax_fetchw_be_mmu_rh850 +#define helper_atomic_umax_fetchw_be_mmu helper_atomic_umax_fetchw_be_mmu_rh850 +#define helper_atomic_fetch_addw_be_mmu helper_atomic_fetch_addw_be_mmu_rh850 +#define helper_atomic_add_fetchw_be_mmu helper_atomic_add_fetchw_be_mmu_rh850 +#define helper_atomic_cmpxchgl_le_mmu helper_atomic_cmpxchgl_le_mmu_rh850 +#define helper_atomic_xchgl_le_mmu helper_atomic_xchgl_le_mmu_rh850 +#define helper_atomic_fetch_addl_le_mmu helper_atomic_fetch_addl_le_mmu_rh850 +#define helper_atomic_fetch_andl_le_mmu helper_atomic_fetch_andl_le_mmu_rh850 +#define helper_atomic_fetch_orl_le_mmu helper_atomic_fetch_orl_le_mmu_rh850 +#define helper_atomic_fetch_xorl_le_mmu helper_atomic_fetch_xorl_le_mmu_rh850 +#define helper_atomic_add_fetchl_le_mmu helper_atomic_add_fetchl_le_mmu_rh850 +#define helper_atomic_and_fetchl_le_mmu helper_atomic_and_fetchl_le_mmu_rh850 +#define helper_atomic_or_fetchl_le_mmu helper_atomic_or_fetchl_le_mmu_rh850 +#define helper_atomic_xor_fetchl_le_mmu helper_atomic_xor_fetchl_le_mmu_rh850 +#define helper_atomic_fetch_sminl_le_mmu helper_atomic_fetch_sminl_le_mmu_rh850 +#define helper_atomic_fetch_uminl_le_mmu helper_atomic_fetch_uminl_le_mmu_rh850 +#define helper_atomic_fetch_smaxl_le_mmu helper_atomic_fetch_smaxl_le_mmu_rh850 +#define helper_atomic_fetch_umaxl_le_mmu helper_atomic_fetch_umaxl_le_mmu_rh850 +#define helper_atomic_smin_fetchl_le_mmu helper_atomic_smin_fetchl_le_mmu_rh850 +#define helper_atomic_umin_fetchl_le_mmu helper_atomic_umin_fetchl_le_mmu_rh850 +#define helper_atomic_smax_fetchl_le_mmu helper_atomic_smax_fetchl_le_mmu_rh850 +#define helper_atomic_umax_fetchl_le_mmu helper_atomic_umax_fetchl_le_mmu_rh850 +#define helper_atomic_cmpxchgl_be_mmu helper_atomic_cmpxchgl_be_mmu_rh850 +#define helper_atomic_xchgl_be_mmu helper_atomic_xchgl_be_mmu_rh850 +#define helper_atomic_fetch_andl_be_mmu helper_atomic_fetch_andl_be_mmu_rh850 +#define helper_atomic_fetch_orl_be_mmu helper_atomic_fetch_orl_be_mmu_rh850 +#define helper_atomic_fetch_xorl_be_mmu helper_atomic_fetch_xorl_be_mmu_rh850 +#define helper_atomic_and_fetchl_be_mmu helper_atomic_and_fetchl_be_mmu_rh850 +#define helper_atomic_or_fetchl_be_mmu helper_atomic_or_fetchl_be_mmu_rh850 +#define helper_atomic_xor_fetchl_be_mmu helper_atomic_xor_fetchl_be_mmu_rh850 +#define helper_atomic_fetch_sminl_be_mmu helper_atomic_fetch_sminl_be_mmu_rh850 +#define helper_atomic_fetch_uminl_be_mmu helper_atomic_fetch_uminl_be_mmu_rh850 +#define helper_atomic_fetch_smaxl_be_mmu helper_atomic_fetch_smaxl_be_mmu_rh850 +#define helper_atomic_fetch_umaxl_be_mmu helper_atomic_fetch_umaxl_be_mmu_rh850 +#define helper_atomic_smin_fetchl_be_mmu helper_atomic_smin_fetchl_be_mmu_rh850 +#define helper_atomic_umin_fetchl_be_mmu helper_atomic_umin_fetchl_be_mmu_rh850 +#define helper_atomic_smax_fetchl_be_mmu helper_atomic_smax_fetchl_be_mmu_rh850 +#define helper_atomic_umax_fetchl_be_mmu helper_atomic_umax_fetchl_be_mmu_rh850 +#define helper_atomic_fetch_addl_be_mmu helper_atomic_fetch_addl_be_mmu_rh850 +#define helper_atomic_add_fetchl_be_mmu helper_atomic_add_fetchl_be_mmu_rh850 +#define helper_atomic_cmpxchgq_le_mmu helper_atomic_cmpxchgq_le_mmu_rh850 +#define helper_atomic_xchgq_le_mmu helper_atomic_xchgq_le_mmu_rh850 +#define helper_atomic_fetch_addq_le_mmu helper_atomic_fetch_addq_le_mmu_rh850 +#define helper_atomic_fetch_andq_le_mmu helper_atomic_fetch_andq_le_mmu_rh850 +#define helper_atomic_fetch_orq_le_mmu helper_atomic_fetch_orq_le_mmu_rh850 +#define helper_atomic_fetch_xorq_le_mmu helper_atomic_fetch_xorq_le_mmu_rh850 +#define helper_atomic_add_fetchq_le_mmu helper_atomic_add_fetchq_le_mmu_rh850 +#define helper_atomic_and_fetchq_le_mmu helper_atomic_and_fetchq_le_mmu_rh850 +#define helper_atomic_or_fetchq_le_mmu helper_atomic_or_fetchq_le_mmu_rh850 +#define helper_atomic_xor_fetchq_le_mmu helper_atomic_xor_fetchq_le_mmu_rh850 +#define helper_atomic_fetch_sminq_le_mmu helper_atomic_fetch_sminq_le_mmu_rh850 +#define helper_atomic_fetch_uminq_le_mmu helper_atomic_fetch_uminq_le_mmu_rh850 +#define helper_atomic_fetch_smaxq_le_mmu helper_atomic_fetch_smaxq_le_mmu_rh850 +#define helper_atomic_fetch_umaxq_le_mmu helper_atomic_fetch_umaxq_le_mmu_rh850 +#define helper_atomic_smin_fetchq_le_mmu helper_atomic_smin_fetchq_le_mmu_rh850 +#define helper_atomic_umin_fetchq_le_mmu helper_atomic_umin_fetchq_le_mmu_rh850 +#define helper_atomic_smax_fetchq_le_mmu helper_atomic_smax_fetchq_le_mmu_rh850 +#define helper_atomic_umax_fetchq_le_mmu helper_atomic_umax_fetchq_le_mmu_rh850 +#define helper_atomic_cmpxchgq_be_mmu helper_atomic_cmpxchgq_be_mmu_rh850 +#define helper_atomic_xchgq_be_mmu helper_atomic_xchgq_be_mmu_rh850 +#define helper_atomic_fetch_andq_be_mmu helper_atomic_fetch_andq_be_mmu_rh850 +#define helper_atomic_fetch_orq_be_mmu helper_atomic_fetch_orq_be_mmu_rh850 +#define helper_atomic_fetch_xorq_be_mmu helper_atomic_fetch_xorq_be_mmu_rh850 +#define helper_atomic_and_fetchq_be_mmu helper_atomic_and_fetchq_be_mmu_rh850 +#define helper_atomic_or_fetchq_be_mmu helper_atomic_or_fetchq_be_mmu_rh850 +#define helper_atomic_xor_fetchq_be_mmu helper_atomic_xor_fetchq_be_mmu_rh850 +#define helper_atomic_fetch_sminq_be_mmu helper_atomic_fetch_sminq_be_mmu_rh850 +#define helper_atomic_fetch_uminq_be_mmu helper_atomic_fetch_uminq_be_mmu_rh850 +#define helper_atomic_fetch_smaxq_be_mmu helper_atomic_fetch_smaxq_be_mmu_rh850 +#define helper_atomic_fetch_umaxq_be_mmu helper_atomic_fetch_umaxq_be_mmu_rh850 +#define helper_atomic_smin_fetchq_be_mmu helper_atomic_smin_fetchq_be_mmu_rh850 +#define helper_atomic_umin_fetchq_be_mmu helper_atomic_umin_fetchq_be_mmu_rh850 +#define helper_atomic_smax_fetchq_be_mmu helper_atomic_smax_fetchq_be_mmu_rh850 +#define helper_atomic_umax_fetchq_be_mmu helper_atomic_umax_fetchq_be_mmu_rh850 +#define helper_atomic_fetch_addq_be_mmu helper_atomic_fetch_addq_be_mmu_rh850 +#define helper_atomic_add_fetchq_be_mmu helper_atomic_add_fetchq_be_mmu_rh850 +#define helper_atomic_cmpxchgb helper_atomic_cmpxchgb_rh850 +#define helper_atomic_xchgb helper_atomic_xchgb_rh850 +#define helper_atomic_fetch_addb helper_atomic_fetch_addb_rh850 +#define helper_atomic_fetch_andb helper_atomic_fetch_andb_rh850 +#define helper_atomic_fetch_orb helper_atomic_fetch_orb_rh850 +#define helper_atomic_fetch_xorb helper_atomic_fetch_xorb_rh850 +#define helper_atomic_add_fetchb helper_atomic_add_fetchb_rh850 +#define helper_atomic_and_fetchb helper_atomic_and_fetchb_rh850 +#define helper_atomic_or_fetchb helper_atomic_or_fetchb_rh850 +#define helper_atomic_xor_fetchb helper_atomic_xor_fetchb_rh850 +#define helper_atomic_fetch_sminb helper_atomic_fetch_sminb_rh850 +#define helper_atomic_fetch_uminb helper_atomic_fetch_uminb_rh850 +#define helper_atomic_fetch_smaxb helper_atomic_fetch_smaxb_rh850 +#define helper_atomic_fetch_umaxb helper_atomic_fetch_umaxb_rh850 +#define helper_atomic_smin_fetchb helper_atomic_smin_fetchb_rh850 +#define helper_atomic_umin_fetchb helper_atomic_umin_fetchb_rh850 +#define helper_atomic_smax_fetchb helper_atomic_smax_fetchb_rh850 +#define helper_atomic_umax_fetchb helper_atomic_umax_fetchb_rh850 +#define helper_atomic_cmpxchgw_le helper_atomic_cmpxchgw_le_rh850 +#define helper_atomic_xchgw_le helper_atomic_xchgw_le_rh850 +#define helper_atomic_fetch_addw_le helper_atomic_fetch_addw_le_rh850 +#define helper_atomic_fetch_andw_le helper_atomic_fetch_andw_le_rh850 +#define helper_atomic_fetch_orw_le helper_atomic_fetch_orw_le_rh850 +#define helper_atomic_fetch_xorw_le helper_atomic_fetch_xorw_le_rh850 +#define helper_atomic_add_fetchw_le helper_atomic_add_fetchw_le_rh850 +#define helper_atomic_and_fetchw_le helper_atomic_and_fetchw_le_rh850 +#define helper_atomic_or_fetchw_le helper_atomic_or_fetchw_le_rh850 +#define helper_atomic_xor_fetchw_le helper_atomic_xor_fetchw_le_rh850 +#define helper_atomic_fetch_sminw_le helper_atomic_fetch_sminw_le_rh850 +#define helper_atomic_fetch_uminw_le helper_atomic_fetch_uminw_le_rh850 +#define helper_atomic_fetch_smaxw_le helper_atomic_fetch_smaxw_le_rh850 +#define helper_atomic_fetch_umaxw_le helper_atomic_fetch_umaxw_le_rh850 +#define helper_atomic_smin_fetchw_le helper_atomic_smin_fetchw_le_rh850 +#define helper_atomic_umin_fetchw_le helper_atomic_umin_fetchw_le_rh850 +#define helper_atomic_smax_fetchw_le helper_atomic_smax_fetchw_le_rh850 +#define helper_atomic_umax_fetchw_le helper_atomic_umax_fetchw_le_rh850 +#define helper_atomic_cmpxchgw_be helper_atomic_cmpxchgw_be_rh850 +#define helper_atomic_xchgw_be helper_atomic_xchgw_be_rh850 +#define helper_atomic_fetch_andw_be helper_atomic_fetch_andw_be_rh850 +#define helper_atomic_fetch_orw_be helper_atomic_fetch_orw_be_rh850 +#define helper_atomic_fetch_xorw_be helper_atomic_fetch_xorw_be_rh850 +#define helper_atomic_and_fetchw_be helper_atomic_and_fetchw_be_rh850 +#define helper_atomic_or_fetchw_be helper_atomic_or_fetchw_be_rh850 +#define helper_atomic_xor_fetchw_be helper_atomic_xor_fetchw_be_rh850 +#define helper_atomic_fetch_sminw_be helper_atomic_fetch_sminw_be_rh850 +#define helper_atomic_fetch_uminw_be helper_atomic_fetch_uminw_be_rh850 +#define helper_atomic_fetch_smaxw_be helper_atomic_fetch_smaxw_be_rh850 +#define helper_atomic_fetch_umaxw_be helper_atomic_fetch_umaxw_be_rh850 +#define helper_atomic_smin_fetchw_be helper_atomic_smin_fetchw_be_rh850 +#define helper_atomic_umin_fetchw_be helper_atomic_umin_fetchw_be_rh850 +#define helper_atomic_smax_fetchw_be helper_atomic_smax_fetchw_be_rh850 +#define helper_atomic_umax_fetchw_be helper_atomic_umax_fetchw_be_rh850 +#define helper_atomic_fetch_addw_be helper_atomic_fetch_addw_be_rh850 +#define helper_atomic_add_fetchw_be helper_atomic_add_fetchw_be_rh850 +#define helper_atomic_cmpxchgl_le helper_atomic_cmpxchgl_le_rh850 +#define helper_atomic_xchgl_le helper_atomic_xchgl_le_rh850 +#define helper_atomic_fetch_addl_le helper_atomic_fetch_addl_le_rh850 +#define helper_atomic_fetch_andl_le helper_atomic_fetch_andl_le_rh850 +#define helper_atomic_fetch_orl_le helper_atomic_fetch_orl_le_rh850 +#define helper_atomic_fetch_xorl_le helper_atomic_fetch_xorl_le_rh850 +#define helper_atomic_add_fetchl_le helper_atomic_add_fetchl_le_rh850 +#define helper_atomic_and_fetchl_le helper_atomic_and_fetchl_le_rh850 +#define helper_atomic_or_fetchl_le helper_atomic_or_fetchl_le_rh850 +#define helper_atomic_xor_fetchl_le helper_atomic_xor_fetchl_le_rh850 +#define helper_atomic_fetch_sminl_le helper_atomic_fetch_sminl_le_rh850 +#define helper_atomic_fetch_uminl_le helper_atomic_fetch_uminl_le_rh850 +#define helper_atomic_fetch_smaxl_le helper_atomic_fetch_smaxl_le_rh850 +#define helper_atomic_fetch_umaxl_le helper_atomic_fetch_umaxl_le_rh850 +#define helper_atomic_smin_fetchl_le helper_atomic_smin_fetchl_le_rh850 +#define helper_atomic_umin_fetchl_le helper_atomic_umin_fetchl_le_rh850 +#define helper_atomic_smax_fetchl_le helper_atomic_smax_fetchl_le_rh850 +#define helper_atomic_umax_fetchl_le helper_atomic_umax_fetchl_le_rh850 +#define helper_atomic_cmpxchgl_be helper_atomic_cmpxchgl_be_rh850 +#define helper_atomic_xchgl_be helper_atomic_xchgl_be_rh850 +#define helper_atomic_fetch_andl_be helper_atomic_fetch_andl_be_rh850 +#define helper_atomic_fetch_orl_be helper_atomic_fetch_orl_be_rh850 +#define helper_atomic_fetch_xorl_be helper_atomic_fetch_xorl_be_rh850 +#define helper_atomic_and_fetchl_be helper_atomic_and_fetchl_be_rh850 +#define helper_atomic_or_fetchl_be helper_atomic_or_fetchl_be_rh850 +#define helper_atomic_xor_fetchl_be helper_atomic_xor_fetchl_be_rh850 +#define helper_atomic_fetch_sminl_be helper_atomic_fetch_sminl_be_rh850 +#define helper_atomic_fetch_uminl_be helper_atomic_fetch_uminl_be_rh850 +#define helper_atomic_fetch_smaxl_be helper_atomic_fetch_smaxl_be_rh850 +#define helper_atomic_fetch_umaxl_be helper_atomic_fetch_umaxl_be_rh850 +#define helper_atomic_smin_fetchl_be helper_atomic_smin_fetchl_be_rh850 +#define helper_atomic_umin_fetchl_be helper_atomic_umin_fetchl_be_rh850 +#define helper_atomic_smax_fetchl_be helper_atomic_smax_fetchl_be_rh850 +#define helper_atomic_umax_fetchl_be helper_atomic_umax_fetchl_be_rh850 +#define helper_atomic_fetch_addl_be helper_atomic_fetch_addl_be_rh850 +#define helper_atomic_add_fetchl_be helper_atomic_add_fetchl_be_rh850 +#define helper_atomic_cmpxchgq_le helper_atomic_cmpxchgq_le_rh850 +#define helper_atomic_xchgq_le helper_atomic_xchgq_le_rh850 +#define helper_atomic_fetch_addq_le helper_atomic_fetch_addq_le_rh850 +#define helper_atomic_fetch_andq_le helper_atomic_fetch_andq_le_rh850 +#define helper_atomic_fetch_orq_le helper_atomic_fetch_orq_le_rh850 +#define helper_atomic_fetch_xorq_le helper_atomic_fetch_xorq_le_rh850 +#define helper_atomic_add_fetchq_le helper_atomic_add_fetchq_le_rh850 +#define helper_atomic_and_fetchq_le helper_atomic_and_fetchq_le_rh850 +#define helper_atomic_or_fetchq_le helper_atomic_or_fetchq_le_rh850 +#define helper_atomic_xor_fetchq_le helper_atomic_xor_fetchq_le_rh850 +#define helper_atomic_fetch_sminq_le helper_atomic_fetch_sminq_le_rh850 +#define helper_atomic_fetch_uminq_le helper_atomic_fetch_uminq_le_rh850 +#define helper_atomic_fetch_smaxq_le helper_atomic_fetch_smaxq_le_rh850 +#define helper_atomic_fetch_umaxq_le helper_atomic_fetch_umaxq_le_rh850 +#define helper_atomic_smin_fetchq_le helper_atomic_smin_fetchq_le_rh850 +#define helper_atomic_umin_fetchq_le helper_atomic_umin_fetchq_le_rh850 +#define helper_atomic_smax_fetchq_le helper_atomic_smax_fetchq_le_rh850 +#define helper_atomic_umax_fetchq_le helper_atomic_umax_fetchq_le_rh850 +#define helper_atomic_cmpxchgq_be helper_atomic_cmpxchgq_be_rh850 +#define helper_atomic_xchgq_be helper_atomic_xchgq_be_rh850 +#define helper_atomic_fetch_andq_be helper_atomic_fetch_andq_be_rh850 +#define helper_atomic_fetch_orq_be helper_atomic_fetch_orq_be_rh850 +#define helper_atomic_fetch_xorq_be helper_atomic_fetch_xorq_be_rh850 +#define helper_atomic_and_fetchq_be helper_atomic_and_fetchq_be_rh850 +#define helper_atomic_or_fetchq_be helper_atomic_or_fetchq_be_rh850 +#define helper_atomic_xor_fetchq_be helper_atomic_xor_fetchq_be_rh850 +#define helper_atomic_fetch_sminq_be helper_atomic_fetch_sminq_be_rh850 +#define helper_atomic_fetch_uminq_be helper_atomic_fetch_uminq_be_rh850 +#define helper_atomic_fetch_smaxq_be helper_atomic_fetch_smaxq_be_rh850 +#define helper_atomic_fetch_umaxq_be helper_atomic_fetch_umaxq_be_rh850 +#define helper_atomic_smin_fetchq_be helper_atomic_smin_fetchq_be_rh850 +#define helper_atomic_umin_fetchq_be helper_atomic_umin_fetchq_be_rh850 +#define helper_atomic_smax_fetchq_be helper_atomic_smax_fetchq_be_rh850 +#define helper_atomic_umax_fetchq_be helper_atomic_umax_fetchq_be_rh850 +#define helper_atomic_fetch_addq_be helper_atomic_fetch_addq_be_rh850 +#define helper_atomic_add_fetchq_be helper_atomic_add_fetchq_be_rh850 +#define cpu_ldub_code cpu_ldub_code_rh850 +#define cpu_lduw_code cpu_lduw_code_rh850 +#define cpu_ldl_code cpu_ldl_code_rh850 +#define cpu_ldq_code cpu_ldq_code_rh850 +#define helper_div_i32 helper_div_i32_rh850 +#define helper_rem_i32 helper_rem_i32_rh850 +#define helper_divu_i32 helper_divu_i32_rh850 +#define helper_remu_i32 helper_remu_i32_rh850 +#define helper_shl_i64 helper_shl_i64_rh850 +#define helper_shr_i64 helper_shr_i64_rh850 +#define helper_sar_i64 helper_sar_i64_rh850 +#define helper_div_i64 helper_div_i64_rh850 +#define helper_rem_i64 helper_rem_i64_rh850 +#define helper_divu_i64 helper_divu_i64_rh850 +#define helper_remu_i64 helper_remu_i64_rh850 +#define helper_muluh_i64 helper_muluh_i64_rh850 +#define helper_mulsh_i64 helper_mulsh_i64_rh850 +#define helper_clz_i32 helper_clz_i32_rh850 +#define helper_ctz_i32 helper_ctz_i32_rh850 +#define helper_clz_i64 helper_clz_i64_rh850 +#define helper_ctz_i64 helper_ctz_i64_rh850 +#define helper_clrsb_i32 helper_clrsb_i32_rh850 +#define helper_clrsb_i64 helper_clrsb_i64_rh850 +#define helper_ctpop_i32 helper_ctpop_i32_rh850 +#define helper_ctpop_i64 helper_ctpop_i64_rh850 +#define helper_lookup_tb_ptr helper_lookup_tb_ptr_rh850 +#define helper_exit_atomic helper_exit_atomic_rh850 +#define helper_gvec_add8 helper_gvec_add8_rh850 +#define helper_gvec_add16 helper_gvec_add16_rh850 +#define helper_gvec_add32 helper_gvec_add32_rh850 +#define helper_gvec_add64 helper_gvec_add64_rh850 +#define helper_gvec_adds8 helper_gvec_adds8_rh850 +#define helper_gvec_adds16 helper_gvec_adds16_rh850 +#define helper_gvec_adds32 helper_gvec_adds32_rh850 +#define helper_gvec_adds64 helper_gvec_adds64_rh850 +#define helper_gvec_sub8 helper_gvec_sub8_rh850 +#define helper_gvec_sub16 helper_gvec_sub16_rh850 +#define helper_gvec_sub32 helper_gvec_sub32_rh850 +#define helper_gvec_sub64 helper_gvec_sub64_rh850 +#define helper_gvec_subs8 helper_gvec_subs8_rh850 +#define helper_gvec_subs16 helper_gvec_subs16_rh850 +#define helper_gvec_subs32 helper_gvec_subs32_rh850 +#define helper_gvec_subs64 helper_gvec_subs64_rh850 +#define helper_gvec_mul8 helper_gvec_mul8_rh850 +#define helper_gvec_mul16 helper_gvec_mul16_rh850 +#define helper_gvec_mul32 helper_gvec_mul32_rh850 +#define helper_gvec_mul64 helper_gvec_mul64_rh850 +#define helper_gvec_muls8 helper_gvec_muls8_rh850 +#define helper_gvec_muls16 helper_gvec_muls16_rh850 +#define helper_gvec_muls32 helper_gvec_muls32_rh850 +#define helper_gvec_muls64 helper_gvec_muls64_rh850 +#define helper_gvec_neg8 helper_gvec_neg8_rh850 +#define helper_gvec_neg16 helper_gvec_neg16_rh850 +#define helper_gvec_neg32 helper_gvec_neg32_rh850 +#define helper_gvec_neg64 helper_gvec_neg64_rh850 +#define helper_gvec_abs8 helper_gvec_abs8_rh850 +#define helper_gvec_abs16 helper_gvec_abs16_rh850 +#define helper_gvec_abs32 helper_gvec_abs32_rh850 +#define helper_gvec_abs64 helper_gvec_abs64_rh850 +#define helper_gvec_mov helper_gvec_mov_rh850 +#define helper_gvec_dup64 helper_gvec_dup64_rh850 +#define helper_gvec_dup32 helper_gvec_dup32_rh850 +#define helper_gvec_dup16 helper_gvec_dup16_rh850 +#define helper_gvec_dup8 helper_gvec_dup8_rh850 +#define helper_gvec_not helper_gvec_not_rh850 +#define helper_gvec_and helper_gvec_and_rh850 +#define helper_gvec_or helper_gvec_or_rh850 +#define helper_gvec_xor helper_gvec_xor_rh850 +#define helper_gvec_andc helper_gvec_andc_rh850 +#define helper_gvec_orc helper_gvec_orc_rh850 +#define helper_gvec_nand helper_gvec_nand_rh850 +#define helper_gvec_nor helper_gvec_nor_rh850 +#define helper_gvec_eqv helper_gvec_eqv_rh850 +#define helper_gvec_ands helper_gvec_ands_rh850 +#define helper_gvec_xors helper_gvec_xors_rh850 +#define helper_gvec_ors helper_gvec_ors_rh850 +#define helper_gvec_shl8i helper_gvec_shl8i_rh850 +#define helper_gvec_shl16i helper_gvec_shl16i_rh850 +#define helper_gvec_shl32i helper_gvec_shl32i_rh850 +#define helper_gvec_shl64i helper_gvec_shl64i_rh850 +#define helper_gvec_shr8i helper_gvec_shr8i_rh850 +#define helper_gvec_shr16i helper_gvec_shr16i_rh850 +#define helper_gvec_shr32i helper_gvec_shr32i_rh850 +#define helper_gvec_shr64i helper_gvec_shr64i_rh850 +#define helper_gvec_sar8i helper_gvec_sar8i_rh850 +#define helper_gvec_sar16i helper_gvec_sar16i_rh850 +#define helper_gvec_sar32i helper_gvec_sar32i_rh850 +#define helper_gvec_sar64i helper_gvec_sar64i_rh850 +#define helper_gvec_shl8v helper_gvec_shl8v_rh850 +#define helper_gvec_shl16v helper_gvec_shl16v_rh850 +#define helper_gvec_shl32v helper_gvec_shl32v_rh850 +#define helper_gvec_shl64v helper_gvec_shl64v_rh850 +#define helper_gvec_shr8v helper_gvec_shr8v_rh850 +#define helper_gvec_shr16v helper_gvec_shr16v_rh850 +#define helper_gvec_shr32v helper_gvec_shr32v_rh850 +#define helper_gvec_shr64v helper_gvec_shr64v_rh850 +#define helper_gvec_sar8v helper_gvec_sar8v_rh850 +#define helper_gvec_sar16v helper_gvec_sar16v_rh850 +#define helper_gvec_sar32v helper_gvec_sar32v_rh850 +#define helper_gvec_sar64v helper_gvec_sar64v_rh850 +#define helper_gvec_eq8 helper_gvec_eq8_rh850 +#define helper_gvec_ne8 helper_gvec_ne8_rh850 +#define helper_gvec_lt8 helper_gvec_lt8_rh850 +#define helper_gvec_le8 helper_gvec_le8_rh850 +#define helper_gvec_ltu8 helper_gvec_ltu8_rh850 +#define helper_gvec_leu8 helper_gvec_leu8_rh850 +#define helper_gvec_eq16 helper_gvec_eq16_rh850 +#define helper_gvec_ne16 helper_gvec_ne16_rh850 +#define helper_gvec_lt16 helper_gvec_lt16_rh850 +#define helper_gvec_le16 helper_gvec_le16_rh850 +#define helper_gvec_ltu16 helper_gvec_ltu16_rh850 +#define helper_gvec_leu16 helper_gvec_leu16_rh850 +#define helper_gvec_eq32 helper_gvec_eq32_rh850 +#define helper_gvec_ne32 helper_gvec_ne32_rh850 +#define helper_gvec_lt32 helper_gvec_lt32_rh850 +#define helper_gvec_le32 helper_gvec_le32_rh850 +#define helper_gvec_ltu32 helper_gvec_ltu32_rh850 +#define helper_gvec_leu32 helper_gvec_leu32_rh850 +#define helper_gvec_eq64 helper_gvec_eq64_rh850 +#define helper_gvec_ne64 helper_gvec_ne64_rh850 +#define helper_gvec_lt64 helper_gvec_lt64_rh850 +#define helper_gvec_le64 helper_gvec_le64_rh850 +#define helper_gvec_ltu64 helper_gvec_ltu64_rh850 +#define helper_gvec_leu64 helper_gvec_leu64_rh850 +#define helper_gvec_ssadd8 helper_gvec_ssadd8_rh850 +#define helper_gvec_ssadd16 helper_gvec_ssadd16_rh850 +#define helper_gvec_ssadd32 helper_gvec_ssadd32_rh850 +#define helper_gvec_ssadd64 helper_gvec_ssadd64_rh850 +#define helper_gvec_sssub8 helper_gvec_sssub8_rh850 +#define helper_gvec_sssub16 helper_gvec_sssub16_rh850 +#define helper_gvec_sssub32 helper_gvec_sssub32_rh850 +#define helper_gvec_sssub64 helper_gvec_sssub64_rh850 +#define helper_gvec_usadd8 helper_gvec_usadd8_rh850 +#define helper_gvec_usadd16 helper_gvec_usadd16_rh850 +#define helper_gvec_usadd32 helper_gvec_usadd32_rh850 +#define helper_gvec_usadd64 helper_gvec_usadd64_rh850 +#define helper_gvec_ussub8 helper_gvec_ussub8_rh850 +#define helper_gvec_ussub16 helper_gvec_ussub16_rh850 +#define helper_gvec_ussub32 helper_gvec_ussub32_rh850 +#define helper_gvec_ussub64 helper_gvec_ussub64_rh850 +#define helper_gvec_smin8 helper_gvec_smin8_rh850 +#define helper_gvec_smin16 helper_gvec_smin16_rh850 +#define helper_gvec_smin32 helper_gvec_smin32_rh850 +#define helper_gvec_smin64 helper_gvec_smin64_rh850 +#define helper_gvec_smax8 helper_gvec_smax8_rh850 +#define helper_gvec_smax16 helper_gvec_smax16_rh850 +#define helper_gvec_smax32 helper_gvec_smax32_rh850 +#define helper_gvec_smax64 helper_gvec_smax64_rh850 +#define helper_gvec_umin8 helper_gvec_umin8_rh850 +#define helper_gvec_umin16 helper_gvec_umin16_rh850 +#define helper_gvec_umin32 helper_gvec_umin32_rh850 +#define helper_gvec_umin64 helper_gvec_umin64_rh850 +#define helper_gvec_umax8 helper_gvec_umax8_rh850 +#define helper_gvec_umax16 helper_gvec_umax16_rh850 +#define helper_gvec_umax32 helper_gvec_umax32_rh850 +#define helper_gvec_umax64 helper_gvec_umax64_rh850 +#define helper_gvec_bitsel helper_gvec_bitsel_rh850 +#define cpu_restore_state cpu_restore_state_rh850 +#define page_collection_lock page_collection_lock_rh850 +#define page_collection_unlock page_collection_unlock_rh850 +#define free_code_gen_buffer free_code_gen_buffer_rh850 +#define tcg_exec_init tcg_exec_init_rh850 +#define tb_cleanup tb_cleanup_rh850 +#define tb_flush tb_flush_rh850 +#define tb_phys_invalidate tb_phys_invalidate_rh850 +#define tb_gen_code tb_gen_code_rh850 +#define tb_exec_lock tb_exec_lock_rh850 +#define tb_exec_unlock tb_exec_unlock_rh850 +#define tb_invalidate_phys_page_range tb_invalidate_phys_page_range_rh850 +#define tb_invalidate_phys_range tb_invalidate_phys_range_rh850 +#define tb_invalidate_phys_page_fast tb_invalidate_phys_page_fast_rh850 +#define tb_check_watchpoint tb_check_watchpoint_rh850 +#define cpu_io_recompile cpu_io_recompile_rh850 +#define tb_flush_jmp_cache tb_flush_jmp_cache_rh850 +#define tcg_flush_softmmu_tlb tcg_flush_softmmu_tlb_rh850 +#define translator_loop_temp_check translator_loop_temp_check_rh850 +#define translator_loop translator_loop_rh850 +#define helper_atomic_cmpxchgo_le_mmu helper_atomic_cmpxchgo_le_mmu_rh850 +#define helper_atomic_cmpxchgo_be_mmu helper_atomic_cmpxchgo_be_mmu_rh850 +#define helper_atomic_ldo_le_mmu helper_atomic_ldo_le_mmu_rh850 +#define helper_atomic_ldo_be_mmu helper_atomic_ldo_be_mmu_rh850 +#define helper_atomic_sto_le_mmu helper_atomic_sto_le_mmu_rh850 +#define helper_atomic_sto_be_mmu helper_atomic_sto_be_mmu_rh850 +#define unassigned_mem_ops unassigned_mem_ops_rh850 +#define floatx80_infinity floatx80_infinity_rh850 +#define dup_const_func dup_const_func_rh850 +#define gen_helper_raise_exception gen_helper_raise_exception_rh850 +#define gen_helper_raise_interrupt gen_helper_raise_interrupt_rh850 +#define gen_helper_vfp_get_fpscr gen_helper_vfp_get_fpscr_rh850 +#define gen_helper_vfp_set_fpscr gen_helper_vfp_set_fpscr_rh850 +#define gen_helper_cpsr_read gen_helper_cpsr_read_rh850 +#define gen_helper_cpsr_write gen_helper_cpsr_write_rh850 +#endif diff --git a/qemu/target/rh850/Makefile.objs b/qemu/target/rh850/Makefile.objs new file mode 100644 index 0000000000..abd0a7cde3 --- /dev/null +++ b/qemu/target/rh850/Makefile.objs @@ -0,0 +1 @@ +obj-y += translate.o op_helper.o helper.o cpu.o fpu_helper.o gdbstub.o pmp.o diff --git a/qemu/target/rh850/cpu-param.h b/qemu/target/rh850/cpu-param.h new file mode 100644 index 0000000000..24231873c3 --- /dev/null +++ b/qemu/target/rh850/cpu-param.h @@ -0,0 +1,11 @@ +#pragma once + +/* QEMU addressing/paging config */ +#define TARGET_PAGE_BITS 12 /* 4 KiB Pages */ + +#define TARGET_LONG_BITS 32 +#define TARGET_PHYS_ADDR_SPACE_BITS 32 +#define TARGET_VIRT_ADDR_SPACE_BITS 32 + +#define NB_MMU_MODES 4 + diff --git a/qemu/target/rh850/cpu.c b/qemu/target/rh850/cpu.c new file mode 100644 index 0000000000..172fcd51f2 --- /dev/null +++ b/qemu/target/rh850/cpu.c @@ -0,0 +1,639 @@ +/* + * QEMU RH850 CPU + * + * Copyright (c) 2018-2019 iSYSTEM Labs d.o.o. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/ctype.h" +#include "cpu.h" +#include "exec/exec-all.h" + +/* RH850 CPU definitions */ + +/* Program registers (rh850_prog_regnames): + * r0 - zero + * r1 - assembler reserved register + * r2 - real-time OS register / address and data variable register + * r3 - stack pointer + * r4 - global pointer + * r5 - text pointer + * r6-r29 - address and data variable registers + * r30 - element pointer + * r31 - link pointer + */ + +const char * const rh850_gp_regnames[] = { + "r0-zero", "r1", "r2", "r3-sp", "r4", "r5", "r6", "r7", + "r8", "r9", "r10 ", "r11", "r12", "r13", "r14", "r15", + "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r2 ", + "r24", "r25", "r26", "r27", "r28", "r29", "r30-ep", "r31-lp" +}; + +// Basic system registers +const char * const rh850_sys_regnames[][MAX_SYS_REGS_IN_BANK] = { + +{ // SELECTION ID 0 [5] used to be psw, but now it is stored in flags only + "eipc", "eipsw", "fepc", "fepsw", NULL, NULL, "fpsr", "fpepc", "fpst", "fpcc", + "fpcfg", "fpec", NULL, "eiic", "feic", NULL, "ctpc", "ctpsw", NULL, NULL, + "ctbp", NULL, NULL, NULL, NULL, NULL, NULL, NULL, "eiwr", "fewr", + NULL, "bsel"}, +{ // SELECTION ID 1 + "mcfg0", NULL, "rbase", "ebase", "intbp", "mctl", "pid", "fpipr", NULL, NULL, + NULL, "sccfg", "scbp", +}, +{ // SELECTION ID 2 + "htcfg0",NULL, NULL, NULL, NULL, NULL, "mea", "asid", "mei", NULL, + "ispr", "pmr", "icsr", "intcfg" +}, +{ // SELECTION ID 3 + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL +}, +{ // SELECTION ID 4 + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, "ictagl", "ictagh","icdatl","icdath", + NULL, NULL, NULL, NULL, "icctrl",NULL, "iccfg", NULL, "icerr", NULL +}, +{ // SELECTION ID 5 + "mpm", "mprc", NULL, NULL, "mpbrgn","mptrgn",NULL, NULL, "mca", "mcs" + "mcc", "mcr" +}, +{ // SELECTION ID 6 + "mpla0", "mpua0", "mpat0", NULL, "mpla1", "mpua1", "mpat1", NULL, "mpla2", "mpua2", + "mpat2", NULL, "mpla3", "mpua3", "mpat3", NULL, "mpla4", "mpua4", "mpat4", NULL, + "mpla5", "mpua5", "mpat5", NULL, "mpla6", "mpua6", "mpat6", NULL, "mpla7", "mpua7", + "mpat7", NULL +}, +{ // SELECTION ID 7 + /* MPU function system registers */ + "mpla8", "mpua8", "mpat8", NULL, "mpla9", "mpua9", "mpat9", NULL, "mpla10","mpua10", + "mpat10",NULL, "mpla11", "mpua11", "mpat11",NULL, "mpla12","mpua12","mpat12",NULL, + "mpla13","mpua13","mpat13", NULL, "mpla14","mpua14","mpat14",NULL, "mpla15","mpua15", + "mpat15",NULL +} +}; + +// Where bits are read only, mask is set to 0 +const uint32_t rh850_sys_reg_read_only_masks[][MAX_SYS_REGS_IN_BANK] = { + +{ //SELECTION ID 0 PSW - implemented as registers for each used bit, see cpu_ZF, ... + 0xFFFFFFFF, 0x40078EFF, 0xFFFFFFFF, 0x40078EFF, 0x0, /*0x40018EFF*/ 0, 0xFFEEFFFF, 0xFFFFFFFE, 0x00003F3F, 0x000000FF, + 0x0000031F, 0x00000001, 0x0, 0xFFFFFFFF, 0xFFFFFFFF, 0x0, 0xFFFFFFFF, 0x0000001F, 0x0, 0x0, + 0xFFFFFFFE, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFFFFFFFF, 0xFFFFFFFF, + 0x0, 0x0 +}, +{ //SELECTION ID 1 + // for MCFG (idx = 0), byte 3 seems to not be writable, at least on devicee used for testing + 0x00000000, 0x0, 0x00000000, 0xFFFFFE01, 0xFFFFFE00, 0x00000003, 0x00000000, 0x0000001F, 0x0, 0x0, + 0x0, 0x000000FF, 0xFFFFFFFC +}, +{ //SELECTION ID 2 + 0x00000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFFFFFFFF, 0x000003FF, 0x001F073F, 0x0, + 0x00000000, 0x0000FFFF, 0x00000000, 0x00000001 +}, +{ //SELECTION ID 3 + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 +}, +{ //SELECTION ID 4 + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xFFFFFA35, 0xF0FFFF00, 0xFFFFFFFF, 0xFFFFFFFF, + 0x0, 0x0, 0x0, 0x0, 0x00020107, 0x0, 0x00000000, 0x0, 0xBF3F7FFD, 0x0 +}, +{ //SELECTION ID 5 + 0x00000003, 0x0000FFFF, 0x0, 0x0, 0x00000000, 0x00000000, 0x0, 0x0, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0x0000013F +}, +{ //SELECTION ID 6 + 0xFFFFFFFC, 0xFFFFFFFC, 0x03FF00FF, 0x0, 0xFFFFFFFC, 0xFFFFFFFC, 0x03FF00FF, 0x0, 0xFFFFFFFC, 0xFFFFFFFF, + 0x03FF00FF, 0x0, 0xFFFFFFFC, 0xFFFFFFFC, 0x03FF00FF, 0x0, 0xFFFFFFFC, 0xFFFFFFFC, 0x03FF00FF, 0x0, + 0xFFFFFFFC, 0xFFFFFFFC, 0x03FF00FF, 0x0, 0xFFFFFFFC, 0xFFFFFFFC, 0x03FF00FF, 0x0, 0xFFFFFFFC, 0xFFFFFFFC, + 0x03FF00FF, 0x0 +}, +{ //SELECTION ID 7 + 0xFFFFFFFC, 0xFFFFFFFC, 0x03FF00FF, 0x0, 0xFFFFFFFC, 0xFFFFFFFC, 0x03FF00FF, 0x0, 0xFFFFFFFC, 0xFFFFFFFF, + 0x03FF00FF, 0x0, 0xFFFFFFFC, 0xFFFFFFFC, 0x03FF00FF, 0x0, 0xFFFFFFFC, 0xFFFFFFFC, 0x03FF00FF, 0x0, + 0xFFFFFFFC, 0xFFFFFFFC, 0x03FF00FF, 0x0, 0xFFFFFFFC, 0xFFFFFFFC, 0x03FF00FF, 0x0, 0xFFFFFFFC, 0xFFFFFFFC, + 0x03FF00FF, 0x0 +} +}; + + +const uint32_t rh850_sys_reg_read_only_values[][MAX_SYS_REGS_IN_BANK] = { +{ //SELECTION ID 0 + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0 +}, +{ //SELECTION ID 1 + 0x4, 0x0, 0x0, 0x0, 0x0, 0x80000000, 0x12345678, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0 +}, +{ //SELECTION ID 2 + 0x00008000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0 +}, +{ //SELECTION ID 3 + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 +}, +{ //SELECTION ID 4 + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x00010000, 0x0, 0x00010000, 0x0, 0x0, 0x0 +}, +{ //SELECTION ID 5 + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0 +}, +{ //SELECTION ID 6 + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0 +}, +{ //SELECTION ID 7 + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0 +} +}; + + + +/*Data Buffer Operation Registers (rh850_sys_databuff_regnames): + * sr24, 13 - cbdcr */ +const char * const rh850_sys_databuff_regnames[] = { /* Data buffer operation registers */ + "cbdcr" +}; + +const char * const rh850_excp_names[] = { + "misaligned_fetch", + "fault_fetch", + "illegal_instruction", + "breakpoint", + "misaligned_load", + "fault_load", + "misaligned_store", + "fault_store", + "user_ecall", + "supervisor_ecall", + "hypervisor_ecall", + "machine_ecall", + "exec_page_fault", + "load_page_fault", + "reserved", + "store_page_fault" +}; + +const char * const rh850_intr_names[] = { + "u_software", + "s_software", + "h_software", + "m_software", + "u_timer", + "s_timer", + "h_timer", + "m_timer", + "u_external", + "s_external", + "h_external", + "m_external", + "coprocessor", + "host" +}; + +/* +static void set_misa(CPURH850State *env, target_ulong misa) +{ + env->misa = misa; +} + +static void set_versions(CPURH850State *env, int user_ver, int priv_ver) +{ + env->user_ver = user_ver; + env->priv_ver = priv_ver; +} +*/ + +/* +static void set_feature(CPURH850State *env, int feature) +{ + env->features |= (1ULL << feature); +} */ + +/* +#if defined(TARGET_RH850) + +static void rv32gcsu_priv1_09_1_cpu_init(Object *obj) +{ + CPURH850State *env = &RH850_CPU(obj)->env; + set_misa(env, RV32 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU); + set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_09_1); + set_resetvec(env, DEFAULT_RSTVEC); + set_feature(env, RH850_FEATURE_MMU); +} + +static void rv32gcsu_priv1_10_0_cpu_init(Object *obj) +{ + CPURH850State *env = &RH850_CPU(obj)->env; + set_misa(env, RV32 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU); + set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0); + set_resetvec(env, DEFAULT_RSTVEC); + set_feature(env, RH850_FEATURE_MMU); +} + +static void rv32imacu_nommu_cpu_init(Object *obj) +{ + CPURH850State *env = &RH850_CPU(obj)->env; + set_misa(env, RV32 | RVI | RVM | RVA | RVC | RVU); + set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0); + set_resetvec(env, DEFAULT_RSTVEC); +} + +#elif defined(TARGET_RH85064) + +static void rv64gcsu_priv1_09_1_cpu_init(Object *obj) +{ + CPURH850State *env = &RH850_CPU(obj)->env; + set_misa(env, RV64 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU); + set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_09_1); + set_resetvec(env, DEFAULT_RSTVEC); + set_feature(env, RH850_FEATURE_MMU); +} + +static void rv64gcsu_priv1_10_0_cpu_init(Object *obj) +{ + CPURH850State *env = &RH850_CPU(obj)->env; + set_misa(env, RV64 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU); + set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0); + set_resetvec(env, DEFAULT_RSTVEC); + set_feature(env, RH850_FEATURE_MMU); +} + +static void rv64imacu_nommu_cpu_init(Object *obj) +{ + CPURH850State *env = &RH850_CPU(obj)->env; + set_misa(env, RV64 | RVI | RVM | RVA | RVC | RVU); + set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0); + set_resetvec(env, DEFAULT_RSTVEC); +} + +#endif +*/ + +void rh850_cpu_set_pc(CPUState *cs, vaddr value) +{ + RH850CPU *cpu = RH850_CPU(cs); + CPURH850State *env = &cpu->env; + env->pc = value; +} + +vaddr rh850_cpu_get_pc(CPUState *cs) +{ + RH850CPU *cpu = RH850_CPU(cs); + CPURH850State *env = &cpu->env; + return env->pc; +} + +AddressSpace *cpu_addressspace(CPUState *cs, MemTxAttrs attrs) +{ + return cpu_get_address_space(cs, cpu_asidx_from_attrs(cs, attrs)); +} + + +/* called by qemu's softmmu to fill the qemu tlb */ +static bool rh850_tlb_fill(CPUState *cs, vaddr addr, int size, + MMUAccessType access_type, int mmu_idx, + bool probe, uintptr_t retaddr) +{ + int ret; + ret = rh850_cpu_handle_mmu_fault(cs, addr, size, access_type, mmu_idx); + if (ret == TRANSLATE_FAIL) { + RH850CPU *cpu = RH850_CPU(cs); + CPURH850State *env = &cpu->env; + do_raise_exception_err(env, cs->exception_index, retaddr); + } + return true; +} + + +static void rh850_cpu_synchronize_from_tb(CPUState *cs, TranslationBlock *tb) +{ + RH850CPU *cpu = RH850_CPU(cs); + CPURH850State *env = &cpu->env; + env->pc = tb->pc; +} + +static bool rh850_cpu_has_work(CPUState *cs) +{ +#ifndef CONFIG_USER_ONLY + return true; +#else + return true; +#endif +} + +void restore_state_to_opc(CPURH850State *env, TranslationBlock *tb, + target_ulong *data) +{ + env->pc = data[0]; +} + + +/* not yet adapted for rh850 from ARM +int rh850_gen_dynamic_xml(CPUState *cs) +{ + RH850CPU *cpu = RH850_CPU(cs); + GString *s = g_string_new(NULL); + RegisterSysregXmlParam param = {cs, s}; + + cpu->dyn_xml.num_cpregs = 0; + cpu->dyn_xml.cpregs_keys = g_new(uint32_t, g_hash_table_size(cpu->cp_regs)); + g_string_printf(s, ""); + g_string_append_printf(s, ""); + g_string_append_printf(s, ""); + g_hash_table_foreach(cpu->cp_regs, arm_register_sysreg_for_xml, ¶m); + g_string_append_printf(s, ""); + cpu->dyn_xml.desc = g_string_free(s, false); + return cpu->dyn_xml.num_cpregs; +} */ + + + +static void rh850_raise_exception(CPURH850State *env, uint32_t excp, + uint32_t syndrome, uint32_t target_el) +{ + CPUState *cs = CPU(rh850_env_get_cpu(env)); + + cs->exception_index = excp; + // env->exception.syndrome = syndrome; + // env->exception.target_el = target_el; + cpu_loop_exit(cs); +} + + +static void rh850_debug_excp_handler(CPUState *cs) +{ + /* Called by core code when a watchpoint or breakpoint fires; + * need to check which one and raise the appropriate exception. + */ + RH850CPU *cpu = RH850_CPU(cs); + CPURH850State *env = &cpu->env; + CPUWatchpoint *wp_hit = cs->watchpoint_hit; + + if (wp_hit) { + if (wp_hit->flags & BP_CPU) { + // bool wnr = (wp_hit->flags & BP_WATCHPOINT_HIT_WRITE) != 0; + // bool same_el = true; + + cs->watchpoint_hit = NULL; + + // env->exception.fsr = arm_debug_exception_fsr(env); + // env->exception.vaddress = wp_hit->hitaddr; + rh850_raise_exception(env, 0, 0, 0); + } + } else { + uint64_t pc = env->pc; + // bool same_el = true; + + /* (1) GDB breakpoints should be handled first. + * (2) Do not raise a CPU exception if no CPU breakpoint has fired, + * since singlestep is also done by generating a debug internal + * exception. + */ + if (!cpu_breakpoint_test(cs, pc, BP_GDB) && + cpu_breakpoint_test(cs, pc, BP_CPU)) { + + rh850_raise_exception(env, 0, 0, 0); + } + } +} + +static bool check_watchpoints(RH850CPU *cpu) +{ + // CPURH850State *env = &cpu->env; + + /* If watchpoints are disabled globally or we can't take debug + * exceptions here then watchpoint firings are ignored. + */ +// if (extract32(env->cp15.mdscr_el1, 15, 1) == 0 +// || !arm_generate_debug_exceptions(env)) { +// return false; +// } + +// for (int n = 0; n < ARRAY_SIZE(env->cpu_watchpoint); n++) { +// if (bp_wp_matches(cpu, n, true)) { +// return true; +// } +// } +// return false; + return true; +} + + +//static bool bp_wp_matches(RH850CPU *cpu, int n, bool is_wp) +//{ +// CPURH850State *env = &cpu->env; + +// if (is_wp) { +// CPUWatchpoint *wp = env->cpu_watchpoint[n]; +// +// if (!wp || !(wp->flags & BP_WATCHPOINT_HIT)) { +// return false; +// } +// } else { +// uint64_t pc = is_a64(env) ? env->pc : env->regs[15]; +// +// if (!env->cpu_breakpoint[n] || env->cpu_breakpoint[n]->pc != pc) { +// return false; +// } +// } + /* The WATCHPOINT_HIT flag guarantees us that the watchpoint is + * enabled and that the address and access type match; for breakpoints + * we know the address matched; check the remaining fields, including + * linked breakpoints. We rely on WCR and BCR having the same layout + * for the LBN, SSC, HMC, PAC/PMC and is-linked fields. + * Note that some combinations of {PAC, HMC, SSC} are reserved and + * must act either like some valid combination or as if the watchpoint + * were disabled. We choose the former, and use this together with + * the fact that EL3 must always be Secure and EL2 must always be + * Non-Secure to simplify the code slightly compared to the full + * table in the ARM ARM. + */ + +// return true; +//} + + +//static bool check_breakpoints(RH850CPU *cpu) +//{ +// CPURH850State *env = &cpu->env; +// int n; +// +// for (n = 0; n < ARRAY_SIZE(env->cpu_breakpoint); n++) { +// if (bp_wp_matches(cpu, n, false)) { +// return true; +// } +// } +// return false; +// return true; +//} + + +static bool rh850_debug_check_watchpoint(CPUState *cs, CPUWatchpoint *wp) +{ + /* Called by core code when a CPU watchpoint fires; need to check if this + * is also an architectural watchpoint match. + */ + RH850CPU *cpu = RH850_CPU(cs); + + return check_watchpoints(cpu); +} + + +static void rh850_cpu_reset(CPUState *cs) +{ + + RH850CPU *cpu = RH850_CPU(cs); + RH850CPUClass *mcc = RH850_CPU_GET_CLASS(cpu); + CPURH850State *env = &cpu->env; + + mcc->parent_reset(cs); +#ifndef CONFIG_USER_ONLY + env->mstatus &= ~(MSTATUS_MIE | MSTATUS_MPRV); // +#endif + cs->exception_index = EXCP_NONE; + set_default_nan_mode(1, &env->fp_status); + env->pc = 0; + env->ID_flag = 1; // interrupts are disable on reset + env->systemRegs[BANK_ID_BASIC_0][EIPSW_IDX] = 0x20; + env->systemRegs[BANK_ID_BASIC_0][FEPSW_IDX] = 0x20; + env->systemRegs[BANK_ID_BASIC_0][EIIC_IDX] = 0x0; + env->systemRegs[BANK_ID_BASIC_0][FEIC_IDX] = 0x0; + env->systemRegs[BANK_ID_BASIC_0][PSW_IDX] = 0x20; // reset value of PSW + env->systemRegs[BANK_ID_BASIC_0][CTPSW_IDX] = 0; + env->systemRegs[BANK_ID_BASIC_0][CTBP_IDX] = 0; // only bit 0 must be set to 0 + env->systemRegs[BANK_ID_BASIC_2][ASID_IDX2] = 0; // only bits 31-10 must be set to 0 + env->systemRegs[BANK_ID_BASIC_2][HTCFG0_IDX2] = 0x00018000; // const value + env->systemRegs[BANK_ID_BASIC_2][MEI_IDX2] = 0; // only some bits must be 0 + env->systemRegs[BANK_ID_BASIC_1][RBASE_IDX1] = 0; + env->systemRegs[BANK_ID_BASIC_1][EBASE_IDX1] = 0; // only bits 8-1 must be 0 + env->systemRegs[BANK_ID_BASIC_1][INTBP_IDX1] = 0; // only bits 8-0 must be 0 + env->systemRegs[BANK_ID_BASIC_1][PID_IDX1] = 0x05000120; // const + env->systemRegs[BANK_ID_BASIC_1][SCCFG_IDX1] = 0; // bits 31-8 must be 0 + env->systemRegs[BANK_ID_BASIC_1][SCBP_IDX1] = 0; // bits 1-0 must be 0 + env->systemRegs[BANK_ID_BASIC_1][MCFG0_IDX1] = 0x4; // bits 31-8 must be 0 + env->systemRegs[BANK_ID_BASIC_1][MCTL_IDX1] = 0x80000000; // bits 31-8 must be 0 + + env->systemRegs[BANK_ID_BASIC_2][FPIPR_IDX1] = 0; + env->systemRegs[BANK_ID_BASIC_2][ISPR_IDX2] = 0; + env->systemRegs[BANK_ID_BASIC_2][PMR_IDX2] = 0; + env->systemRegs[BANK_ID_BASIC_2][ICSR_IDX2] = 0; + env->systemRegs[BANK_ID_BASIC_2][INTCFG_IDX2] = 0; +} + +static void rh850_cpu_realize(struct uc_struct *uc, CPUState *dev) +{ + CPUState *cs = CPU(dev); + + cpu_exec_realizefn(cs); + + qemu_init_vcpu(cs); + + cpu_reset(cs); +} + +static void rh850_cpu_init(struct uc_struct *uc, CPUState *obj) +{ + CPUState *cs = CPU(obj); + RH850CPU *cpu = RH850_CPU(obj); + + /* Set CPU pointers. */ + cpu_set_cpustate_pointers(cpu); + + cs->env_ptr = &cpu->env; +} + +static void rh850_cpu_class_init(struct uc_struct *uc, CPUClass *c) +{ + RH850CPUClass *mcc = RH850_CPU_CLASS(c); + CPUClass *cc = CPU_CLASS(c); + + mcc->parent_reset = cc->reset; + cc->reset = rh850_cpu_reset; + + cc->has_work = rh850_cpu_has_work; + cc->do_interrupt = rh850_cpu_do_interrupt; + cc->cpu_exec_interrupt = rh850_cpu_exec_interrupt; + cc->set_pc = rh850_cpu_set_pc; + cc->tlb_fill = rh850_tlb_fill; + cc->synchronize_from_tb = rh850_cpu_synchronize_from_tb; + cc->debug_excp_handler = rh850_debug_excp_handler; + cc->debug_check_watchpoint = rh850_debug_check_watchpoint; + +#ifdef CONFIG_USER_ONLY + cc->handle_mmu_fault = rh850_cpu_handle_mmu_fault; +#else + cc->do_unaligned_access = rh850_cpu_do_unaligned_access; + cc->get_phys_page_debug = rh850_cpu_get_phys_page_debug; +#endif +#ifdef CONFIG_TCG + cc->tcg_initialize = rh850_translate_init; +#endif +} + +RH850CPU *cpu_rh850_init(struct uc_struct *uc, const char *cpu_model) +{ + RH850CPU *cpu; + CPUState *cs; + CPUClass *cc; + + cpu = calloc(1, sizeof(*cpu)); + if (cpu == NULL) { + return NULL; + } + + cs = (CPUState *)cpu; + cc = (CPUClass *)&cpu->cc; + cs->cc = cc; + cs->uc = uc; + uc->cpu = (CPUState *)cpu; + + /* init CPUClass */ + cpu_class_init(uc, cc); + + /* init CPUClass */ + rh850_cpu_class_init(uc, cc); + + /* init CPUState */ + cpu_common_initfn(uc, cs); + + /* init CPU */ + rh850_cpu_init(uc, cs); + + /* realize CPU */ + rh850_cpu_realize(uc, cs); + + // init addresss space + cpu_address_space_init(cs, 0, cs->memory); + + return cpu; +} + + + + diff --git a/qemu/target/rh850/cpu.h b/qemu/target/rh850/cpu.h new file mode 100644 index 0000000000..79f9f8d2f6 --- /dev/null +++ b/qemu/target/rh850/cpu.h @@ -0,0 +1,322 @@ +/* + * QEMU RH850 CPU + * + * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu + * Copyright (c) 2017-2018 SiFive, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef RH850_CPU_H +#define RH850_CPU_H + +#define TCG_GUEST_DEFAULT_MO 0 + +#define ELF_MACHINE EM_RH850 +#define CPUArchState struct CPURH850State + +#include "qemu-common.h" +#include "hw/core/cpu.h" +#include "exec/cpu-defs.h" +#include "fpu/softfloat.h" + +#define TYPE_RH850_CPU "rh850-cpu" + +#define RH850_CPU_TYPE_SUFFIX "-" TYPE_RH850_CPU +#define RH850_CPU_TYPE_NAME(name) (name RH850_CPU_TYPE_SUFFIX) +#define CPU_RESOLVING_TYPE TYPE_RH850_CPU +#define TYPE_RH850_CPU_ANY RH850_CPU_TYPE_NAME("any") + +#define RV32 ((target_ulong)1 << (TARGET_LONG_BITS - 2)) +#define RV64 ((target_ulong)2 << (TARGET_LONG_BITS - 2)) + +#if defined(TARGET_RH850) +#define RVXLEN RV32 +#elif defined(TARGET_RH85064) +#define RVXLEN RV64 +#endif + +#define RV(x) ((target_ulong)1 << (x - 'A')) + +#define RVI RV('I') +#define RVM RV('M') +#define RVA RV('A') +#define RVF RV('F') +#define RVD RV('D') +#define RVC RV('C') +#define RVS RV('S') +#define RVU RV('U') + +/* S extension denotes that Supervisor mode exists, however it is possible + to have a core that support S mode but does not have an MMU and there + is currently no bit in misa to indicate whether an MMU exists or not + so a cpu features bitfield is required */ +enum { + RH850_FEATURE_MMU +}; + +#define USER_VERSION_2_02_0 0x00020200 +#define PRIV_VERSION_1_09_1 0x00010901 +#define PRIV_VERSION_1_10_0 0x00011000 + +#define TRANSLATE_FAIL 1 +#define TRANSLATE_SUCCESS 0 +#define MMU_USER_IDX 3 + +#define MAX_RH850_PMPS (16) + +typedef struct CPURH850State CPURH850State; + +#include "pmp.h" + +#include "register_indices.h" + +#define NUM_GP_REGS 32 +#define NUM_SYS_REG_BANKS 7 +#define MAX_SYS_REGS_IN_BANK 32 +#define BANK_ID_BASIC_0 0 +#define BANK_ID_BASIC_1 1 +#define BANK_ID_BASIC_2 2 + +struct CPURH850State { + + + target_ulong gpRegs[NUM_GP_REGS]; + target_ulong pc; + target_ulong sysDatabuffRegs[1]; + target_ulong systemRegs[NUM_SYS_REG_BANKS][MAX_SYS_REGS_IN_BANK]; + //target_ulong sysBasicRegs[31]; + //target_ulong sysInterruptRegs[5]; + //uint64_t sysFpuRegs[6]; //using rh850 basic system registers(sr6-sr11), 32-bit or 64-bit precision + //target_ulong sysMpuRegs[56]; + //target_ulong sysCacheRegs[7]; + + // flags contained in PSW register + uint32_t Z_flag; + uint32_t S_flag; + uint32_t OV_flag; + uint32_t CY_flag; + uint32_t SAT_flag; + uint32_t ID_flag; + uint32_t EP_flag; + uint32_t NP_flag; + uint32_t EBV_flag; + uint32_t CU0_flag; + uint32_t CU1_flag; + uint32_t CU2_flag; + uint32_t UM_flag; + + uint32_t condSatisfied; + + target_ulong misa; + + uint32_t features; + target_ulong mstatus; //machine status + + target_ulong cpu_LLbit; // register for mutual exclusion (LDL.W, STC.W) + target_ulong cpu_LLAddress; // register for mutual exclusion (LDL.W, STC.W) + + target_ulong load_res; // inst addr for TCG + target_ulong load_val; // inst val for TCG + + float_status fp_status; // not used yet in rh850, left for floating-point support. + + // the following items were copied from original proc, remove them + uint32_t mip; + target_ulong mie; //machine interrupt enable + target_ulong mepc; //machine exception program counter + target_ulong sepc; //supervisor exception program counter + target_ulong mscratch; + target_ulong priv_ver; + target_ulong priv; + + target_ulong frm; // CSR floating point rounding mode + target_ulong mideleg; //machine interrupt delegation register + target_ulong medeleg; + + target_ulong stvec; //supervisor trap vector base + target_ulong scause; //suprevisor cause register + target_ulong mhartid; //hardware thread ID ===> is this same as HTCFG0.PEID ???? rh850 doesnt support multithread? + uint32_t mucounteren; //user counter enable + uint32_t mscounteren; //supervisor counter enable + target_ulong sscratch; + target_ulong mtvec; //machine trap handler base address + target_ulong mcause; //machine trap cause + target_ulong scounteren; + target_ulong mcounteren; + target_ulong sptbr; + target_ulong satp; + target_ulong sbadaddr; + target_ulong mbadaddr; + target_ulong badaddr; //changed to mea + // physical memory protection + pmp_table_t pmp_state; //this should be modified +/* + target_ulong icsr; //interrupt control status register + target_ulong intcfg; //interrupt function setting + + target_ulong fpsr; //floating-point configuration/status <---write the bit defines + target_ulong fpepc; //floating point exception PC + + target_ulong mpm; //memory protection operation mode + + + //target_ulong user_ver; +*/ + /* +#ifndef CONFIG_USER_ONLY + + target_ulong mtval; + +#endif + +*/ + + // Unicorn engine + struct uc_struct *uc; +}; + +#define RH850_CPU(obj) ((RH850CPU *)obj) +#define RH850_CPU_CLASS(klass) ((RH850CPUClass *)klass) +#define RH850_CPU_GET_CLASS(obj) (&((RH850CPU *)obj)->cc) + + +/** + * RH850CPUClass: + * @parent_realize: The parent class' realize handler. + * @parent_reset: The parent class' reset handler. + * + * A RH850 CPU model. + */ +typedef struct RH850CPUClass { + /*< private >*/ + CPUClass parent_class; + /*< public >*/ + void (*parent_reset)(CPUState *cpu); +} RH850CPUClass; + +/** + * RH850CPU: + * @env: #CPURH850State + * + * A RH850 CPU. + */ +typedef struct RH850CPU { + /*< private >*/ + CPUState parent_obj; + /*< public >*/ + CPUNegativeOffsetState neg; + CPURH850State env; + + RH850CPUClass cc; +} RH850CPU; + +typedef RH850CPU ArchCPU; + +static inline RH850CPU *rh850_env_get_cpu(CPURH850State *env) +{ + return container_of(env, RH850CPU, env); +} + +static inline int rh850_has_ext(CPURH850State *env, target_ulong ext) +{ // TODO: what does value 'ext' represent?? + return (env->misa & ext) != 0; + //return false; +} + +static inline bool rh850_feature(CPURH850State *env, int feature) +{ + return env->features & (1ULL << feature); +} + +#include "cpu_user.h" +#include "cpu_bits.h" + +extern const char * const rh850_gp_regnames[]; +extern const char * const rh850_sys_regnames[][MAX_SYS_REGS_IN_BANK]; +extern const char * const rh850_sys_databuff_regnames[]; + +extern const char * const rh850_excp_names[]; +extern const char * const rh850_intr_names[]; +extern const uint32_t rh850_sys_reg_read_only_values[][MAX_SYS_REGS_IN_BANK]; +extern const uint32_t rh850_sys_reg_read_only_masks[][MAX_SYS_REGS_IN_BANK]; + +#define ENV_GET_CPU(e) CPU(rh850_env_get_cpu(e)) +#define ENV_OFFSET offsetof(RH850CPU, env) + +void rh850_cpu_do_interrupt(CPUState *cpu); +int rh850_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int rh850_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); +bool rh850_cpu_exec_interrupt(CPUState *cs, int interrupt_request); +int rh850_cpu_mmu_index(CPURH850State *env, bool ifetch); +hwaddr rh850_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); +void rh850_cpu_do_unaligned_access(CPUState *cs, vaddr addr, + MMUAccessType access_type, int mmu_idx, + uintptr_t retaddr); +int rh850_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int size, + int rw, int mmu_idx); + +char *rh850_isa_string(RH850CPU *cpu); +void rh850_cpu_list(void); + +#define cpu_init(cpu_model) cpu_generic_init(TYPE_RH850_CPU, cpu_model) +#define cpu_signal_handler cpu_rh850_signal_handler +#define cpu_list rh850_cpu_list +#define cpu_mmu_index rh850_cpu_mmu_index + +void rh850_set_mode(CPURH850State *env, target_ulong newpriv); + +void rh850_translate_init(struct uc_struct *uc); +RH850CPU *cpu_rh850_init(struct uc_struct *uc, const char *cpu_model); +int cpu_rh850_signal_handler(int host_signum, void *pinfo, void *puc); +void QEMU_NORETURN do_raise_exception_err(CPURH850State *env, + uint32_t exception, uintptr_t pc); + +target_ulong cpu_rh850_get_fflags(CPURH850State *env); +void cpu_rh850_set_fflags(CPURH850State *env, target_ulong); +void rh850_cpu_set_pc(CPUState *cs, vaddr value); +vaddr rh850_cpu_get_pc(CPUState *cs); +AddressSpace *cpu_addressspace(CPUState *cs, MemTxAttrs attrs); + +#define TB_FLAGS_MMU_MASK 3 +#define TB_FLAGS_FP_ENABLE MSTATUS_FS + +/* + * This f. is called from tcg_gen_lookup_and_goto_ptr() to obtain PC + * which is then used for TB lookup. + */ +static inline void cpu_get_tb_cpu_state(CPURH850State *env, target_ulong *pc, + target_ulong *cs_base, uint32_t *flags) +{ + *pc = env->pc; + *cs_base = 0; +#ifdef CONFIG_USER_ONLY + *flags = TB_FLAGS_FP_ENABLE; +#else + *flags = cpu_mmu_index(env, 0); +#endif +} + +void csr_write_helper(CPURH850State *env, target_ulong val_to_write, + target_ulong csrno); +target_ulong csr_read_helper(CPURH850State *env, target_ulong csrno); + +#ifndef CONFIG_USER_ONLY +void rh850_set_local_interrupt(RH850CPU *cpu, target_ulong mask, int value); +#endif + +extern const int NUM_GDB_REGS; + +#include "exec/cpu-all.h" + +#endif /* RH850_CPU_H */ diff --git a/qemu/target/rh850/cpu_bits.h b/qemu/target/rh850/cpu_bits.h new file mode 100644 index 0000000000..5602864d4f --- /dev/null +++ b/qemu/target/rh850/cpu_bits.h @@ -0,0 +1,418 @@ +/* RH850 PSW constants */ + +#define PSW_Z 0x00000001 +#define PSW_S 0x00000002 +#define PSW_OV 0x00000004 +#define PSW_CY 0x00000008 +#define PSW_SAT 0x00000010 +#define PSW_ID 0x00000020 +#define PSW_EP 0x00000040 +#define PSW_NP 0x00000080 +#define PSW_EBV 0x00008000 +#define PSW_CU0 0x00010000 +#define PSW_UM 0x40000000 + +/* */ + +/* RH850 ISA constants */ + +#define get_field(reg, mask) (((reg) & \ + (target_ulong)(mask)) / ((mask) & ~((mask) << 1))) +#define set_field(reg, mask, val) (((reg) & ~(target_ulong)(mask)) | \ + (((target_ulong)(val) * ((mask) & ~((mask) << 1))) & \ + (target_ulong)(mask))) + +#define PGSHIFT 12 + +#define FSR_RD_SHIFT 5 +#define FSR_RD (0x7 << FSR_RD_SHIFT) + +#define FPEXC_NX 0x01 +#define FPEXC_UF 0x02 +#define FPEXC_OF 0x04 +#define FPEXC_DZ 0x08 +#define FPEXC_NV 0x10 + +#define FSR_AEXC_SHIFT 0 +#define FSR_NVA (FPEXC_NV << FSR_AEXC_SHIFT) +#define FSR_OFA (FPEXC_OF << FSR_AEXC_SHIFT) +#define FSR_UFA (FPEXC_UF << FSR_AEXC_SHIFT) +#define FSR_DZA (FPEXC_DZ << FSR_AEXC_SHIFT) +#define FSR_NXA (FPEXC_NX << FSR_AEXC_SHIFT) +#define FSR_AEXC (FSR_NVA | FSR_OFA | FSR_UFA | FSR_DZA | FSR_NXA) + +/* CSR numbers */ +#define CSR_FFLAGS 0x1 +#define CSR_FRM 0x2 +#define CSR_FCSR 0x3 +#define CSR_CYCLE 0xc00 +#define CSR_TIME 0xc01 +#define CSR_INSTRET 0xc02 +#define CSR_HPMCOUNTER3 0xc03 +#define CSR_HPMCOUNTER4 0xc04 +#define CSR_HPMCOUNTER5 0xc05 +#define CSR_HPMCOUNTER6 0xc06 +#define CSR_HPMCOUNTER7 0xc07 +#define CSR_HPMCOUNTER8 0xc08 +#define CSR_HPMCOUNTER9 0xc09 +#define CSR_HPMCOUNTER10 0xc0a +#define CSR_HPMCOUNTER11 0xc0b +#define CSR_HPMCOUNTER12 0xc0c +#define CSR_HPMCOUNTER13 0xc0d +#define CSR_HPMCOUNTER14 0xc0e +#define CSR_HPMCOUNTER15 0xc0f +#define CSR_HPMCOUNTER16 0xc10 +#define CSR_HPMCOUNTER17 0xc11 +#define CSR_HPMCOUNTER18 0xc12 +#define CSR_HPMCOUNTER19 0xc13 +#define CSR_HPMCOUNTER20 0xc14 +#define CSR_HPMCOUNTER21 0xc15 +#define CSR_HPMCOUNTER22 0xc16 +#define CSR_HPMCOUNTER23 0xc17 +#define CSR_HPMCOUNTER24 0xc18 +#define CSR_HPMCOUNTER25 0xc19 +#define CSR_HPMCOUNTER26 0xc1a +#define CSR_HPMCOUNTER27 0xc1b +#define CSR_HPMCOUNTER28 0xc1c +#define CSR_HPMCOUNTER29 0xc1d +#define CSR_HPMCOUNTER30 0xc1e +#define CSR_HPMCOUNTER31 0xc1f +#define CSR_SSTATUS 0x100 +#define CSR_SIE 0x104 +#define CSR_STVEC 0x105 +#define CSR_SCOUNTEREN 0x106 +#define CSR_SSCRATCH 0x140 +#define CSR_SEPC 0x141 +#define CSR_SCAUSE 0x142 +#define CSR_SBADADDR 0x143 +#define CSR_SIP 0x144 +#define CSR_SPTBR 0x180 +#define CSR_SATP 0x180 +#define CSR_MSTATUS 0x300 +#define CSR_MISA 0x301 +#define CSR_MEDELEG 0x302 +#define CSR_MIDELEG 0x303 +#define CSR_MIE 0x304 +#define CSR_MTVEC 0x305 +#define CSR_MCOUNTEREN 0x306 +#define CSR_MSCRATCH 0x340 +#define CSR_MEPC 0x341 +#define CSR_MCAUSE 0x342 +#define CSR_MBADADDR 0x343 +#define CSR_MIP 0x344 +#define CSR_PMPCFG0 0x3a0 +#define CSR_PMPCFG1 0x3a1 +#define CSR_PMPCFG2 0x3a2 +#define CSR_PMPCFG3 0x3a3 +#define CSR_PMPADDR0 0x3b0 +#define CSR_PMPADDR1 0x3b1 +#define CSR_PMPADDR2 0x3b2 +#define CSR_PMPADDR3 0x3b3 +#define CSR_PMPADDR4 0x3b4 +#define CSR_PMPADDR5 0x3b5 +#define CSR_PMPADDR6 0x3b6 +#define CSR_PMPADDR7 0x3b7 +#define CSR_PMPADDR8 0x3b8 +#define CSR_PMPADDR9 0x3b9 +#define CSR_PMPADDR10 0x3ba +#define CSR_PMPADDR11 0x3bb +#define CSR_PMPADDR12 0x3bc +#define CSR_PMPADDR13 0x3bd +#define CSR_PMPADDR14 0x3be +#define CSR_PMPADDR15 0x3bf +#define CSR_TSELECT 0x7a0 +#define CSR_TDATA1 0x7a1 +#define CSR_TDATA2 0x7a2 +#define CSR_TDATA3 0x7a3 +#define CSR_DCSR 0x7b0 +#define CSR_DPC 0x7b1 +#define CSR_DSCRATCH 0x7b2 +#define CSR_MCYCLE 0xb00 +#define CSR_MINSTRET 0xb02 +#define CSR_MHPMCOUNTER3 0xb03 +#define CSR_MHPMCOUNTER4 0xb04 +#define CSR_MHPMCOUNTER5 0xb05 +#define CSR_MHPMCOUNTER6 0xb06 +#define CSR_MHPMCOUNTER7 0xb07 +#define CSR_MHPMCOUNTER8 0xb08 +#define CSR_MHPMCOUNTER9 0xb09 +#define CSR_MHPMCOUNTER10 0xb0a +#define CSR_MHPMCOUNTER11 0xb0b +#define CSR_MHPMCOUNTER12 0xb0c +#define CSR_MHPMCOUNTER13 0xb0d +#define CSR_MHPMCOUNTER14 0xb0e +#define CSR_MHPMCOUNTER15 0xb0f +#define CSR_MHPMCOUNTER16 0xb10 +#define CSR_MHPMCOUNTER17 0xb11 +#define CSR_MHPMCOUNTER18 0xb12 +#define CSR_MHPMCOUNTER19 0xb13 +#define CSR_MHPMCOUNTER20 0xb14 +#define CSR_MHPMCOUNTER21 0xb15 +#define CSR_MHPMCOUNTER22 0xb16 +#define CSR_MHPMCOUNTER23 0xb17 +#define CSR_MHPMCOUNTER24 0xb18 +#define CSR_MHPMCOUNTER25 0xb19 +#define CSR_MHPMCOUNTER26 0xb1a +#define CSR_MHPMCOUNTER27 0xb1b +#define CSR_MHPMCOUNTER28 0xb1c +#define CSR_MHPMCOUNTER29 0xb1d +#define CSR_MHPMCOUNTER30 0xb1e +#define CSR_MHPMCOUNTER31 0xb1f +#define CSR_MUCOUNTEREN 0x320 +#define CSR_MSCOUNTEREN 0x321 +#define CSR_MHPMEVENT3 0x323 +#define CSR_MHPMEVENT4 0x324 +#define CSR_MHPMEVENT5 0x325 +#define CSR_MHPMEVENT6 0x326 +#define CSR_MHPMEVENT7 0x327 +#define CSR_MHPMEVENT8 0x328 +#define CSR_MHPMEVENT9 0x329 +#define CSR_MHPMEVENT10 0x32a +#define CSR_MHPMEVENT11 0x32b +#define CSR_MHPMEVENT12 0x32c +#define CSR_MHPMEVENT13 0x32d +#define CSR_MHPMEVENT14 0x32e +#define CSR_MHPMEVENT15 0x32f +#define CSR_MHPMEVENT16 0x330 +#define CSR_MHPMEVENT17 0x331 +#define CSR_MHPMEVENT18 0x332 +#define CSR_MHPMEVENT19 0x333 +#define CSR_MHPMEVENT20 0x334 +#define CSR_MHPMEVENT21 0x335 +#define CSR_MHPMEVENT22 0x336 +#define CSR_MHPMEVENT23 0x337 +#define CSR_MHPMEVENT24 0x338 +#define CSR_MHPMEVENT25 0x339 +#define CSR_MHPMEVENT26 0x33a +#define CSR_MHPMEVENT27 0x33b +#define CSR_MHPMEVENT28 0x33c +#define CSR_MHPMEVENT29 0x33d +#define CSR_MHPMEVENT30 0x33e +#define CSR_MHPMEVENT31 0x33f +#define CSR_MVENDORID 0xf11 +#define CSR_MARCHID 0xf12 +#define CSR_MIMPID 0xf13 +#define CSR_MHARTID 0xf14 +#define CSR_CYCLEH 0xc80 +#define CSR_TIMEH 0xc81 +#define CSR_INSTRETH 0xc82 +#define CSR_HPMCOUNTER3H 0xc83 +#define CSR_HPMCOUNTER4H 0xc84 +#define CSR_HPMCOUNTER5H 0xc85 +#define CSR_HPMCOUNTER6H 0xc86 +#define CSR_HPMCOUNTER7H 0xc87 +#define CSR_HPMCOUNTER8H 0xc88 +#define CSR_HPMCOUNTER9H 0xc89 +#define CSR_HPMCOUNTER10H 0xc8a +#define CSR_HPMCOUNTER11H 0xc8b +#define CSR_HPMCOUNTER12H 0xc8c +#define CSR_HPMCOUNTER13H 0xc8d +#define CSR_HPMCOUNTER14H 0xc8e +#define CSR_HPMCOUNTER15H 0xc8f +#define CSR_HPMCOUNTER16H 0xc90 +#define CSR_HPMCOUNTER17H 0xc91 +#define CSR_HPMCOUNTER18H 0xc92 +#define CSR_HPMCOUNTER19H 0xc93 +#define CSR_HPMCOUNTER20H 0xc94 +#define CSR_HPMCOUNTER21H 0xc95 +#define CSR_HPMCOUNTER22H 0xc96 +#define CSR_HPMCOUNTER23H 0xc97 +#define CSR_HPMCOUNTER24H 0xc98 +#define CSR_HPMCOUNTER25H 0xc99 +#define CSR_HPMCOUNTER26H 0xc9a +#define CSR_HPMCOUNTER27H 0xc9b +#define CSR_HPMCOUNTER28H 0xc9c +#define CSR_HPMCOUNTER29H 0xc9d +#define CSR_HPMCOUNTER30H 0xc9e +#define CSR_HPMCOUNTER31H 0xc9f +#define CSR_MCYCLEH 0xb80 +#define CSR_MINSTRETH 0xb82 +#define CSR_MHPMCOUNTER3H 0xb83 +#define CSR_MHPMCOUNTER4H 0xb84 +#define CSR_MHPMCOUNTER5H 0xb85 +#define CSR_MHPMCOUNTER6H 0xb86 +#define CSR_MHPMCOUNTER7H 0xb87 +#define CSR_MHPMCOUNTER8H 0xb88 +#define CSR_MHPMCOUNTER9H 0xb89 +#define CSR_MHPMCOUNTER10H 0xb8a +#define CSR_MHPMCOUNTER11H 0xb8b +#define CSR_MHPMCOUNTER12H 0xb8c +#define CSR_MHPMCOUNTER13H 0xb8d +#define CSR_MHPMCOUNTER14H 0xb8e +#define CSR_MHPMCOUNTER15H 0xb8f +#define CSR_MHPMCOUNTER16H 0xb90 +#define CSR_MHPMCOUNTER17H 0xb91 +#define CSR_MHPMCOUNTER18H 0xb92 +#define CSR_MHPMCOUNTER19H 0xb93 +#define CSR_MHPMCOUNTER20H 0xb94 +#define CSR_MHPMCOUNTER21H 0xb95 +#define CSR_MHPMCOUNTER22H 0xb96 +#define CSR_MHPMCOUNTER23H 0xb97 +#define CSR_MHPMCOUNTER24H 0xb98 +#define CSR_MHPMCOUNTER25H 0xb99 +#define CSR_MHPMCOUNTER26H 0xb9a +#define CSR_MHPMCOUNTER27H 0xb9b +#define CSR_MHPMCOUNTER28H 0xb9c +#define CSR_MHPMCOUNTER29H 0xb9d +#define CSR_MHPMCOUNTER30H 0xb9e +#define CSR_MHPMCOUNTER31H 0xb9f + +/* mstatus bits */ +#define MSTATUS_UIE 0x00000001 +#define MSTATUS_SIE 0x00000002 +#define MSTATUS_HIE 0x00000004 +#define MSTATUS_MIE 0x00000008 +#define MSTATUS_UPIE 0x00000010 +#define MSTATUS_SPIE 0x00000020 +#define MSTATUS_HPIE 0x00000040 +#define MSTATUS_MPIE 0x00000080 +#define MSTATUS_SPP 0x00000100 +#define MSTATUS_HPP 0x00000600 +#define MSTATUS_MPP 0x00001800 +#define MSTATUS_FS 0x00006000 +#define MSTATUS_XS 0x00018000 +#define MSTATUS_MPRV 0x00020000 +#define MSTATUS_PUM 0x00040000 /* until: priv-1.9.1 */ +#define MSTATUS_SUM 0x00040000 /* since: priv-1.10 */ +#define MSTATUS_MXR 0x00080000 +#define MSTATUS_VM 0x1F000000 /* until: priv-1.9.1 */ +#define MSTATUS_TVM 0x00100000 /* since: priv-1.10 */ +#define MSTATUS_TW 0x20000000 /* since: priv-1.10 */ +#define MSTATUS_TSR 0x40000000 /* since: priv-1.10 */ + +#define MSTATUS64_UXL 0x0000000300000000ULL +#define MSTATUS64_SXL 0x0000000C00000000ULL + +#define MSTATUS32_SD 0x80000000 +#define MSTATUS64_SD 0x8000000000000000ULL + +#if defined(TARGET_RH850) +#define MSTATUS_SD MSTATUS32_SD +#endif + +/* sstatus bits */ +#define SSTATUS_UIE 0x00000001 +#define SSTATUS_SIE 0x00000002 +#define SSTATUS_UPIE 0x00000010 +#define SSTATUS_SPIE 0x00000020 +#define SSTATUS_SPP 0x00000100 +#define SSTATUS_FS 0x00006000 +#define SSTATUS_XS 0x00018000 +#define SSTATUS_PUM 0x00040000 /* until: priv-1.9.1 */ +#define SSTATUS_SUM 0x00040000 /* since: priv-1.10 */ +#define SSTATUS_MXR 0x00080000 + +#define SSTATUS32_SD 0x80000000 +#define SSTATUS64_SD 0x8000000000000000ULL + +#if defined(TARGET_RH850) +#define SSTATUS_SD SSTATUS32_SD +#endif + +/* irqs */ +#define MIP_SSIP (1 << IRQ_S_SOFT) +#define MIP_HSIP (1 << IRQ_H_SOFT) +#define MIP_MSIP (1 << IRQ_M_SOFT) +#define MIP_STIP (1 << IRQ_S_TIMER) +#define MIP_HTIP (1 << IRQ_H_TIMER) +#define MIP_MTIP (1 << IRQ_M_TIMER) +#define MIP_SEIP (1 << IRQ_S_EXT) +#define MIP_HEIP (1 << IRQ_H_EXT) +#define MIP_MEIP (1 << IRQ_M_EXT) + +#define SIP_SSIP MIP_SSIP +#define SIP_STIP MIP_STIP +#define SIP_SEIP MIP_SEIP + +#define PRV_U 0 +#define PRV_S 1 +#define PRV_H 2 +#define PRV_M 3 + +/* privileged ISA 1.9.1 VM modes (mstatus.vm) */ +#define VM_1_09_MBARE 0 +#define VM_1_09_MBB 1 +#define VM_1_09_MBBID 2 +#define VM_1_09_SV32 8 +#define VM_1_09_SV39 9 +#define VM_1_09_SV48 10 + +/* privileged ISA 1.10.0 VM modes (satp.mode) */ +#define VM_1_10_MBARE 0 +#define VM_1_10_SV32 1 +#define VM_1_10_SV39 8 +#define VM_1_10_SV48 9 +#define VM_1_10_SV57 10 +#define VM_1_10_SV64 11 + +/* privileged ISA interrupt causes */ +#define IRQ_U_SOFT 0 /* since: priv-1.10 */ +#define IRQ_S_SOFT 1 +#define IRQ_H_SOFT 2 /* until: priv-1.9.1 */ +#define IRQ_M_SOFT 3 /* until: priv-1.9.1 */ +#define IRQ_U_TIMER 4 /* since: priv-1.10 */ +#define IRQ_S_TIMER 5 +#define IRQ_H_TIMER 6 /* until: priv-1.9.1 */ +#define IRQ_M_TIMER 7 /* until: priv-1.9.1 */ +#define IRQ_U_EXT 8 /* since: priv-1.10 */ +#define IRQ_S_EXT 9 +#define IRQ_H_EXT 10 /* until: priv-1.9.1 */ +#define IRQ_M_EXT 11 /* until: priv-1.9.1 */ +#define IRQ_X_COP 12 /* non-standard */ + +/* Default addresses */ +#define DEFAULT_RSTVEC 0x00000000 + +/* RV32 satp field masks */ +#define SATP32_MODE 0x80000000 +#define SATP32_ASID 0x7fc00000 +#define SATP32_PPN 0x003fffff + +/* RV64 satp field masks */ +#define SATP64_MODE 0xF000000000000000ULL +#define SATP64_ASID 0x0FFFF00000000000ULL +#define SATP64_PPN 0x00000FFFFFFFFFFFULL + +#if defined(TARGET_RH850) +#define SATP_MODE SATP32_MODE +#define SATP_ASID SATP32_ASID +#define SATP_PPN SATP32_PPN +#endif + +/* RH850 Exception Codes */ +#define EXCP_NONE -1 /* not a real RH850 exception code */ +#define RH850_EXCP_INST_ADDR_MIS 0x0 +#define RH850_EXCP_INST_ACCESS_FAULT 0x1 +#define RH850_EXCP_ILLEGAL_INST 0x2 +#define RH850_EXCP_BREAKPOINT 0x3 +#define RH850_EXCP_LOAD_ADDR_MIS 0x4 +#define RH850_EXCP_LOAD_ACCESS_FAULT 0x5 +#define RH850_EXCP_STORE_AMO_ADDR_MIS 0x6 +#define RH850_EXCP_STORE_AMO_ACCESS_FAULT 0x7 +#define RH850_EXCP_U_ECALL 0x8 /* for convenience, report all + ECALLs as this, handler + fixes */ +#define RH850_EXCP_S_ECALL 0x9 +#define RH850_EXCP_H_ECALL 0xa +#define RH850_EXCP_M_ECALL 0xb +#define RH850_EXCP_INST_PAGE_FAULT 0xc /* since: priv-1.10.0 */ +#define RH850_EXCP_LOAD_PAGE_FAULT 0xd /* since: priv-1.10.0 */ +#define RH850_EXCP_STORE_PAGE_FAULT 0xf /* since: priv-1.10.0 */ + +#define RH850_EXCP_INT_FLAG 0x80000000 +#define RH850_EXCP_INT_MASK 0x7fffffff + +/* page table entry (PTE) fields */ +#define PTE_V 0x001 /* Valid */ +#define PTE_R 0x002 /* Read */ +#define PTE_W 0x004 /* Write */ +#define PTE_X 0x008 /* Execute */ +#define PTE_U 0x010 /* User */ +#define PTE_G 0x020 /* Global */ +#define PTE_A 0x040 /* Accessed */ +#define PTE_D 0x080 /* Dirty */ +#define PTE_SOFT 0x300 /* Reserved for Software */ + +#define PTE_PPN_SHIFT 10 + +#define PTE_TABLE(PTE) (((PTE) & (PTE_V | PTE_R | PTE_W | PTE_X)) == PTE_V) diff --git a/qemu/target/rh850/cpu_user.h b/qemu/target/rh850/cpu_user.h new file mode 100644 index 0000000000..c2199610ab --- /dev/null +++ b/qemu/target/rh850/cpu_user.h @@ -0,0 +1,13 @@ +#define xRA 1 /* return address (aka link register) */ +#define xSP 2 /* stack pointer */ +#define xGP 3 /* global pointer */ +#define xTP 4 /* thread pointer */ + +#define xA0 10 /* gpr[10-17] are syscall arguments */ +#define xA1 11 +#define xA2 12 +#define xA3 13 +#define xA4 14 +#define xA5 15 +#define xA6 16 +#define xA7 17 /* syscall number goes here */ diff --git a/qemu/target/rh850/fpu_helper.c b/qemu/target/rh850/fpu_helper.c new file mode 100644 index 0000000000..0ccfdad34f --- /dev/null +++ b/qemu/target/rh850/fpu_helper.c @@ -0,0 +1,371 @@ +/* + * RH850 FPU Emulation Helpers for QEMU. + * + * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "qemu/osdep.h" +#include +#include "cpu.h" +#include "qemu/host-utils.h" +#include "exec/exec-all.h" +#include "exec/helper-proto.h" + +target_ulong cpu_rh850_get_fflags(CPURH850State *env) +{ + int soft = get_float_exception_flags(&env->fp_status); + target_ulong hard = 0; + + hard |= (soft & float_flag_inexact) ? FPEXC_NX : 0; + hard |= (soft & float_flag_underflow) ? FPEXC_UF : 0; + hard |= (soft & float_flag_overflow) ? FPEXC_OF : 0; + hard |= (soft & float_flag_divbyzero) ? FPEXC_DZ : 0; + hard |= (soft & float_flag_invalid) ? FPEXC_NV : 0; + + return hard; +} + +void cpu_rh850_set_fflags(CPURH850State *env, target_ulong hard) +{ + int soft = 0; + + soft |= (hard & FPEXC_NX) ? float_flag_inexact : 0; + soft |= (hard & FPEXC_UF) ? float_flag_underflow : 0; + soft |= (hard & FPEXC_OF) ? float_flag_overflow : 0; + soft |= (hard & FPEXC_DZ) ? float_flag_divbyzero : 0; + soft |= (hard & FPEXC_NV) ? float_flag_invalid : 0; + + set_float_exception_flags(soft, &env->fp_status); +} + +void helper_set_rounding_mode(CPURH850State *env, uint32_t rm) +{ + int softrm; + + if (rm == 7) { + rm = 0; //env->frm; + } + switch (rm) { + case 0: + softrm = float_round_nearest_even; + break; + case 1: + softrm = float_round_to_zero; + break; + case 2: + softrm = float_round_down; + break; + case 3: + softrm = float_round_up; + break; + case 4: + softrm = float_round_ties_away; + break; + default: + do_raise_exception_err(env, RH850_EXCP_ILLEGAL_INST, GETPC()); + } + + set_float_rounding_mode(softrm, &env->fp_status); +} + +uint64_t helper_fmadd_s(CPURH850State *env, uint64_t frs1, uint64_t frs2, + uint64_t frs3) +{ + return float32_muladd(frs1, frs2, frs3, 0, &env->fp_status); +} + +uint64_t helper_fmadd_d(CPURH850State *env, uint64_t frs1, uint64_t frs2, + uint64_t frs3) +{ + return float64_muladd(frs1, frs2, frs3, 0, &env->fp_status); +} + +uint64_t helper_fmsub_s(CPURH850State *env, uint64_t frs1, uint64_t frs2, + uint64_t frs3) +{ + return float32_muladd(frs1, frs2, frs3, float_muladd_negate_c, + &env->fp_status); +} + +uint64_t helper_fmsub_d(CPURH850State *env, uint64_t frs1, uint64_t frs2, + uint64_t frs3) +{ + return float64_muladd(frs1, frs2, frs3, float_muladd_negate_c, + &env->fp_status); +} + +uint64_t helper_fnmsub_s(CPURH850State *env, uint64_t frs1, uint64_t frs2, + uint64_t frs3) +{ + return float32_muladd(frs1, frs2, frs3, float_muladd_negate_product, + &env->fp_status); +} + +uint64_t helper_fnmsub_d(CPURH850State *env, uint64_t frs1, uint64_t frs2, + uint64_t frs3) +{ + return float64_muladd(frs1, frs2, frs3, float_muladd_negate_product, + &env->fp_status); +} + +uint64_t helper_fnmadd_s(CPURH850State *env, uint64_t frs1, uint64_t frs2, + uint64_t frs3) +{ + return float32_muladd(frs1, frs2, frs3, float_muladd_negate_c | + float_muladd_negate_product, &env->fp_status); +} + +uint64_t helper_fnmadd_d(CPURH850State *env, uint64_t frs1, uint64_t frs2, + uint64_t frs3) +{ + return float64_muladd(frs1, frs2, frs3, float_muladd_negate_c | + float_muladd_negate_product, &env->fp_status); +} + +uint64_t helper_fadd_s(CPURH850State *env, uint64_t frs1, uint64_t frs2) +{ + return float32_add(frs1, frs2, &env->fp_status); +} + +uint64_t helper_fsub_s(CPURH850State *env, uint64_t frs1, uint64_t frs2) +{ + return float32_sub(frs1, frs2, &env->fp_status); +} + +uint64_t helper_fmul_s(CPURH850State *env, uint64_t frs1, uint64_t frs2) +{ + return float32_mul(frs1, frs2, &env->fp_status); +} + +uint64_t helper_fdiv_s(CPURH850State *env, uint64_t frs1, uint64_t frs2) +{ + return float32_div(frs1, frs2, &env->fp_status); +} + +uint64_t helper_fmin_s(CPURH850State *env, uint64_t frs1, uint64_t frs2) +{ + return float32_minnum(frs1, frs2, &env->fp_status); +} + +uint64_t helper_fmax_s(CPURH850State *env, uint64_t frs1, uint64_t frs2) +{ + return float32_maxnum(frs1, frs2, &env->fp_status); +} + +uint64_t helper_fsqrt_s(CPURH850State *env, uint64_t frs1) +{ + return float32_sqrt(frs1, &env->fp_status); +} + +target_ulong helper_fle_s(CPURH850State *env, uint64_t frs1, uint64_t frs2) +{ + return float32_le(frs1, frs2, &env->fp_status); +} + +target_ulong helper_flt_s(CPURH850State *env, uint64_t frs1, uint64_t frs2) +{ + return float32_lt(frs1, frs2, &env->fp_status); +} + +target_ulong helper_feq_s(CPURH850State *env, uint64_t frs1, uint64_t frs2) +{ + return float32_eq_quiet(frs1, frs2, &env->fp_status); +} + +target_ulong helper_fcvt_w_s(CPURH850State *env, uint64_t frs1) +{ + return float32_to_int32(frs1, &env->fp_status); +} + +target_ulong helper_fcvt_wu_s(CPURH850State *env, uint64_t frs1) +{ + return (int32_t)float32_to_uint32(frs1, &env->fp_status); +} + +#if defined(TARGET_RH85064) +uint64_t helper_fcvt_l_s(CPURH850State *env, uint64_t frs1) +{ + return float32_to_int64(frs1, &env->fp_status); +} + +uint64_t helper_fcvt_lu_s(CPURH850State *env, uint64_t frs1) +{ + return float32_to_uint64(frs1, &env->fp_status); +} +#endif + +uint64_t helper_fcvt_s_w(CPURH850State *env, target_ulong rs1) +{ + return int32_to_float32((int32_t)rs1, &env->fp_status); +} + +uint64_t helper_fcvt_s_wu(CPURH850State *env, target_ulong rs1) +{ + return uint32_to_float32((uint32_t)rs1, &env->fp_status); +} + +#if defined(TARGET_RH85064) +uint64_t helper_fcvt_s_l(CPURH850State *env, uint64_t rs1) +{ + return int64_to_float32(rs1, &env->fp_status); +} + +uint64_t helper_fcvt_s_lu(CPURH850State *env, uint64_t rs1) +{ + return uint64_to_float32(rs1, &env->fp_status); +} +#endif + +target_ulong helper_fclass_s(uint64_t frs1) +{ + float32 f = frs1; + bool sign = float32_is_neg(f); + + if (float32_is_infinity(f)) { + return sign ? 1 << 0 : 1 << 7; + } else if (float32_is_zero(f)) { + return sign ? 1 << 3 : 1 << 4; + } else if (float32_is_zero_or_denormal(f)) { + return sign ? 1 << 2 : 1 << 5; + } else if (float32_is_any_nan(f)) { + float_status s = { }; /* for snan_bit_is_one */ + return float32_is_quiet_nan(f, &s) ? 1 << 9 : 1 << 8; + } else { + return sign ? 1 << 1 : 1 << 6; + } +} + +uint64_t helper_fadd_d(CPURH850State *env, uint64_t frs1, uint64_t frs2) +{ + return float64_add(frs1, frs2, &env->fp_status); +} + +uint64_t helper_fsub_d(CPURH850State *env, uint64_t frs1, uint64_t frs2) +{ + return float64_sub(frs1, frs2, &env->fp_status); +} + +uint64_t helper_fmul_d(CPURH850State *env, uint64_t frs1, uint64_t frs2) +{ + return float64_mul(frs1, frs2, &env->fp_status); +} + +uint64_t helper_fdiv_d(CPURH850State *env, uint64_t frs1, uint64_t frs2) +{ + return float64_div(frs1, frs2, &env->fp_status); +} + +uint64_t helper_fmin_d(CPURH850State *env, uint64_t frs1, uint64_t frs2) +{ + return float64_minnum(frs1, frs2, &env->fp_status); +} + +uint64_t helper_fmax_d(CPURH850State *env, uint64_t frs1, uint64_t frs2) +{ + return float64_maxnum(frs1, frs2, &env->fp_status); +} + +uint64_t helper_fcvt_s_d(CPURH850State *env, uint64_t rs1) +{ + return float64_to_float32(rs1, &env->fp_status); +} + +uint64_t helper_fcvt_d_s(CPURH850State *env, uint64_t rs1) +{ + return float32_to_float64(rs1, &env->fp_status); +} + +uint64_t helper_fsqrt_d(CPURH850State *env, uint64_t frs1) +{ + return float64_sqrt(frs1, &env->fp_status); +} + +target_ulong helper_fle_d(CPURH850State *env, uint64_t frs1, uint64_t frs2) +{ + return float64_le(frs1, frs2, &env->fp_status); +} + +target_ulong helper_flt_d(CPURH850State *env, uint64_t frs1, uint64_t frs2) +{ + return float64_lt(frs1, frs2, &env->fp_status); +} + +target_ulong helper_feq_d(CPURH850State *env, uint64_t frs1, uint64_t frs2) +{ + return float64_eq_quiet(frs1, frs2, &env->fp_status); +} + +target_ulong helper_fcvt_w_d(CPURH850State *env, uint64_t frs1) +{ + return float64_to_int32(frs1, &env->fp_status); +} + +target_ulong helper_fcvt_wu_d(CPURH850State *env, uint64_t frs1) +{ + return (int32_t)float64_to_uint32(frs1, &env->fp_status); +} + +#if defined(TARGET_RH85064) +uint64_t helper_fcvt_l_d(CPURH850State *env, uint64_t frs1) +{ + return float64_to_int64(frs1, &env->fp_status); +} + +uint64_t helper_fcvt_lu_d(CPURH850State *env, uint64_t frs1) +{ + return float64_to_uint64(frs1, &env->fp_status); +} +#endif + +uint64_t helper_fcvt_d_w(CPURH850State *env, target_ulong rs1) +{ + return int32_to_float64((int32_t)rs1, &env->fp_status); +} + +uint64_t helper_fcvt_d_wu(CPURH850State *env, target_ulong rs1) +{ + return uint32_to_float64((uint32_t)rs1, &env->fp_status); +} + +#if defined(TARGET_RH85064) +uint64_t helper_fcvt_d_l(CPURH850State *env, uint64_t rs1) +{ + return int64_to_float64(rs1, &env->fp_status); +} + +uint64_t helper_fcvt_d_lu(CPURH850State *env, uint64_t rs1) +{ + return uint64_to_float64(rs1, &env->fp_status); +} +#endif + +target_ulong helper_fclass_d(uint64_t frs1) +{ + float64 f = frs1; + bool sign = float64_is_neg(f); + + if (float64_is_infinity(f)) { + return sign ? 1 << 0 : 1 << 7; + } else if (float64_is_zero(f)) { + return sign ? 1 << 3 : 1 << 4; + } else if (float64_is_zero_or_denormal(f)) { + return sign ? 1 << 2 : 1 << 5; + } else if (float64_is_any_nan(f)) { + float_status s = { }; /* for snan_bit_is_one */ + return float64_is_quiet_nan(f, &s) ? 1 << 9 : 1 << 8; + } else { + return sign ? 1 << 1 : 1 << 6; + } +} diff --git a/qemu/target/rh850/gdbstub.c b/qemu/target/rh850/gdbstub.c new file mode 100644 index 0000000000..216b53b30f --- /dev/null +++ b/qemu/target/rh850/gdbstub.c @@ -0,0 +1,179 @@ +/* + * RH850 GDB Server Stub + * + * Copyright (c) 2019-2020 Marko Klopcic, iSYSTEM Labs + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "exec/gdbstub.h" +#include "cpu.h" + +/* Mapping of winIDEA register index to env->sysBasicRegs() index. (see mail + * from Matic 2019-05-06 and isystem/doc/v850-tdep.c) + QEMU idx wI idx + 32, // eipc 0 + 33, // eipsw 1 + 34, // fepc 2 + 35, // fepsw 3 + 37, // psw 4 + 128, // fpsr 5 + 129, // fpepc 6 + 130, // fpst 7 + 131, // fpcc 8 + 132, // fpcfg 9 + 133, // fpec 10 + 44, // SESR N/A + 45, // EIIC 11 + 46, // FEIC 12 + 48, // CTPC 13 + 49, // CTPSW 14 + 52, // CTBP 15 + 60, // EIWR 16 + 61, // FEWR 17 + 63, // BSEL 18 + 150, // mcfg0 19 + 152, // RBASE 20 + 153, // EBASE 21 + 154, // intbp 22 + 155, // mctl 23 + 156, // pid 24 + 161, // sccfg 25 + 162, // scbp 26 + 182, // htcfg0 27 + 188, // mea 28 + 189, // asid 29 + 190 // mei 30 +*/ +#define BANK_MASK 0xf0000 +#define BANK_SHIFT 16 +#define SRI(selID, regID) (((selID) << BANK_SHIFT) | (regID)) +#define SRI0(regID) (regID) +#define SRI1(regID) SRI(1, (regID)) +#define SRI2(regID) SRI(2, (regID)) + +typedef int IdxType; +const IdxType winIdeaRegIdx2qemuSysRegIdx[] = { +// 0 1 2 3 4 5 6 7 8 9 +// --------------------------------------------- +-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0 +-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 1 +-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 2 + +-1, -1, SRI0(EIPC_IDX), SRI0(EIPSW_IDX),SRI0(FEPC_IDX),SRI0(FEPSW_IDX),-1, SRI0(PSW_IDX), -1, -1, // 3 +-1, -1, -1, -1, -1, SRI0(EIIC_IDX),SRI0(FEIC_IDX),-1,SRI0(CTPC_IDX),SRI0(CTPSW_IDX), // 4 +-1, -1, SRI0(CTBP_IDX), -1, -1, -1, -1, -1, -1, -1, // 5 + +SRI0(EIWR_IDX),SRI0(FEWR_IDX),-1,SRI0(BSEL_IDX), -1, -1, -1, -1, -1, -1, // 6 +-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 7 +-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 8 + +-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 9 +-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 10 +-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 11 + +-1, -1, -1, -1, -1, -1, -1, -1, SRI0(FPSR_IDX), SRI0(FEPC_IDX), // 12 +SRI0(FPST_IDX),SRI0(FPCC_IDX),SRI0(FPCFG_IDX),SRI0(FPEC_IDX), -1,-1, -1, -1, -1, -1, // 13 +-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 14 + +SRI1(MCFG0_IDX1),-1,SRI1(RBASE_IDX1),SRI1(EBASE_IDX1),SRI1(INTBP_IDX1),SRI1(MCTL_IDX1),SRI1(PID_IDX1),-1,-1, -1, // 15 +-1, SRI1(SCCFG_IDX1), SRI1(SCBP_IDX1), -1, -1, -1, -1, -1, -1, -1, // 16 +-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 17 + +-1, -1,SRI2(HTCFG0_IDX2), -1, -1, -1, -1, -1,SRI2(MEA_IDX2),SRI2(ASID_IDX2), // 18 +SRI2(MEI_IDX2), -1, -1, -1, -1, -1, -1, -1, -1, -1, // 19 +}; + +const int NUM_GDB_REGS = sizeof(winIdeaRegIdx2qemuSysRegIdx) / sizeof(IdxType); + +int rh850_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + RH850CPU *cpu = RH850_CPU(cs); + CPURH850State *env = &cpu->env; + + if (n < 32) { + return gdb_get_regl(mem_buf, env->gpRegs[n]); //gpr is now supposed to be progRegs + } else if (n == 64) { + return gdb_get_regl(mem_buf, env->pc); + } else if (n < NUM_GDB_REGS) { + int sysRegIdx = winIdeaRegIdx2qemuSysRegIdx[n]; + if (sysRegIdx >= 0) { + int selID = sysRegIdx >> BANK_SHIFT; + int regID = sysRegIdx & ~BANK_MASK; + if (selID == BANK_ID_BASIC_0 && regID == PSW_IDX) { + int psw = env->Z_flag | (env->S_flag << 1) | (env->OV_flag << 2) | (env->CY_flag << 3); + psw |= (env->SAT_flag << 4) | (env->ID_flag << 5) | (env->EP_flag << 6); + psw |= (env->NP_flag << 7) | (env->EBV_flag << 15) | (env->CU0_flag << 16); + psw |= (env->CU1_flag << 17) | (env->CU2_flag << 18) | (env->UM_flag << 30); + return gdb_get_regl(mem_buf, psw); + } else { + return gdb_get_regl(mem_buf, env->systemRegs[selID][regID]); // eipc, eipsw, fepc, fepsw, psw, ... + } + } + } + + *((uint32_t *)mem_buf) = 0xBAD0BAD0; + return 4; // registers in slots not set above are ignored +} + +int rh850_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + RH850CPU *cpu = RH850_CPU(cs); + CPURH850State *env = &cpu->env; + // at the moment our GDB server has different indices for writing single register + // will fix this if batch write will have to be supported or interfacing + // to other GDB servers for RH850 will be needed. + if (n > 0 && n < 32) { // skip R0, because it is always 0 + env->gpRegs[n] = ldtul_p(mem_buf); + } else if (n == 64) { + env->pc = ldtul_p(mem_buf); + } else if (n < NUM_GDB_REGS) { + int sysRegIdx = winIdeaRegIdx2qemuSysRegIdx[n]; + if (sysRegIdx >= 0) { + int selID = sysRegIdx >> BANK_SHIFT; + int regID = sysRegIdx & ~BANK_MASK; + if (selID == BANK_ID_BASIC_0 && regID == PSW_IDX) { + int psw = ldtul_p(mem_buf); + env->Z_flag = psw & 1; + env->S_flag = (psw >> 1) & 1; + env->OV_flag = (psw >> 2) & 1; + env->CY_flag = (psw >> 3) & 1; + env->SAT_flag = (psw >> 4) & 1; + env->ID_flag = (psw >> 5) & 1; + env->EP_flag = (psw >> 6) & 1; + env->NP_flag = (psw >> 7) & 1; + env->EBV_flag = (psw >> 15) & 1; + env->CU0_flag = (psw >> 16) & 1; + env->CU1_flag = (psw >> 17) & 1; + env->CU2_flag = (psw >> 18) & 1; + env->UM_flag = (psw >> 30) & 1; + } else { + env->systemRegs[selID][regID] = ldtul_p(mem_buf); // eipc, eipsw, fepc, fepsw, psw, ... + } + } + } + + /* arm example + if (n == 0) { + ... + } else if (n < 65) { + env->fpr[n - 33] = ldq_p(mem_buf); // always 64-bit + return sizeof(uint64_t); + } else if (n < 4096 + 65) { + csr_write_helper(env, ldtul_p(mem_buf), n - 65); + } +*/ + return sizeof(target_ulong); +} diff --git a/qemu/target/rh850/helper.c b/qemu/target/rh850/helper.c new file mode 100644 index 0000000000..60d18c1394 --- /dev/null +++ b/qemu/target/rh850/helper.c @@ -0,0 +1,500 @@ +/* + * RH850 emulation helpers for qemu. + * + * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu + * Copyright (c) 2017-2018 SiFive, Inc. + * Copyright (c) 2018-2019 iSYSTEM Labs d.o.o. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "cpu.h" +#include "exec/exec-all.h" + +#define RH850_DEBUG_INTERRUPT 0 + +int rh850_cpu_mmu_index(CPURH850State *env, bool ifetch) +{ +#ifdef CONFIG_USER_ONLY + return 0; +#else + return env->priv; +#endif +} + + +uint32_t rh850_ldl_phys(CPUState *cs, hwaddr addr) +{ + MemTxAttrs attrs; + AddressSpace *as = cpu_addressspace(cs, attrs); + + attrs.secure = false; + +#ifdef UNICORN_ARCH_POSTFIX + return glue(address_space_ldl, UNICORN_ARCH_POSTFIX)(as->uc, as, addr, attrs, NULL); +#else + return address_space_ldl(as->uc, as, addr, attrs, NULL); +#endif +} + +#ifndef CONFIG_USER_ONLY +/* + * Return RH850 IRQ number if an interrupt should be taken, else -1. + * Used in cpu-exec.c + * + * Adapted from Spike's processor_t::take_interrupt() + */ +static int rh850_cpu_hw_interrupts_pending(CPURH850State *env) +{ + target_ulong pending_interrupts = atomic_read(&env->mip) & env->mie; + + target_ulong mie = get_field(env->mstatus, MSTATUS_MIE); + target_ulong m_enabled = env->priv < PRV_M || (env->priv == PRV_M && mie); + target_ulong enabled_interrupts = pending_interrupts & + ~env->mideleg & -m_enabled; + + target_ulong sie = get_field(env->mstatus, MSTATUS_SIE); + target_ulong s_enabled = env->priv < PRV_S || (env->priv == PRV_S && sie); + enabled_interrupts |= pending_interrupts & env->mideleg & + -s_enabled; + + if (enabled_interrupts) { + return ctz64(enabled_interrupts); /* since non-zero */ + } else { + return EXCP_NONE; /* indicates no pending interrupt */ + } +} +#endif + +bool rh850_cpu_exec_interrupt(CPUState *cs, int interrupt_request) +{ +#if !defined(CONFIG_USER_ONLY) + if (interrupt_request & CPU_INTERRUPT_HARD) { + RH850CPU *cpu = RH850_CPU(cs); + CPURH850State *env = &cpu->env; + int interruptno = rh850_cpu_hw_interrupts_pending(env); + if (interruptno >= 0) { + cs->exception_index = RH850_EXCP_INT_FLAG | interruptno; + rh850_cpu_do_interrupt(cs); + return true; + } + } +#endif + return false; +} + +#if !defined(CONFIG_USER_ONLY) + +/* get_physical_address - get the physical address for this virtual address + * + * Do a page table walk to obtain the physical address corresponding to a + * virtual address. Returns 0 if the translation was successful + * + * Adapted from Spike's mmu_t::translate and mmu_t::walk + * + */ +static int get_physical_address(struct uc_struct *uc, CPURH850State *env, hwaddr *physical, + int *prot, target_ulong addr, + int access_type, int mmu_idx) +{ + /* NOTE: the env->pc value visible here will not be + * correct, but the value visible to the exception handler + * (rh850_cpu_do_interrupt) is correct */ + + int mode = mmu_idx; + + if (mode == PRV_M && access_type != MMU_INST_FETCH) { + if (get_field(env->mstatus, MSTATUS_MPRV)) { + mode = get_field(env->mstatus, MSTATUS_MPP); + } + } + + if (mode == PRV_M || !rh850_feature(env, RH850_FEATURE_MMU)) { + *physical = addr; + *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + return TRANSLATE_SUCCESS; + } + + *prot = 0; + + target_ulong base; + int levels, ptidxbits, ptesize, vm, sum; + int mxr = get_field(env->mstatus, MSTATUS_MXR); + + if (env->priv_ver >= PRIV_VERSION_1_10_0) { + base = get_field(env->satp, SATP_PPN) << PGSHIFT; + sum = get_field(env->mstatus, MSTATUS_SUM); + vm = get_field(env->satp, SATP_MODE); + switch (vm) { + case VM_1_10_SV32: + levels = 2; ptidxbits = 10; ptesize = 4; break; + case VM_1_10_SV39: + levels = 3; ptidxbits = 9; ptesize = 8; break; + case VM_1_10_SV48: + levels = 4; ptidxbits = 9; ptesize = 8; break; + case VM_1_10_SV57: + levels = 5; ptidxbits = 9; ptesize = 8; break; + case VM_1_10_MBARE: + *physical = addr; + *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + return TRANSLATE_SUCCESS; + default: + g_assert_not_reached(); + } + } else { + base = env->sptbr << PGSHIFT; + sum = !get_field(env->mstatus, MSTATUS_PUM); + vm = get_field(env->mstatus, MSTATUS_VM); + switch (vm) { + case VM_1_09_SV32: + levels = 2; ptidxbits = 10; ptesize = 4; break; + case VM_1_09_SV39: + levels = 3; ptidxbits = 9; ptesize = 8; break; + case VM_1_09_SV48: + levels = 4; ptidxbits = 9; ptesize = 8; break; + case VM_1_09_MBARE: + *physical = addr; + *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + return TRANSLATE_SUCCESS; + default: + g_assert_not_reached(); + } + } + + CPUState *cs = CPU(rh850_env_get_cpu(env)); + int va_bits = PGSHIFT + levels * ptidxbits; + target_ulong mask = (1L << (TARGET_LONG_BITS - (va_bits - 1))) - 1; + target_ulong masked_msbs = (addr >> (va_bits - 1)) & mask; + if (masked_msbs != 0 && masked_msbs != mask) { + return TRANSLATE_FAIL; + } + + int ptshift = (levels - 1) * ptidxbits; + int i; + +#if !TCG_OVERSIZED_GUEST +restart: +#endif + for (i = 0; i < levels; i++, ptshift -= ptidxbits) { + target_ulong idx = (addr >> (PGSHIFT + ptshift)) & + ((1 << ptidxbits) - 1); + + /* check that physical address of PTE is legal */ + target_ulong pte_addr = base + idx * ptesize; +#if defined(TARGET_RH850) + target_ulong pte = rh850_ldl_phys(cs, pte_addr); +#endif + target_ulong ppn = pte >> PTE_PPN_SHIFT; + + if (PTE_TABLE(pte)) { /* next level of page table */ + base = ppn << PGSHIFT; + } else if ((pte & PTE_U) ? (mode == PRV_S) && !sum : !(mode == PRV_S)) { + break; + } else if (!(pte & PTE_V) || (!(pte & PTE_R) && (pte & PTE_W))) { + break; + } else if (access_type == MMU_INST_FETCH ? !(pte & PTE_X) : + access_type == MMU_DATA_LOAD ? !(pte & PTE_R) && + !(mxr && (pte & PTE_X)) : !((pte & PTE_R) && (pte & PTE_W))) { + break; + } else { + /* if necessary, set accessed and dirty bits. */ + target_ulong updated_pte = pte | PTE_A | + (access_type == MMU_DATA_STORE ? PTE_D : 0); + + /* Page table updates need to be atomic with MTTCG enabled */ + if (updated_pte != pte) { + /* if accessed or dirty bits need updating, and the PTE is + * in RAM, then we do so atomically with a compare and swap. + * if the PTE is in IO space, then it can't be updated. + * if the PTE changed, then we must re-walk the page table + as the PTE is no longer valid */ + MemoryRegion *mr; + hwaddr l = sizeof(target_ulong), addr1; + mr = address_space_translate(cs->as, pte_addr, + &addr1, &l, false, MEMTXATTRS_UNSPECIFIED); + if (memory_access_is_direct(mr, true)) { + target_ulong *pte_pa = + qemu_map_ram_ptr(uc, mr->ram_block, addr1); +#if TCG_OVERSIZED_GUEST + /* MTTCG is not enabled on oversized TCG guests so + * page table updates do not need to be atomic */ + *pte_pa = pte = updated_pte; +#else + target_ulong old_pte = + atomic_cmpxchg(pte_pa, pte, updated_pte); + if (old_pte != pte) { + goto restart; + } else { + pte = updated_pte; + } +#endif + } else { + /* misconfigured PTE in ROM (AD bits are not preset) or + * PTE is in IO space and can't be updated atomically */ + return TRANSLATE_FAIL; + } + } + + /* for superpage mappings, make a fake leaf PTE for the TLB's + benefit. */ + target_ulong vpn = addr >> PGSHIFT; + *physical = (ppn | (vpn & ((1L << ptshift) - 1))) << PGSHIFT; + + if ((pte & PTE_R)) { + *prot |= PAGE_READ; + } + if ((pte & PTE_X)) { + *prot |= PAGE_EXEC; + } + /* only add write permission on stores or if the page + is already dirty, so that we don't miss further + page table walks to update the dirty bit */ + if ((pte & PTE_W) && + (access_type == MMU_DATA_STORE || (pte & PTE_D))) { + *prot |= PAGE_WRITE; + } + return TRANSLATE_SUCCESS; + } + } + return TRANSLATE_FAIL; +} + +static void raise_mmu_exception(CPURH850State *env, target_ulong address, + MMUAccessType access_type) +{ + CPUState *cs = CPU(rh850_env_get_cpu(env)); + int page_fault_exceptions = + (env->priv_ver >= PRIV_VERSION_1_10_0) && + get_field(env->satp, SATP_MODE) != VM_1_10_MBARE; + switch (access_type) { + case MMU_INST_FETCH: + cs->exception_index = page_fault_exceptions ? + RH850_EXCP_INST_PAGE_FAULT : RH850_EXCP_INST_ACCESS_FAULT; + break; + case MMU_DATA_LOAD: + cs->exception_index = page_fault_exceptions ? + RH850_EXCP_LOAD_PAGE_FAULT : RH850_EXCP_LOAD_ACCESS_FAULT; + break; + case MMU_DATA_STORE: + cs->exception_index = page_fault_exceptions ? + RH850_EXCP_STORE_PAGE_FAULT : RH850_EXCP_STORE_AMO_ACCESS_FAULT; + break; + default: + g_assert_not_reached(); + } + env->badaddr = address; +} + +hwaddr rh850_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) +{ + RH850CPU *cpu = RH850_CPU(cs); + hwaddr phys_addr; + int prot; + int mmu_idx = cpu_mmu_index(&cpu->env, false); + + if (get_physical_address(cs->uc, &cpu->env, &phys_addr, &prot, addr, 0, mmu_idx)) { + return -1; + } + return phys_addr; +} + +void rh850_cpu_do_unaligned_access(CPUState *cs, vaddr addr, + MMUAccessType access_type, int mmu_idx, + uintptr_t retaddr) +{ + RH850CPU *cpu = RH850_CPU(cs); + CPURH850State *env = &cpu->env; + switch (access_type) { + case MMU_INST_FETCH: + cs->exception_index = RH850_EXCP_INST_ADDR_MIS; + break; + case MMU_DATA_LOAD: + cs->exception_index = RH850_EXCP_LOAD_ADDR_MIS; + break; + case MMU_DATA_STORE: + cs->exception_index = RH850_EXCP_STORE_AMO_ADDR_MIS; + break; + default: + g_assert_not_reached(); + } + env->badaddr = addr; + do_raise_exception_err(env, cs->exception_index, retaddr); +} + +#endif + +int rh850_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size, + int rw, int mmu_idx) +{ + RH850CPU *cpu = RH850_CPU(cs); + CPURH850State *env = &cpu->env; +#if !defined(CONFIG_USER_ONLY) + hwaddr pa = 0; + int prot; +#endif + int ret = TRANSLATE_FAIL; + + qemu_log_mask(CPU_LOG_MMU, + "%s pc " TARGET_FMT_lx " ad %" VADDR_PRIx " rw %d mmu_idx \ + %d\n", __func__, env->pc, address, rw, mmu_idx); + +#if !defined(CONFIG_USER_ONLY) + ret = get_physical_address(cs->uc, env, &pa, &prot, address, rw, mmu_idx); + qemu_log_mask(CPU_LOG_MMU, + "%s address=%" VADDR_PRIx " ret %d physical " TARGET_FMT_plx + " prot %d\n", __func__, address, ret, pa, prot); + if (!pmp_hart_has_privs(env, pa, TARGET_PAGE_SIZE, 1 << rw)) { + ret = TRANSLATE_FAIL; + } + if (ret == TRANSLATE_SUCCESS) { + tlb_set_page(cs, address & TARGET_PAGE_MASK, pa & TARGET_PAGE_MASK, + prot, mmu_idx, TARGET_PAGE_SIZE); + } else if (ret == TRANSLATE_FAIL) { + raise_mmu_exception(env, address, rw); + } +#else + switch (rw) { + case MMU_INST_FETCH: + cs->exception_index = RH850_EXCP_INST_PAGE_FAULT; + break; + case MMU_DATA_LOAD: + cs->exception_index = RH850_EXCP_LOAD_PAGE_FAULT; + break; + case MMU_DATA_STORE: + cs->exception_index = RH850_EXCP_STORE_PAGE_FAULT; + break; + } +#endif + return ret; +} + +/* + */ +void rh850_cpu_do_interrupt(CPUState *cs) +{ +#if !defined(CONFIG_USER_ONLY) + + //RH850CPU *cpu = RH850_CPU(cs); + +#if RH850_DEBUG_INTERRUPT + int log_cause = cs->exception_index & RH850_EXCP_INT_MASK; + if (cs->exception_index & RH850_EXCP_INT_FLAG) { + qemu_log_mask(LOG_TRACE, "core 0: trap %s, epc 0x" TARGET_FMT_lx, + rh850_intr_names[log_cause], env->pc); + } else { + qemu_log_mask(LOG_TRACE, "core 0: intr %s, epc 0x" TARGET_FMT_lx, + rh850_excp_names[log_cause], env->pc); + } + } +#endif +// exceptions are not yet implemented on rh850 + +// target_ulong fixed_cause = 0; +// if (cs->exception_index & (RH850_EXCP_INT_FLAG)) { +// /* hacky for now. the MSB (bit 63) indicates interrupt but cs->exception +// index is only 32 bits wide */ +// fixed_cause = cs->exception_index & RH850_EXCP_INT_MASK; +// fixed_cause |= ((target_ulong)1) << (TARGET_LONG_BITS - 1); +// } else { +// /* fixup User ECALL -> correct priv ECALL */ +// if (cs->exception_index == RH850_EXCP_U_ECALL) { +// switch (env->priv) { +// case PRV_U: +// fixed_cause = RH850_EXCP_U_ECALL; +// break; +// case PRV_S: +// fixed_cause = RH850_EXCP_S_ECALL; +// break; +// case PRV_H: +// fixed_cause = RH850_EXCP_H_ECALL; +// break; +// case PRV_M: +// fixed_cause = RH850_EXCP_M_ECALL; +// break; +// } +// } else { +// fixed_cause = cs->exception_index; +// } +// } +// +// target_ulong backup_epc = env->pc; +// +// target_ulong bit = fixed_cause; +// target_ulong deleg = env->medeleg; +// +// int hasbadaddr = +// (fixed_cause == RH850_EXCP_INST_ADDR_MIS) || +// (fixed_cause == RH850_EXCP_INST_ACCESS_FAULT) || +// (fixed_cause == RH850_EXCP_LOAD_ADDR_MIS) || +// (fixed_cause == RH850_EXCP_STORE_AMO_ADDR_MIS) || +// (fixed_cause == RH850_EXCP_LOAD_ACCESS_FAULT) || +// (fixed_cause == RH850_EXCP_STORE_AMO_ACCESS_FAULT) || +// (fixed_cause == RH850_EXCP_INST_PAGE_FAULT) || +// (fixed_cause == RH850_EXCP_LOAD_PAGE_FAULT) || +// (fixed_cause == RH850_EXCP_STORE_PAGE_FAULT); +// +// if (bit & ((target_ulong)1 << (TARGET_LONG_BITS - 1))) { +// deleg = env->mideleg; +// bit &= ~((target_ulong)1 << (TARGET_LONG_BITS - 1)); +// } +// +// if (env->priv <= PRV_S && bit < 64 && ((deleg >> bit) & 1)) { +// /* handle the trap in S-mode */ +// /* No need to check STVEC for misaligned - lower 2 bits cannot be set */ +// env->pc = env->stvec; +// env->scause = fixed_cause; +// env->sepc = backup_epc; +// +// if (hasbadaddr) { +// if (RH850_DEBUG_INTERRUPT) { +// qemu_log_mask(LOG_TRACE, "core " TARGET_FMT_ld +// ": badaddr 0x" TARGET_FMT_lx, env->mhartid, env->badaddr); +// } +// //env->sbadaddr = env->mea; Is sbadaddr=any badaddr???? +// } +// +// target_ulong s = env->mstatus; +// s = set_field(s, MSTATUS_SPIE, env->priv_ver >= PRIV_VERSION_1_10_0 ? +// get_field(s, MSTATUS_SIE) : get_field(s, MSTATUS_UIE << env->priv)); +// s = set_field(s, MSTATUS_SPP, env->priv); +// s = set_field(s, MSTATUS_SIE, 0); +//// csr_write_helper(env, s, CSR_MSTATUS); +// rh850_set_mode(env, PRV_S); +// } else { +// /* No need to check MTVEC for misaligned - lower 2 bits cannot be set */ +// env->pc = env->mtvec; +// env->mepc = backup_epc; +// env->mcause = fixed_cause; +// +// if (hasbadaddr) { +// if (RH850_DEBUG_INTERRUPT) { +// qemu_log_mask(LOG_TRACE, "core " TARGET_FMT_ld +// ": mea 0x" TARGET_FMT_lx, env->mhartid, env->badaddr); +// } +// //env->mbadaddr = env->badaddr; +// } +// +// target_ulong s = env->mstatus; +// s = set_field(s, MSTATUS_MPIE, env->priv_ver >= PRIV_VERSION_1_10_0 ? +// get_field(s, MSTATUS_MIE) : get_field(s, MSTATUS_UIE << env->priv)); +// s = set_field(s, MSTATUS_MPP, env->priv); +// s = set_field(s, MSTATUS_MIE, 0); +//// csr_write_helper(env, s, CSR_MSTATUS); +// rh850_set_mode(env, PRV_M); +// } + /* TODO yield load reservation */ +#endif + cs->exception_index = EXCP_NONE; /* mark handled to qemu */ +} diff --git a/qemu/target/rh850/helper.h b/qemu/target/rh850/helper.h new file mode 100644 index 0000000000..cbdf08ffec --- /dev/null +++ b/qemu/target/rh850/helper.h @@ -0,0 +1,82 @@ + +DEF_HELPER_4(uc_tracecode, void, i32, i32, ptr, i64) +DEF_HELPER_6(uc_traceopcode, void, ptr, i64, i64, i32, ptr, i64) + +/* Exceptions */ +DEF_HELPER_2(raise_exception, noreturn, env, i32) + +/* Floating Point - rounding mode */ +DEF_HELPER_FLAGS_2(set_rounding_mode, TCG_CALL_NO_WG, void, env, i32) + +/* Floating Point - fused */ +DEF_HELPER_FLAGS_4(fmadd_s, TCG_CALL_NO_RWG, i64, env, i64, i64, i64) +DEF_HELPER_FLAGS_4(fmadd_d, TCG_CALL_NO_RWG, i64, env, i64, i64, i64) +DEF_HELPER_FLAGS_4(fmsub_s, TCG_CALL_NO_RWG, i64, env, i64, i64, i64) +DEF_HELPER_FLAGS_4(fmsub_d, TCG_CALL_NO_RWG, i64, env, i64, i64, i64) +DEF_HELPER_FLAGS_4(fnmsub_s, TCG_CALL_NO_RWG, i64, env, i64, i64, i64) +DEF_HELPER_FLAGS_4(fnmsub_d, TCG_CALL_NO_RWG, i64, env, i64, i64, i64) +DEF_HELPER_FLAGS_4(fnmadd_s, TCG_CALL_NO_RWG, i64, env, i64, i64, i64) +DEF_HELPER_FLAGS_4(fnmadd_d, TCG_CALL_NO_RWG, i64, env, i64, i64, i64) + +/* Floating Point - Single Precision */ +DEF_HELPER_FLAGS_3(fadd_s, TCG_CALL_NO_RWG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fsub_s, TCG_CALL_NO_RWG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fmul_s, TCG_CALL_NO_RWG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fdiv_s, TCG_CALL_NO_RWG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fmin_s, TCG_CALL_NO_RWG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fmax_s, TCG_CALL_NO_RWG, i64, env, i64, i64) +DEF_HELPER_FLAGS_2(fsqrt_s, TCG_CALL_NO_RWG, i64, env, i64) +DEF_HELPER_FLAGS_3(fle_s, TCG_CALL_NO_RWG, tl, env, i64, i64) +DEF_HELPER_FLAGS_3(flt_s, TCG_CALL_NO_RWG, tl, env, i64, i64) +DEF_HELPER_FLAGS_3(feq_s, TCG_CALL_NO_RWG, tl, env, i64, i64) +DEF_HELPER_FLAGS_2(fcvt_w_s, TCG_CALL_NO_RWG, tl, env, i64) +DEF_HELPER_FLAGS_2(fcvt_wu_s, TCG_CALL_NO_RWG, tl, env, i64) +#if defined(TARGET_RH85064) +DEF_HELPER_FLAGS_2(fcvt_l_s, TCG_CALL_NO_RWG, tl, env, i64) +DEF_HELPER_FLAGS_2(fcvt_lu_s, TCG_CALL_NO_RWG, tl, env, i64) +#endif +DEF_HELPER_FLAGS_2(fcvt_s_w, TCG_CALL_NO_RWG, i64, env, tl) +DEF_HELPER_FLAGS_2(fcvt_s_wu, TCG_CALL_NO_RWG, i64, env, tl) +#if defined(TARGET_RH85064) +DEF_HELPER_FLAGS_2(fcvt_s_l, TCG_CALL_NO_RWG, i64, env, tl) +DEF_HELPER_FLAGS_2(fcvt_s_lu, TCG_CALL_NO_RWG, i64, env, tl) +#endif +DEF_HELPER_FLAGS_1(fclass_s, TCG_CALL_NO_RWG_SE, tl, i64) + +/* Floating Point - Double Precision */ +DEF_HELPER_FLAGS_3(fadd_d, TCG_CALL_NO_RWG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fsub_d, TCG_CALL_NO_RWG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fmul_d, TCG_CALL_NO_RWG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fdiv_d, TCG_CALL_NO_RWG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fmin_d, TCG_CALL_NO_RWG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fmax_d, TCG_CALL_NO_RWG, i64, env, i64, i64) +DEF_HELPER_FLAGS_2(fcvt_s_d, TCG_CALL_NO_RWG, i64, env, i64) +DEF_HELPER_FLAGS_2(fcvt_d_s, TCG_CALL_NO_RWG, i64, env, i64) +DEF_HELPER_FLAGS_2(fsqrt_d, TCG_CALL_NO_RWG, i64, env, i64) +DEF_HELPER_FLAGS_3(fle_d, TCG_CALL_NO_RWG, tl, env, i64, i64) +DEF_HELPER_FLAGS_3(flt_d, TCG_CALL_NO_RWG, tl, env, i64, i64) +DEF_HELPER_FLAGS_3(feq_d, TCG_CALL_NO_RWG, tl, env, i64, i64) +DEF_HELPER_FLAGS_2(fcvt_w_d, TCG_CALL_NO_RWG, tl, env, i64) +DEF_HELPER_FLAGS_2(fcvt_wu_d, TCG_CALL_NO_RWG, tl, env, i64) +#if defined(TARGET_RH85064) +DEF_HELPER_FLAGS_2(fcvt_l_d, TCG_CALL_NO_RWG, tl, env, i64) +DEF_HELPER_FLAGS_2(fcvt_lu_d, TCG_CALL_NO_RWG, tl, env, i64) +#endif +DEF_HELPER_FLAGS_2(fcvt_d_w, TCG_CALL_NO_RWG, i64, env, tl) +DEF_HELPER_FLAGS_2(fcvt_d_wu, TCG_CALL_NO_RWG, i64, env, tl) +#if defined(TARGET_RH85064) +DEF_HELPER_FLAGS_2(fcvt_d_l, TCG_CALL_NO_RWG, i64, env, tl) +DEF_HELPER_FLAGS_2(fcvt_d_lu, TCG_CALL_NO_RWG, i64, env, tl) +#endif +DEF_HELPER_FLAGS_1(fclass_d, TCG_CALL_NO_RWG_SE, tl, i64) + +/* Special functions */ +//DEF_HELPER_3(csrrw, tl, env, tl, tl) +//DEF_HELPER_4(csrrs, tl, env, tl, tl, tl) +//DEF_HELPER_4(csrrc, tl, env, tl, tl, tl) +#ifndef CONFIG_USER_ONLY +//DEF_HELPER_2(sret, tl, env, tl) +//DEF_HELPER_2(mret, tl, env, tl) +//DEF_HELPER_1(wfi, void, env) +DEF_HELPER_1(tlb_flush, void, env) +#endif diff --git a/qemu/target/rh850/instmap.h b/qemu/target/rh850/instmap.h new file mode 100644 index 0000000000..5698353de9 --- /dev/null +++ b/qemu/target/rh850/instmap.h @@ -0,0 +1,420 @@ +/* + * RH850 emulation for qemu: Instruction decode helpers + * + * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +enum{ + /*SIGNED INT*/ + COND_RH850_BGE = 1110, + COND_RH850_BGT = 1111, + COND_RH850_BLE = 0111, + COND_RH850_BLT = 0110, + /*UNSIGNED INT*/ + COND_RH850_BH = 1011, + COND_RH850_BL = 0001, + COND_RH850_BNH = 0011, + COND_RH850_BNL = 1001, + /*COMMON*/ + COND_RH850_BE = 0010, + COND_RH850_BNE = 1010, + /*OTHERS*/ + COND_RH850_BC = 0001, + COND_RH850_BF = 1010, + COND_RH850_BN = 0100, + COND_RH850_BNC = 1001, + COND_RH850_BNV = 1000, + COND_RH850_BNZ = 1010, + COND_RH850_BP = 1100, + COND_RH850_BR = 0101, + COND_RH850_BSA = 1101, + COND_RH850_BT = 0010, + COND_RH850_BV = 0000, + COND_RH850_BZ = 0010, +}; + +#define MASK_OP_MAJOR(op) (op & (0x3F << 5)) // the major opcode in rh850 is at bits 10-5 +enum { + /* FORMAT I */ // unique opcodes and grouped instructions + OPC_RH850_16bit_0 = (0x0 << 5), // group with opcode 0x0 (nop, synci, synce, syncm, syncp, mov) + OPC_RH850_NOT_reg1_reg2 = (0x1 << 5), + OPC_RH850_16bit_2 = (0x2 << 5), // group with opcode 0x2 (rie, switch, divh, fetrap) + OPC_RH850_16bit_3 = (0x3 << 5), // group with opcode 0x3 (jmp,sld.bu,sld.hu) + OPC_RH850_16bit_4 = (0x4 << 5), // group with opcode 0x4 (zyb, satsub) + OPC_RH850_16bit_5 = (0x5 << 5), // group with opcode 0x5 (sxb, satsub) + OPC_RH850_16bit_6 = (0x6 << 5), // group with opcode 0x6 (zyh, satadd) + OPC_RH850_16bit_7 = (0x7 << 5), // group with opcode 0x7 (sxh, mulh) + OPC_RH850_OR_reg1_reg2 = (0x8 << 5), + OPC_RH850_XOR_reg1_reg2 = (0x9 << 5), + OPC_RH850_AND_reg1_reg2 = (0xA << 5), + OPC_RH850_TST_reg1_reg2 = (0xB << 5), + OPC_RH850_SUBR_reg1_reg2 = (0xC << 5), + OPC_RH850_SUB_reg1_reg2 = (0xD << 5), + OPC_RH850_ADD_reg1_reg2 = (0xE << 5), + OPC_RH850_CMP_reg1_reg2 = (0xF << 5), + + /* FORMAT II */ + OPC_RH850_16bit_16 = (0x10 << 5), // group with opcode 0x10 (mov,callt) + OPC_RH850_16bit_17 = (0x11 << 5), // group with opcode 0x11 (callt, satadd) + OPC_RH850_ADD_imm5_reg2= (0x12 << 5), // group with opcode 0x12 (add) + OPC_RH850_CMP_imm5_reg2 = (0x13 << 5), // group with opcode 0x13 (cmp) + OPC_RH850_SHR_imm5_reg2 = (0x14 << 5), + OPC_RH850_SAR_imm5_reg2 = (0x15 << 5), + OPC_RH850_SHL_imm5_reg2 = (0x16 << 5), + OPC_RH850_MULH_imm5_reg2 = (0x17 << 5), + + /*FORMAT III */ + OPC_RH850_BCOND = (0xB << 7), // different mask! (bits 10-7) + + /* FORMAT IV */ // different mask! (bits 10-7) + OPC_RH850_16bit_SLDB = (0x6 << 5), + OPC_RH850_16bit_SLDH = (0x8 << 5), + OPC_RH850_16bit_IV10 = (0xA << 5), // group with opcode 0xA (sld.w,sst.w) + OPC_RH850_16bit_SSTB = (0x7 << 5), + OPC_RH850_16bit_SSTH = (0x9 << 5), + + /* FORMAT VI */ + OPC_RH850_ADDI_imm16_reg1_reg2 = (0x30 << 5), + OPC_RH850_ANDI_imm16_reg1_reg2 = (0x36 << 5), + OPC_RH850_MOVEA = (0x31 << 5), // this is also MOV 3, which is 48 bit + OPC_RH850_MOVHI_imm16_reg1_reg2 = (0x32 << 5), + OPC_RH850_ORI_imm16_reg1_reg2 = (0x34 << 5), + OPC_RH850_SATSUBI_imm16_reg1_reg2= (0x33 << 5), + OPC_RH850_XORI_imm16_reg1_reg2 = (0x35 << 5), + + + /* FORMAT VII */ + + OPC_RH850_LOOP = (0x37 << 5), //same as MULHI in format VI !!!! + + OPC_RH850_LDB = (0x38 << 5), + OPC_RH850_LDH_LDW = (0x39 << 5), + OPC_RH850_STB = (0x3A << 5), + OPC_RH850_STH_STW = (0x3B << 5), //the store halfword and store word instructions differ on LSB displacement bit 16 (0=ST.H, 1=ST.W) (format VII) + + OPC_RH850_ST_LD_0 = (0x3C << 5), //5 instructions share this opcode, sub-op bits 11-15 are 0, inst. differ in sub-op bits 16-19 (ST.B2=D, ST.W2=F) (format XIV) + OPC_RH850_ST_LD_1 = (0x3D << 5), //5 instructions share this opcode, sub-op bits 11-15 are 0, inst. differ in sub-op bits 16-19 (ST.DW=F, ST.H2=D) (format XIV) + //OPC_RH850_LDHU = (0x3F << 5), //bits 11-15 are not all 0 + + OPC_RH850_32bit_1 = (0x3F << 5), // 111111 + + + + + OPC_RH850_BIT_MANIPULATION_2 = (0x3E << 5), + + OPC_RH850_FORMAT_V_XIII = (0x1E << 6), + + + OPC_RH850_MULH1 = (0x7 << 5), + OPC_RH850_MULH2 = (0x17 << 5), + + +}; + +enum{ + OPC_RH850_SET1_reg2_reg1 = 0, + OPC_RH850_NOT1_reg2_reg1 = 2, + OPC_RH850_CLR1_reg2_reg1 = 4, + OPC_RH850_TST1_reg2_reg1 = 6, +}; + +enum{ + OPC_RH850_SET1_bit3_disp16_reg1 = 1, + OPC_RH850_NOT1_bit3_disp16_reg1 = 3, + OPC_RH850_CLR1_bit3_disp16_reg1 = 5, + OPC_RH850_TST1_bit3_disp16_reg1 = 7, +}; + +enum{ + OPC_RH850_MOV_reg1_reg2 = 1, + OPC_RH850_MOV_imm5_reg2 = 2, + OPC_RH850_MOV_imm32_reg1 = 3, + OPC_RH850_MOVEA_imm16_reg1_reg2 = 4, +}; + +enum{ + OPC_RH850_SATADD_reg1_reg2 = 1, + OPC_RH850_SATADD_imm5_reg2 = 2, + OPC_RH850_SATADD_reg1_reg2_reg3 = 3, + OPC_RH850_SATSUB_reg1_reg2 = 4, + OPC_RH850_SATSUB_reg1_reg2_reg3 = 5, + OPC_RH850_SATSUBR_reg1_reg2 = 6, +}; + +enum{ + OPC_RH850_MUL_reg1_reg2_reg3 = 1, + OPC_RH850_MUL_imm9_reg2_reg3 = 2, + OPC_RH850_MULH_reg1_reg2 = 3, + //OPC_RH850_MULH_imm5_reg2 = 4, + OPC_RH850_MULHI_imm16_reg1_reg2 = 5, + OPC_RH850_MULU_reg1_reg2_reg3 = 8, + OPC_RH850_MULU_imm9_reg2_reg3 = 9, +}; + +enum{ + OPC_RH850_ADF_cccc_reg1_reg2_reg3 = 10, + OPC_RH850_SBF_cccc_reg1_reg2_reg3 = 11, + OPC_RH850_DIVH_reg1_reg2 = 12, +}; + +enum{ //enum for gen_data_manipulation cases + OPC_RH850_SHR_reg1_reg2 = 111, + OPC_RH850_SHR_reg1_reg2_reg3 = 222, + OPC_RH850_CMOV_cccc_reg1_reg2_reg3 = 333, + OPC_RH850_CMOV_cccc_imm5_reg2_reg3 = 444, + OPC_RH850_ROTL_reg1_reg2_reg3 = 445, + OPC_RH850_ROTL_imm5_reg2_reg3 = 446, + OPC_RH850_SAR_reg1_reg2 = 447, + OPC_RH850_SAR_reg1_reg2_reg3 = 448, + OPC_RH850_SASF_cccc_reg2 = 449, + OPC_RH850_SETF_cccc_reg2 = 450, + OPC_RH850_SHL_reg1_reg2 = 451, + OPC_RH850_SHL_reg1_reg2_reg3 = 453, + OPC_RH850_SXB_reg1 = 454, + OPC_RH850_SXH_reg1 = 455, + OPC_RH850_ZXB_reg1 = 456, + OPC_RH850_ZXH_reg1 = 457, + + + +}; + +enum{ + OPC_RH850_LDSR_reg2_regID_selID = 1, + OPC_RH850_STSR_regID_reg2_selID = 2, + //check for unintentional matching + OPC_RH850_PREPARE_list12_imm5 = 12, + OPC_RH850_PREPARE_list12_imm5_sp = 13, + OPC_RH850_RIE = 3, + OPC_RH850_CALLT_imm6 = 4, + OPC_RH850_CAXI_reg1_reg2_reg3 = 5, + OPC_RH850_DISPOSE_imm5_list12 = 7, + OPC_RH850_DISPOSE_imm5_list12_reg1 = 8, + OPC_RH850_FETRAP_vector4 = 15, + OPC_RH850_SWITCH_reg1 = 10, +}; + +enum{ // magic numbers for branch opcodes + OPC_RH850_JR_imm22 = 0, + OPC_RH850_JR_imm32 = 1, + OPC_RH850_JARL_disp22_reg2 = 2, + OPC_RH850_JARL_disp32_reg1 = 3, //48-bit + OPC_RH850_JARL_reg1_reg3 = 4, + OPC_RH850_JMP_reg1 = 5, + OPC_RH850_JMP_disp32_reg1 = 6, + +}; + + +#define MASK_OP_FORMAT_I_0(op) (MASK_OP_MAJOR(op) | (op & (0x1F << 11)) | (op & (0x1F << 0))) +enum { + OPC_RH850_NOP = OPC_RH850_16bit_0 | (0x0 << 11) | (0x0 << 0), + OPC_RH850_SYNCI = OPC_RH850_16bit_0 | (0x0 << 11) | (0x1C << 0), + OPC_RH850_SYNCE = OPC_RH850_16bit_0 | (0x0 << 11) | (0x1D << 0), + OPC_RH850_SYNCM = OPC_RH850_16bit_0 | (0x0 << 11) | (0x1E << 0), + OPC_RH850_SYNCP = OPC_RH850_16bit_0 | (0x0 << 11) | (0x1F << 0) +}; + + + +#define MASK_OP_ST_LD0(op) (MASK_OP_MAJOR(op) | (op & (0x1F << 11)) | (op & (0xF << 16))) +enum { + + OPC_RH850_LDB2 = OPC_RH850_ST_LD_0 | (0x00 << 11 ) | (0x5 << 16), + OPC_RH850_LDH2 = OPC_RH850_ST_LD_0 | (0x00 << 11 ) | (0x7 << 16), + OPC_RH850_LDW2 = OPC_RH850_ST_LD_0 | (0x00 << 11 ) | (0x9 << 16), + OPC_RH850_STB2 = OPC_RH850_ST_LD_0 | (0x00 << 11 ) | (0xD << 16), //sub-op bits 11-15 are 0, inst. differ in sub-op bits 16-19 (ST.B2=D, ST.W2=F) (format XIV) + OPC_RH850_STW2 = OPC_RH850_ST_LD_0 | (0x00 << 11 ) | (0xF << 16), + +}; +#define MASK_OP_ST_LD1(op) (MASK_OP_MAJOR(op) | (op & (0x1F << 11)) | (op & (0xF << 16))) +enum { + + OPC_RH850_LDBU2 = OPC_RH850_ST_LD_1 | (0x00 << 11 ) | (0x5 << 16), + OPC_RH850_LDHU2 = OPC_RH850_ST_LD_1 | (0x00 << 11 ) | (0x7 << 16), + OPC_RH850_LDDW = OPC_RH850_ST_LD_1 | (0x00 << 11 ) | (0x9 << 16), + OPC_RH850_STDW = OPC_RH850_ST_LD_1 | (0x00 << 11 ) | (0xF << 16), + OPC_RH850_STH2 = OPC_RH850_ST_LD_1 | (0x00 << 11 ) | (0xD << 16), +}; + +#define MASK_OP_32BIT_SUB(op) (op & (0xF << 23)) +enum { + OPC_RH850_LDSR_RIE_SETF_STSR = (0x0 << 23), + OPC_RH850_FORMAT_IX = (0x1 << 23), // 0001 + OPC_RH850_FORMAT_X = (0x2 << 23), // 0010 + OPC_RH850_MUL_INSTS = (0x4 << 23), // 0100 this is also for SASF + OPC_RH850_FORMAT_XI = (0x5 << 23), // 0101 + OPC_RH850_FORMAT_XII = (0x6 << 23), // 0110 + OPC_RH850_ADDIT_ARITH = (0x7 << 23) // 0111 +}; + +#define MASK_OP_FORMAT_IX(op) (op & (0x3 << 21)) //0001 on b26-b23 +enum { + OPC_RH850_BINS_0 = (0x0 << 21), //BINS0,SHR, SHR2 + OPC_RH850_BINS_1 = (0x1 << 21), //BINS1,SAR,SAR2 + OPC_RH850_BINS_2 = (0x2 << 21), //BINS2,SHL, SHL2, ROTL, ROTL2 + OPC_RH850_BIT_MANIPULATION = (0x3 << 21), //clr1, set, tst1, not1, caxi in format IX +}; + +#define MASK_OP_FORMAT_X(op) (op & (0xFFF << 11)) //0010 on b26-b23 +enum { + OPC_RH850_CTRET = (0x880 << 11), + OPC_RH850_DI = (0xC00 << 11), + OPC_RH850_EI = (0XC10 << 11), + OPC_RH850_EIRET = (0X900 << 11), + OPC_RH850_FERET = (0X940 << 11), + OPC_RH850_HALT = (0X400 << 11), + OPC_RH850_JARL3 = (0XC18 << 11), + OPC_RH850_SNOOZE = (0x401 << 11), + OPC_RH850_SYSCALL = (0xC1A << 11), + OPC_RH850_TRAP = (0x000 << 11), + OPC_RH850_PREF = (0xC1B << 11), + OPC_RH850_POPSP_rh_rt = (0xC0C << 11), + OPC_RH850_PUSHSP_rh_rt = (0xC08 << 11), + //don't forget CACHE + OPC_RH850_CLL = (0xC1F << 11), + +}; + +#define MASK_OP_FORMAT_XI(op) (op & (0x7F << 16)) +enum { + OPC_RH850_DIVH_reg1_reg2_reg3 = 0x0, + OPC_RH850_DIVHU_reg1_reg2_reg3 = 0x2, + OPC_RH850_DIV_reg1_reg2_reg3 = 0x40, + OPC_RH850_DIVQ = 0x7C, + OPC_RH850_DIVQU = 0x7E, + OPC_RH850_DIVU_reg1_reg2_reg3 = 0x42 +}; + +#define MASK_OP_FORMAT_XII(op) (op & (0x3 << 17)) +enum { + OPC_RH850_BSW_reg2_reg3 = (0x0 << 0), + OPC_RH850_BSH_reg2_reg3 = (0x1 << 0), + OPC_RH850_HSW_reg2_reg3 = (0x2 << 0), + OPC_RH850_HSH_reg2_reg3 = (0x3 << 0), + // SCHOL, SCHOR, SCH1L, SCH1R + OPC_RH850_SCH0R_reg2_reg3 = (0x0 << 0), + OPC_RH850_SCH1R_reg2_reg3 = (0x1 << 0), //this is also STCW + OPC_RH850_SCH0L_reg2_reg3 = (0x2 << 0), + OPC_RH850_SCH1L_reg2_reg3 = (0x3 << 0), + + +}; + +#define MASK_ADDIT_ARITH_OP(op) (op & (0x3 << 21)) +enum { + OPC_RH850_SBF_SATSUB = 0x0, + OPC_RH850_ADF_SATADD3 = 0x1, + OPC_RH850_MAC_reg1_reg2_reg3_reg4 = 0x2, + OPC_RH850_MACU_reg1_reg2_reg3_reg4 = 0x3, + + + + +}; + +#define MASK_OP_FORMAT_V_FORMAT_XIII(op) (op & (0x1F << 6)) + + +enum { + operation_LDL_W = 0, + operation_STC_W = 1, + operation_CLL = 2, +}; + + + +////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////// + + +#define GET_B_IMM(inst) ((extract32(inst, 8, 4) << 1) \ + | (extract32(inst, 25, 6) << 5) \ + | (extract32(inst, 7, 1) << 11) \ + | (sextract64(inst, 31, 1) << 12)) + +#define GET_STORE_IMM(inst) ((extract32(inst, 7, 5)) \ + | (sextract64(inst, 25, 7) << 5)) + +#define GET_JAL_IMM(inst) ((extract32(inst, 21, 10) << 1) \ + | (extract32(inst, 20, 1) << 11) \ + | (extract32(inst, 12, 8) << 12) \ + | (sextract64(inst, 31, 1) << 20)) + + +#define GET_RS1(inst) extract32(inst, 0, 5) //appropriate for RH850 +#define GET_RS2(inst) extract32(inst, 11, 5) //appropriate for RH850 +#define GET_RS3(inst) extract32(inst, 27, 5) //appropriate for RH850 +#define GET_DISP(inst) (extract32(inst, 20, 7) | (sextract32(inst, 32, 16) << 7 ) ) //b47-b32 + b26-b20 + + +#define GET_RM(inst) extract32(inst, 12, 3) +#define GET_RD(inst) extract32(inst, 7, 5) +#define GET_IMM(inst) sextract64(inst, 20, 12) +#define GET_IMM_32(inst) sextract64(inst, 16, 32) + +/* RVC decoding macros */ +#define GET_C_IMM(inst) (extract32(inst, 2, 5) \ + | (sextract64(inst, 12, 1) << 5)) +#define GET_C_ZIMM(inst) (extract32(inst, 2, 5) \ + | (extract32(inst, 12, 1) << 5)) +#define GET_C_ADDI4SPN_IMM(inst) ((extract32(inst, 6, 1) << 2) \ + | (extract32(inst, 5, 1) << 3) \ + | (extract32(inst, 11, 2) << 4) \ + | (extract32(inst, 7, 4) << 6)) +#define GET_C_ADDI16SP_IMM(inst) ((extract32(inst, 6, 1) << 4) \ + | (extract32(inst, 2, 1) << 5) \ + | (extract32(inst, 5, 1) << 6) \ + | (extract32(inst, 3, 2) << 7) \ + | (sextract64(inst, 12, 1) << 9)) +#define GET_C_LWSP_IMM(inst) ((extract32(inst, 4, 3) << 2) \ + | (extract32(inst, 12, 1) << 5) \ + | (extract32(inst, 2, 2) << 6)) +#define GET_C_LDSP_IMM(inst) ((extract32(inst, 5, 2) << 3) \ + | (extract32(inst, 12, 1) << 5) \ + | (extract32(inst, 2, 3) << 6)) +#define GET_C_SWSP_IMM(inst) ((extract32(inst, 9, 4) << 2) \ + | (extract32(inst, 7, 2) << 6)) +#define GET_C_SDSP_IMM(inst) ((extract32(inst, 10, 3) << 3) \ + | (extract32(inst, 7, 3) << 6)) +#define GET_C_LW_IMM(inst) ((extract32(inst, 6, 1) << 2) \ + | (extract32(inst, 10, 3) << 3) \ + | (extract32(inst, 5, 1) << 6)) +#define GET_C_LD_IMM(inst) ((extract32(inst, 10, 3) << 3) \ + | (extract32(inst, 5, 2) << 6)) +#define GET_C_J_IMM(inst) ((extract32(inst, 3, 3) << 1) \ + | (extract32(inst, 11, 1) << 4) \ + | (extract32(inst, 2, 1) << 5) \ + | (extract32(inst, 7, 1) << 6) \ + | (extract32(inst, 6, 1) << 7) \ + | (extract32(inst, 9, 2) << 8) \ + | (extract32(inst, 8, 1) << 10) \ + | (sextract64(inst, 12, 1) << 11)) +#define GET_C_B_IMM(inst) ((extract32(inst, 3, 2) << 1) \ + | (extract32(inst, 10, 2) << 3) \ + | (extract32(inst, 2, 1) << 5) \ + | (extract32(inst, 5, 2) << 6) \ + | (sextract64(inst, 12, 1) << 8)) +#define GET_C_SIMM3(inst) extract32(inst, 10, 3) +#define GET_C_RD(inst) GET_RD(inst) +#define GET_C_RS1(inst) GET_RD(inst) +#define GET_C_RS2(inst) extract32(inst, 2, 5) +#define GET_C_RS1S(inst) (8 + extract32(inst, 7, 3)) +#define GET_C_RS2S(inst) (8 + extract32(inst, 2, 3)) diff --git a/qemu/target/rh850/op_helper.c b/qemu/target/rh850/op_helper.c new file mode 100644 index 0000000000..0b39585cae --- /dev/null +++ b/qemu/target/rh850/op_helper.c @@ -0,0 +1,669 @@ +/* + * RH850 Emulation Helpers for QEMU. + * + * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu + * Copyright (c) 2017-2018 SiFive, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "cpu.h" +#include "exec/exec-all.h" +#include "exec/helper-proto.h" + +#ifndef CONFIG_USER_ONLY + +#if defined(TARGET_RH850) +//static const char valid_vm_1_09[16] = { +// [VM_1_09_MBARE] = 1, +// [VM_1_09_SV32] = 1, +//}; +//static const char valid_vm_1_10[16] = { +// [VM_1_10_MBARE] = 1, +// [VM_1_10_SV32] = 1 +//}; +#endif + +//static int validate_vm(CPURH850State *env, target_ulong vm) +//{ +// return (env->priv_ver >= PRIV_VERSION_1_10_0) ? +// valid_vm_1_10[vm & 0xf] : valid_vm_1_09[vm & 0xf]; +//} + +#endif + +/* Exceptions processing helpers */ +void QEMU_NORETURN do_raise_exception_err(CPURH850State *env, + uint32_t exception, uintptr_t pc) +{ + CPUState *cs = CPU(rh850_env_get_cpu(env)); + qemu_log_mask(CPU_LOG_INT, "%s: %d\n", __func__, exception); + cs->exception_index = exception; + cpu_loop_exit_restore(cs, pc); +} + +void helper_raise_exception(CPURH850State *env, uint32_t exception) +{ + do_raise_exception_err(env, exception, 0); +} + +static void validate_mstatus_fs(CPURH850State *env, uintptr_t ra) +{ +#ifndef CONFIG_USER_ONLY + if (!(env->mstatus & MSTATUS_FS)) { + do_raise_exception_err(env, RH850_EXCP_ILLEGAL_INST, ra); + } +#endif +} + +/* + * Handle writes to CSRs and any resulting special behavior + * + * Adapted from Spike's processor_t::set_csr + */ +//void csr_write_helper(CPURH850State *env, target_ulong val_to_write, +// target_ulong csrno) +//{ +//#ifndef CONFIG_USER_ONLY +// uint64_t delegable_ints = MIP_SSIP | MIP_STIP | MIP_SEIP | (1 << IRQ_X_COP); +// uint64_t all_ints = delegable_ints | MIP_MSIP | MIP_MTIP; +//#endif +// +// switch (csrno) { +// case CSR_FFLAGS: +// validate_mstatus_fs(env, GETPC()); +// cpu_rh850_set_fflags(env, val_to_write & (FSR_AEXC >> FSR_AEXC_SHIFT)); +// break; +// case CSR_FRM: +// validate_mstatus_fs(env, GETPC()); +// env->frm = val_to_write & (FSR_RD >> FSR_RD_SHIFT); +// break; +// case CSR_FCSR: +// validate_mstatus_fs(env, GETPC()); +// env->frm = (val_to_write & FSR_RD) >> FSR_RD_SHIFT; +// cpu_rh850_set_fflags(env, (val_to_write & FSR_AEXC) >> FSR_AEXC_SHIFT); +// break; +//#ifndef CONFIG_USER_ONLY +// case CSR_MSTATUS: { +// target_ulong mstatus = env->mstatus; +// target_ulong mask = 0; +// target_ulong mpp = get_field(val_to_write, MSTATUS_MPP); +// +// /* flush tlb on mstatus fields that affect VM */ +// if (env->priv_ver <= PRIV_VERSION_1_09_1) { +// if ((val_to_write ^ mstatus) & (MSTATUS_MXR | MSTATUS_MPP | +// MSTATUS_MPRV | MSTATUS_SUM | MSTATUS_VM)) { +// helper_tlb_flush(env); +// } +// mask = MSTATUS_SIE | MSTATUS_SPIE | MSTATUS_MIE | MSTATUS_MPIE | +// MSTATUS_SPP | MSTATUS_FS | MSTATUS_MPRV | MSTATUS_SUM | +// MSTATUS_MPP | MSTATUS_MXR | +// (validate_vm(env, get_field(val_to_write, MSTATUS_VM)) ? +// MSTATUS_VM : 0); +// } +// if (env->priv_ver >= PRIV_VERSION_1_10_0) { +// if ((val_to_write ^ mstatus) & (MSTATUS_MXR | MSTATUS_MPP | +// MSTATUS_MPRV | MSTATUS_SUM)) { +// helper_tlb_flush(env); +// } +// mask = MSTATUS_SIE | MSTATUS_SPIE | MSTATUS_MIE | MSTATUS_MPIE | +// MSTATUS_SPP | MSTATUS_FS | MSTATUS_MPRV | MSTATUS_SUM | +// MSTATUS_MPP | MSTATUS_MXR; +// } +// +// /* silenty discard mstatus.mpp writes for unsupported modes */ +// if (mpp == PRV_H || +// (!rh850_has_ext(env, RVS) && mpp == PRV_S) || +// (!rh850_has_ext(env, RVU) && mpp == PRV_U)) { +// mask &= ~MSTATUS_MPP; +// } +// +// mstatus = (mstatus & ~mask) | (val_to_write & mask); +// +// /* Note: this is a workaround for an issue where mstatus.FS +// does not report dirty after floating point operations +// that modify floating point state. This workaround is +// technically compliant with the RH850 Privileged +// specification as it is legal to return only off, or dirty. +// at the expense of extra floating point save/restore. */ +// +// /* FP is always dirty or off */ +// if (mstatus & MSTATUS_FS) { +// mstatus |= MSTATUS_FS; +// } +// +// int dirty = ((mstatus & MSTATUS_FS) == MSTATUS_FS) | +// ((mstatus & MSTATUS_XS) == MSTATUS_XS); +// mstatus = set_field(mstatus, MSTATUS_SD, dirty); +// env->mstatus = mstatus; +// break; +// } +// case CSR_MIP: { +// /* +// * Since the writeable bits in MIP are not set asynchrously by the +// * CLINT, no additional locking is needed for read-modifiy-write +// * CSR operations +// */ +// qemu_mutex_lock_iothread(); +// RH850CPU *cpu = rh850_env_get_cpu(env); +// rh850_set_local_interrupt(cpu, MIP_SSIP, +// (val_to_write & MIP_SSIP) != 0); +// rh850_set_local_interrupt(cpu, MIP_STIP, +// (val_to_write & MIP_STIP) != 0); +// /* +// * csrs, csrc on mip.SEIP is not decomposable into separate read and +// * write steps, so a different implementation is needed +// */ +// qemu_mutex_unlock_iothread(); +// break; +// } +// case CSR_MIE: { +// env->mie = (env->mie & ~all_ints) | +// (val_to_write & all_ints); +// break; +// } +// case CSR_MIDELEG: +// env->mideleg = (env->mideleg & ~delegable_ints) +// | (val_to_write & delegable_ints); +// break; +// case CSR_MEDELEG: { +// target_ulong mask = 0; +// mask |= 1ULL << (RH850_EXCP_INST_ADDR_MIS); +// mask |= 1ULL << (RH850_EXCP_INST_ACCESS_FAULT); +// mask |= 1ULL << (RH850_EXCP_ILLEGAL_INST); +// mask |= 1ULL << (RH850_EXCP_BREAKPOINT); +// mask |= 1ULL << (RH850_EXCP_LOAD_ADDR_MIS); +// mask |= 1ULL << (RH850_EXCP_LOAD_ACCESS_FAULT); +// mask |= 1ULL << (RH850_EXCP_STORE_AMO_ADDR_MIS); +// mask |= 1ULL << (RH850_EXCP_STORE_AMO_ACCESS_FAULT); +// mask |= 1ULL << (RH850_EXCP_U_ECALL); +// mask |= 1ULL << (RH850_EXCP_S_ECALL); +// mask |= 1ULL << (RH850_EXCP_H_ECALL); +// mask |= 1ULL << (RH850_EXCP_M_ECALL); +// mask |= 1ULL << (RH850_EXCP_INST_PAGE_FAULT); +// mask |= 1ULL << (RH850_EXCP_LOAD_PAGE_FAULT); +// mask |= 1ULL << (RH850_EXCP_STORE_PAGE_FAULT); +// env->medeleg = (env->medeleg & ~mask) +// | (val_to_write & mask); +// break; +// } +// case CSR_MINSTRET: +// qemu_log_mask(LOG_UNIMP, "CSR_MINSTRET: write not implemented"); +// goto do_illegal; +// case CSR_MCYCLE: +// qemu_log_mask(LOG_UNIMP, "CSR_MCYCLE: write not implemented"); +// goto do_illegal; +// case CSR_MINSTRETH: +// qemu_log_mask(LOG_UNIMP, "CSR_MINSTRETH: write not implemented"); +// goto do_illegal; +// case CSR_MCYCLEH: +// qemu_log_mask(LOG_UNIMP, "CSR_MCYCLEH: write not implemented"); +// goto do_illegal; +// case CSR_MUCOUNTEREN: +// env->mucounteren = val_to_write; +// break; +// case CSR_MSCOUNTEREN: +// env->mscounteren = val_to_write; +// break; +// case CSR_SSTATUS: { +// target_ulong ms = env->mstatus; +// target_ulong mask = SSTATUS_SIE | SSTATUS_SPIE | SSTATUS_UIE +// | SSTATUS_UPIE | SSTATUS_SPP | SSTATUS_FS | SSTATUS_XS +// | SSTATUS_SUM | SSTATUS_MXR | SSTATUS_SD; +// ms = (ms & ~mask) | (val_to_write & mask); +// csr_write_helper(env, ms, CSR_MSTATUS); +// break; +// } +// case CSR_SIP: { +// qemu_mutex_lock_iothread(); +// target_ulong next_mip = (env->mip & ~env->mideleg) +// | (val_to_write & env->mideleg); +// qemu_mutex_unlock_iothread(); +// csr_write_helper(env, next_mip, CSR_MIP); +// break; +// } +// case CSR_SIE: { +// target_ulong next_mie = (env->mie & ~env->mideleg) +// | (val_to_write & env->mideleg); +// csr_write_helper(env, next_mie, CSR_MIE); +// break; +// } +// case CSR_SATP: /* CSR_SPTBR */ { +// if (!rh850_feature(env, RH850_FEATURE_MMU)) { +// goto do_illegal; +// } +// if (env->priv_ver <= PRIV_VERSION_1_09_1 && (val_to_write ^ env->sptbr)) +// { +// helper_tlb_flush(env); +// env->sptbr = val_to_write & (((target_ulong) +// 1 << (TARGET_PHYS_ADDR_SPACE_BITS - PGSHIFT)) - 1); +// } +// if (env->priv_ver >= PRIV_VERSION_1_10_0 && +// validate_vm(env, get_field(val_to_write, SATP_MODE)) && +// ((val_to_write ^ env->satp) & (SATP_MODE | SATP_ASID | SATP_PPN))) +// { +// helper_tlb_flush(env); +// env->satp = val_to_write; +// } +// break; +// } +// case CSR_SEPC: +// env->sepc = val_to_write; +// break; +// case CSR_STVEC: +// if (val_to_write & 1) { +// qemu_log_mask(LOG_UNIMP, "CSR_STVEC: vectored traps not supported"); +// goto do_illegal; +// } +// env->stvec = val_to_write >> 2 << 2; +// break; +// case CSR_SCOUNTEREN: +// env->scounteren = val_to_write; +// break; +// case CSR_SSCRATCH: +// env->sscratch = val_to_write; +// break; +// case CSR_SCAUSE: +// env->scause = val_to_write; +// break; +// case CSR_SBADADDR: +// env->sbadaddr = val_to_write; +// break; +// case CSR_MEPC: +// env->mepc = val_to_write; +// break; +// case CSR_MTVEC: +// if (val_to_write & 1) { +// qemu_log_mask(LOG_UNIMP, "CSR_MTVEC: vectored traps not supported"); +// goto do_illegal; +// } +// env->mtvec = val_to_write >> 2 << 2; +// break; +// case CSR_MCOUNTEREN: +// env->mcounteren = val_to_write; +// break; +// case CSR_MSCRATCH: +// env->mscratch = val_to_write; +// break; +// case CSR_MCAUSE: +// env->mcause = val_to_write; +// break; +// case CSR_MBADADDR: +// env->mbadaddr = val_to_write; +// break; +// case CSR_MISA: { +// qemu_log_mask(LOG_UNIMP, "CSR_MISA: misa writes not supported"); +// goto do_illegal; +// } +// case CSR_PMPCFG0: +// case CSR_PMPCFG1: +// case CSR_PMPCFG2: +// case CSR_PMPCFG3: +// pmpcfg_csr_write(env, csrno - CSR_PMPCFG0, val_to_write); +// break; +// case CSR_PMPADDR0: +// case CSR_PMPADDR1: +// case CSR_PMPADDR2: +// case CSR_PMPADDR3: +// case CSR_PMPADDR4: +// case CSR_PMPADDR5: +// case CSR_PMPADDR6: +// case CSR_PMPADDR7: +// case CSR_PMPADDR8: +// case CSR_PMPADDR9: +// case CSR_PMPADDR10: +// case CSR_PMPADDR11: +// case CSR_PMPADDR12: +// case CSR_PMPADDR13: +// case CSR_PMPADDR14: +// case CSR_PMPADDR15: +// pmpaddr_csr_write(env, csrno - CSR_PMPADDR0, val_to_write); +// break; +// do_illegal: +//#endif +// default: +// do_raise_exception_err(env, RH850_EXCP_ILLEGAL_INST, GETPC()); +// } +//} + +/* + * Handle reads to CSRs and any resulting special behavior + * + * Adapted from Spike's processor_t::get_csr + */ +target_ulong csr_read_helper(CPURH850State *env, target_ulong csrno) +{ +#ifndef CONFIG_USER_ONLY + target_ulong ctr_en = env->priv == PRV_U ? env->mucounteren : + env->priv == PRV_S ? env->mscounteren : -1U; +#else + target_ulong ctr_en = -1; +#endif + target_ulong ctr_ok = (ctr_en >> (csrno & 31)) & 1; + + if (csrno >= CSR_HPMCOUNTER3 && csrno <= CSR_HPMCOUNTER31) { + if (ctr_ok) { + return 0; + } + } +#if defined(TARGET_RH850) + if (csrno >= CSR_HPMCOUNTER3H && csrno <= CSR_HPMCOUNTER31H) { + if (ctr_ok) { + return 0; + } + } +#endif + if (csrno >= CSR_MHPMCOUNTER3 && csrno <= CSR_MHPMCOUNTER31) { + return 0; + } +#if defined(TARGET_RH850) + if (csrno >= CSR_MHPMCOUNTER3 && csrno <= CSR_MHPMCOUNTER31) { + return 0; + } +#endif + if (csrno >= CSR_MHPMEVENT3 && csrno <= CSR_MHPMEVENT31) { + return 0; + } + + switch (csrno) { + case CSR_FFLAGS: + validate_mstatus_fs(env, GETPC()); + return cpu_rh850_get_fflags(env); + case CSR_FRM: + validate_mstatus_fs(env, GETPC()); + return env->frm; + case CSR_FCSR: + validate_mstatus_fs(env, GETPC()); + return (cpu_rh850_get_fflags(env) << FSR_AEXC_SHIFT) + | (env->frm << FSR_RD_SHIFT); + /* rdtime/rdtimeh is trapped and emulated by bbl in system mode */ +#ifdef CONFIG_USER_ONLY + case CSR_TIME: + return cpu_get_host_ticks(); +#if defined(TARGET_RH850) + case CSR_TIMEH: + return cpu_get_host_ticks() >> 32; +#endif +#endif + case CSR_INSTRET: + case CSR_CYCLE: + if (ctr_ok) { + return cpu_get_host_ticks(); + } + break; +#if defined(TARGET_RH850) + case CSR_INSTRETH: + case CSR_CYCLEH: + if (ctr_ok) { + return cpu_get_host_ticks() >> 32; + } + break; +#endif +#ifndef CONFIG_USER_ONLY + case CSR_MINSTRET: + case CSR_MCYCLE: + return cpu_get_host_ticks(); + case CSR_MINSTRETH: + case CSR_MCYCLEH: +#if defined(TARGET_RH850) + return cpu_get_host_ticks() >> 32; +#endif + break; + case CSR_MUCOUNTEREN: + return env->mucounteren; + case CSR_MSCOUNTEREN: + return env->mscounteren; + case CSR_SSTATUS: { + target_ulong mask = SSTATUS_SIE | SSTATUS_SPIE | SSTATUS_UIE + | SSTATUS_UPIE | SSTATUS_SPP | SSTATUS_FS | SSTATUS_XS + | SSTATUS_SUM | SSTATUS_SD; + if (env->priv_ver >= PRIV_VERSION_1_10_0) { + mask |= SSTATUS_MXR; + } + return env->mstatus & mask; + } + case CSR_SIP: { + //qemu_mutex_lock_iothread(); + target_ulong tmp = env->mip & env->mideleg; + //qemu_mutex_unlock_iothread(); + return tmp; + } + case CSR_SIE: + return env->mie & env->mideleg; + case CSR_SEPC: + return env->sepc; + case CSR_SBADADDR: + return env->sbadaddr; + case CSR_STVEC: + return env->stvec; + case CSR_SCOUNTEREN: + return env->scounteren; + case CSR_SCAUSE: + return env->scause; + case CSR_SPTBR: + if (env->priv_ver >= PRIV_VERSION_1_10_0) { + return env->satp; + } else { + return env->sptbr; + } + case CSR_SSCRATCH: + return env->sscratch; + case CSR_MSTATUS: + return env->mstatus; + case CSR_MIP: { + //qemu_mutex_lock_iothread(); + target_ulong tmp = env->mip; + //qemu_mutex_unlock_iothread(); + return tmp; + } + case CSR_MIE: + return env->mie; + case CSR_MEPC: + return env->mepc; + case CSR_MSCRATCH: + return env->mscratch; + case CSR_MCAUSE: + return env->mcause; + case CSR_MBADADDR: + return env->mbadaddr; + case CSR_MISA: + return env->misa; + case CSR_MARCHID: + return 0; /* as spike does */ + case CSR_MIMPID: + return 0; /* as spike does */ + case CSR_MVENDORID: + return 0; /* as spike does */ + case CSR_MHARTID: + return env->mhartid; + case CSR_MTVEC: + return env->mtvec; + case CSR_MCOUNTEREN: + return env->mcounteren; + case CSR_MEDELEG: + return env->medeleg; + case CSR_MIDELEG: + return env->mideleg; + case CSR_PMPCFG0: + case CSR_PMPCFG1: + case CSR_PMPCFG2: + case CSR_PMPCFG3: + return pmpcfg_csr_read(env, csrno - CSR_PMPCFG0); + case CSR_PMPADDR0: + case CSR_PMPADDR1: + case CSR_PMPADDR2: + case CSR_PMPADDR3: + case CSR_PMPADDR4: + case CSR_PMPADDR5: + case CSR_PMPADDR6: + case CSR_PMPADDR7: + case CSR_PMPADDR8: + case CSR_PMPADDR9: + case CSR_PMPADDR10: + case CSR_PMPADDR11: + case CSR_PMPADDR12: + case CSR_PMPADDR13: + case CSR_PMPADDR14: + case CSR_PMPADDR15: + return pmpaddr_csr_read(env, csrno - CSR_PMPADDR0); +#endif + } + /* used by e.g. MTIME read */ + do_raise_exception_err(env, RH850_EXCP_ILLEGAL_INST, GETPC()); +} + +/* + * Check that CSR access is allowed. + * + * Adapted from Spike's decode.h:validate_csr + */ +//static void validate_csr(CPURH850State *env, uint64_t which, +// uint64_t write, uintptr_t ra) +//{ +//#ifndef CONFIG_USER_ONLY +// unsigned csr_priv = get_field((which), 0x300); +// unsigned csr_read_only = get_field((which), 0xC00) == 3; +// if (((write) && csr_read_only) || (env->priv < csr_priv)) { +// do_raise_exception_err(env, RH850_EXCP_ILLEGAL_INST, ra); +// } +//#endif +//} + +//target_ulong helper_csrrw(CPURH850State *env, target_ulong src, +// target_ulong csr) +//{ +// validate_csr(env, csr, 1, GETPC()); +// uint64_t csr_backup = csr_read_helper(env, csr); +// csr_write_helper(env, src, csr); +// return csr_backup; +//} +// +//target_ulong helper_csrrs(CPURH850State *env, target_ulong src, +// target_ulong csr, target_ulong rs1_pass) +//{ +// validate_csr(env, csr, rs1_pass != 0, GETPC()); +// uint64_t csr_backup = csr_read_helper(env, csr); +// if (rs1_pass != 0) { +// csr_write_helper(env, src | csr_backup, csr); +// } +// return csr_backup; +//} +// +//target_ulong helper_csrrc(CPURH850State *env, target_ulong src, +// target_ulong csr, target_ulong rs1_pass) +//{ +// validate_csr(env, csr, rs1_pass != 0, GETPC()); +// uint64_t csr_backup = csr_read_helper(env, csr); +// if (rs1_pass != 0) { +// csr_write_helper(env, (~src) & csr_backup, csr); +// } +// return csr_backup; +//} + +#ifndef CONFIG_USER_ONLY + +/* iothread_mutex must be held */ +void rh850_set_local_interrupt(RH850CPU *cpu, target_ulong mask, int value) +{ + target_ulong old_mip = cpu->env.mip; + cpu->env.mip = (old_mip & ~mask) | (value ? mask : 0); + + if (cpu->env.mip && !old_mip) { + cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); + } else if (!cpu->env.mip && old_mip) { + cpu_reset_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); + } +} + +void rh850_set_mode(CPURH850State *env, target_ulong newpriv) +{ + if (newpriv > PRV_M) { + g_assert_not_reached(); + } + if (newpriv == PRV_H) { + newpriv = PRV_U; + } + /* tlb_flush is unnecessary as mode is contained in mmu_idx */ + env->priv = newpriv; +} + +//target_ulong helper_sret(CPURH850State *env, target_ulong cpu_pc_deb) +//{ +// if (!(env->priv >= PRV_S)) { +// do_raise_exception_err(env, RH850_EXCP_ILLEGAL_INST, GETPC()); +// } +// +// target_ulong retpc = env->sepc; +// if (!rh850_has_ext(env, RVC) && (retpc & 0x3)) { +// do_raise_exception_err(env, RH850_EXCP_INST_ADDR_MIS, GETPC()); +// } +// +// target_ulong mstatus = env->mstatus; +// target_ulong prev_priv = get_field(mstatus, MSTATUS_SPP); +// mstatus = set_field(mstatus, +// env->priv_ver >= PRIV_VERSION_1_10_0 ? +// MSTATUS_SIE : MSTATUS_UIE << prev_priv, +// get_field(mstatus, MSTATUS_SPIE)); +// mstatus = set_field(mstatus, MSTATUS_SPIE, 0); +// mstatus = set_field(mstatus, MSTATUS_SPP, PRV_U); +// rh850_set_mode(env, prev_priv); +// csr_write_helper(env, mstatus, CSR_MSTATUS); +// +// return retpc; +//} +// +//target_ulong helper_mret(CPURH850State *env, target_ulong cpu_pc_deb) +//{ +// if (!(env->priv >= PRV_M)) { +// do_raise_exception_err(env, RH850_EXCP_ILLEGAL_INST, GETPC()); +// } +// +// target_ulong retpc = env->mepc; +// if (!rh850_has_ext(env, RVC) && (retpc & 0x3)) { +// do_raise_exception_err(env, RH850_EXCP_INST_ADDR_MIS, GETPC()); +// } +// +// target_ulong mstatus = env->mstatus; +// target_ulong prev_priv = get_field(mstatus, MSTATUS_MPP); +// mstatus = set_field(mstatus, +// env->priv_ver >= PRIV_VERSION_1_10_0 ? +// MSTATUS_MIE : MSTATUS_UIE << prev_priv, +// get_field(mstatus, MSTATUS_MPIE)); +// mstatus = set_field(mstatus, MSTATUS_MPIE, 0); +// mstatus = set_field(mstatus, MSTATUS_MPP, PRV_U); +// rh850_set_mode(env, prev_priv); +// csr_write_helper(env, mstatus, CSR_MSTATUS); +// +// return retpc; +//} + + +//void helper_wfi(CPURH850State *env) +//{ +// CPUState *cs = CPU(rh850_env_get_cpu(env)); +// +// cs->halted = 1; +// cs->exception_index = EXCP_HLT; +// cpu_loop_exit(cs); +//} + +void helper_tlb_flush(CPURH850State *env) +{ + RH850CPU *cpu = rh850_env_get_cpu(env); + CPUState *cs = CPU(cpu); + tlb_flush(cs); +} + +#endif /* !CONFIG_USER_ONLY */ diff --git a/qemu/target/rh850/pmp.c b/qemu/target/rh850/pmp.c new file mode 100644 index 0000000000..8f98659d3a --- /dev/null +++ b/qemu/target/rh850/pmp.c @@ -0,0 +1,379 @@ +/* + * QEMU RH850 PMP (Physical Memory Protection) + * + * Author: Daire McNamara, daire.mcnamara@emdalo.com + * Ivan Griffin, ivan.griffin@emdalo.com + * + * This provides a RH850 Physical Memory Protection implementation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/* + * PMP (Physical Memory Protection) is as-of-yet unused and needs testing. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "cpu.h" +#include "qemu-common.h" + +#ifndef CONFIG_USER_ONLY + +#define RH850_DEBUG_PMP 0 +#define PMP_DEBUG(fmt, ...) \ + do { \ + if (RH850_DEBUG_PMP) { \ + qemu_log_mask(LOG_TRACE, "%s: " fmt "\n", __func__, ##__VA_ARGS__);\ + } \ + } while (0) + +static void pmp_write_cfg(CPURH850State *env, uint32_t addr_index, + uint8_t val); +static uint8_t pmp_read_cfg(CPURH850State *env, uint32_t addr_index); +static void pmp_update_rule(CPURH850State *env, uint32_t pmp_index); + +/* + * Accessor method to extract address matching type 'a field' from cfg reg + */ +static inline uint8_t pmp_get_a_field(uint8_t cfg) +{ + uint8_t a = cfg >> 3; + return a & 0x3; +} + +/* + * Check whether a PMP is locked or not. + */ +static inline int pmp_is_locked(CPURH850State *env, uint32_t pmp_index) +{ + + if (env->pmp_state.pmp[pmp_index].cfg_reg & PMP_LOCK) { + return 1; + } + + /* Top PMP has no 'next' to check */ + if ((pmp_index + 1u) >= MAX_RH850_PMPS) { + return 0; + } + + /* In TOR mode, need to check the lock bit of the next pmp + * (if there is a next) + */ + const uint8_t a_field = + pmp_get_a_field(env->pmp_state.pmp[pmp_index + 1].cfg_reg); + if ((env->pmp_state.pmp[pmp_index + 1u].cfg_reg & PMP_LOCK) && + (PMP_AMATCH_TOR == a_field)) { + return 1; + } + + return 0; +} + +/* + * Count the number of active rules. + */ +static inline uint32_t pmp_get_num_rules(CPURH850State *env) +{ + return env->pmp_state.num_rules; +} + +/* + * Accessor to get the cfg reg for a specific PMP/HART + */ +static inline uint8_t pmp_read_cfg(CPURH850State *env, uint32_t pmp_index) +{ + if (pmp_index < MAX_RH850_PMPS) { + return env->pmp_state.pmp[pmp_index].cfg_reg; + } + + return 0; +} + + +/* + * Accessor to set the cfg reg for a specific PMP/HART + * Bounds checks and relevant lock bit. + */ +static void pmp_write_cfg(CPURH850State *env, uint32_t pmp_index, uint8_t val) +{ + if (pmp_index < MAX_RH850_PMPS) { + if (!pmp_is_locked(env, pmp_index)) { + env->pmp_state.pmp[pmp_index].cfg_reg = val; + pmp_update_rule(env, pmp_index); + } else { + PMP_DEBUG("ignoring write - locked"); + } + } else { + PMP_DEBUG("ignoring write - out of bounds"); + } +} + +static void pmp_decode_napot(target_ulong a, target_ulong *sa, target_ulong *ea) +{ + /* + aaaa...aaa0 8-byte NAPOT range + aaaa...aa01 16-byte NAPOT range + aaaa...a011 32-byte NAPOT range + ... + aa01...1111 2^XLEN-byte NAPOT range + a011...1111 2^(XLEN+1)-byte NAPOT range + 0111...1111 2^(XLEN+2)-byte NAPOT range + 1111...1111 Reserved + */ + if (a == -1) { + *sa = 0u; + *ea = -1; + return; + } else { + target_ulong t1 = ctz64(~a); + target_ulong base = (a & ~(((target_ulong)1 << t1) - 1)) << 3; + target_ulong range = ((target_ulong)1 << (t1 + 3)) - 1; + *sa = base; + *ea = base + range; + } +} + + +/* Convert cfg/addr reg values here into simple 'sa' --> start address and 'ea' + * end address values. + * This function is called relatively infrequently whereas the check that + * an address is within a pmp rule is called often, so optimise that one + */ +static void pmp_update_rule(CPURH850State *env, uint32_t pmp_index) +{ + int i; + + env->pmp_state.num_rules = 0; + + uint8_t this_cfg = env->pmp_state.pmp[pmp_index].cfg_reg; + target_ulong this_addr = env->pmp_state.pmp[pmp_index].addr_reg; + target_ulong prev_addr = 0u; + target_ulong sa = 0u; + target_ulong ea = 0u; + + if (pmp_index >= 1u) { + prev_addr = env->pmp_state.pmp[pmp_index - 1].addr_reg; + } + + switch (pmp_get_a_field(this_cfg)) { + case PMP_AMATCH_OFF: + sa = 0u; + ea = -1; + break; + + case PMP_AMATCH_TOR: + sa = prev_addr << 2; /* shift up from [xx:0] to [xx+2:2] */ + ea = (this_addr << 2) - 1u; + break; + + case PMP_AMATCH_NA4: + sa = this_addr << 2; /* shift up from [xx:0] to [xx+2:2] */ + ea = (this_addr + 4u) - 1u; + break; + + case PMP_AMATCH_NAPOT: + pmp_decode_napot(this_addr, &sa, &ea); + break; + + default: + sa = 0u; + ea = 0u; + break; + } + + env->pmp_state.addr[pmp_index].sa = sa; + env->pmp_state.addr[pmp_index].ea = ea; + + for (i = 0; i < MAX_RH850_PMPS; i++) { + const uint8_t a_field = + pmp_get_a_field(env->pmp_state.pmp[i].cfg_reg); + if (PMP_AMATCH_OFF != a_field) { + env->pmp_state.num_rules++; + } + } +} + +static int pmp_is_in_range(CPURH850State *env, int pmp_index, target_ulong addr) +{ + int result = 0; + + if ((addr >= env->pmp_state.addr[pmp_index].sa) + && (addr <= env->pmp_state.addr[pmp_index].ea)) { + result = 1; + } else { + result = 0; + } + + return result; +} + + +/* + * Public Interface + */ + +/* + * Check if the address has required RWX privs to complete desired operation + */ +bool pmp_hart_has_privs(CPURH850State *env, target_ulong addr, + target_ulong size, pmp_priv_t privs) +{ + int i = 0; + int ret = -1; + target_ulong s = 0; + target_ulong e = 0; + pmp_priv_t allowed_privs = 0; + + /* Short cut if no rules */ + if (0 == pmp_get_num_rules(env)) { + return true; + } + + /* 1.10 draft priv spec states there is an implicit order + from low to high */ + for (i = 0; i < MAX_RH850_PMPS; i++) { + s = pmp_is_in_range(env, i, addr); + e = pmp_is_in_range(env, i, addr + size); + + /* partially inside */ + if ((s + e) == 1) { + PMP_DEBUG("pmp violation - access is partially inside"); + ret = 0; + break; + } + + /* fully inside */ + const uint8_t a_field = + pmp_get_a_field(env->pmp_state.pmp[i].cfg_reg); + if ((s + e) == 2) { + if (PMP_AMATCH_OFF == a_field) { + return 1; + } + + allowed_privs = PMP_READ | PMP_WRITE | PMP_EXEC; + if ((env->priv != PRV_M) || pmp_is_locked(env, i)) { + allowed_privs &= env->pmp_state.pmp[i].cfg_reg; + } + + if ((privs & allowed_privs) == privs) { + ret = 1; + break; + } else { + ret = 0; + break; + } + } + } + + /* No rule matched */ + if (ret == -1) { + if (env->priv == PRV_M) { + ret = 1; /* Privileged spec v1.10 states if no PMP entry matches an + * M-Mode access, the access succeeds */ + } else { + ret = 0; /* Other modes are not allowed to succeed if they don't + * match a rule, but there are rules. We've checked for + * no rule earlier in this function. */ + } + } + + return ret == 1 ? true : false; +} + + +/* + * Handle a write to a pmpcfg CSP + */ +void pmpcfg_csr_write(CPURH850State *env, uint32_t reg_index, + target_ulong val) +{ + int i; + uint8_t cfg_val; + + PMP_DEBUG("hart " TARGET_FMT_ld ": reg%d, val: 0x" TARGET_FMT_lx, + env->mhartid, reg_index, val); + + if ((reg_index & 1) && (sizeof(target_ulong) == 8)) { + PMP_DEBUG("ignoring write - incorrect address"); + return; + } + + for (i = 0; i < sizeof(target_ulong); i++) { + cfg_val = (val >> 8 * i) & 0xff; + pmp_write_cfg(env, (reg_index * sizeof(target_ulong)) + i, + cfg_val); + } +} + + +/* + * Handle a read from a pmpcfg CSP + */ +target_ulong pmpcfg_csr_read(CPURH850State *env, uint32_t reg_index) +{ + int i; + target_ulong cfg_val = 0; + uint8_t val = 0; + + for (i = 0; i < sizeof(target_ulong); i++) { + val = pmp_read_cfg(env, (reg_index * sizeof(target_ulong)) + i); + cfg_val |= (val << (i * 8)); + } + + PMP_DEBUG("hart " TARGET_FMT_ld ": reg%d, val: 0x" TARGET_FMT_lx, + env->mhartid, reg_index, cfg_val); + + return cfg_val; +} + + +/* + * Handle a write to a pmpaddr CSP + */ +void pmpaddr_csr_write(CPURH850State *env, uint32_t addr_index, + target_ulong val) +{ + PMP_DEBUG("hart " TARGET_FMT_ld ": addr%d, val: 0x" TARGET_FMT_lx, + env->mhartid, addr_index, val); + + if (addr_index < MAX_RH850_PMPS) { + if (!pmp_is_locked(env, addr_index)) { + env->pmp_state.pmp[addr_index].addr_reg = val; + pmp_update_rule(env, addr_index); + } else { + PMP_DEBUG("ignoring write - locked"); + } + } else { + PMP_DEBUG("ignoring write - out of bounds"); + } +} + + +/* + * Handle a read from a pmpaddr CSP + */ +target_ulong pmpaddr_csr_read(CPURH850State *env, uint32_t addr_index) +{ + PMP_DEBUG("hart " TARGET_FMT_ld ": addr%d, val: 0x" TARGET_FMT_lx, + env->mhartid, addr_index, + env->pmp_state.pmp[addr_index].addr_reg); + if (addr_index < MAX_RH850_PMPS) { + return env->pmp_state.pmp[addr_index].addr_reg; + } else { + PMP_DEBUG("ignoring read - out of bounds"); + return 0; + } +} + +#endif diff --git a/qemu/target/rh850/pmp.h b/qemu/target/rh850/pmp.h new file mode 100644 index 0000000000..e6e43e8241 --- /dev/null +++ b/qemu/target/rh850/pmp.h @@ -0,0 +1,64 @@ +/* + * QEMU RH850 PMP (Physical Memory Protection) + * + * Author: Daire McNamara, daire.mcnamara@emdalo.com + * Ivan Griffin, ivan.griffin@emdalo.com + * + * This provides a RH850 Physical Memory Protection interface + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef _RH850_PMP_H_ +#define _RH850_PMP_H_ + +typedef enum { + PMP_READ = 1 << 0, + PMP_WRITE = 1 << 1, + PMP_EXEC = 1 << 2, + PMP_LOCK = 1 << 7 +} pmp_priv_t; + +typedef enum { + PMP_AMATCH_OFF, /* Null (off) */ + PMP_AMATCH_TOR, /* Top of Range */ + PMP_AMATCH_NA4, /* Naturally aligned four-byte region */ + PMP_AMATCH_NAPOT /* Naturally aligned power-of-two region */ +} pmp_am_t; + +typedef struct { + target_ulong addr_reg; + uint8_t cfg_reg; +} pmp_entry_t; + +typedef struct { + target_ulong sa; + target_ulong ea; +} pmp_addr_t; + +typedef struct { + pmp_entry_t pmp[MAX_RH850_PMPS]; + pmp_addr_t addr[MAX_RH850_PMPS]; + uint32_t num_rules; +} pmp_table_t; + +void pmpcfg_csr_write(CPURH850State *env, uint32_t reg_index, + target_ulong val); +target_ulong pmpcfg_csr_read(CPURH850State *env, uint32_t reg_index); +void pmpaddr_csr_write(CPURH850State *env, uint32_t addr_index, + target_ulong val); +target_ulong pmpaddr_csr_read(CPURH850State *env, uint32_t addr_index); +bool pmp_hart_has_privs(CPURH850State *env, target_ulong addr, + target_ulong size, pmp_priv_t priv); + +#endif diff --git a/qemu/target/rh850/register_indices.h b/qemu/target/rh850/register_indices.h new file mode 100644 index 0000000000..20fcea8cae --- /dev/null +++ b/qemu/target/rh850/register_indices.h @@ -0,0 +1,63 @@ +/* + * register_indices.h + * + * Created on: Jun 18, 2018 + * + */ + +#ifndef TARGET_RH850_REGISTER_INDICES_H_ +#define TARGET_RH850_REGISTER_INDICES_H_ + + +// BANK ID 0, sys basic regs +#define EIPC_IDX 0 +#define EIPSW_IDX 1 +#define FEPC_IDX 2 +#define FEPSW_IDX 3 +#define PSW_IDX 5 //program status word +// sysFpuRegs indices +#define FPSR_IDX 6 //floating-point configuration/status <---write the bit defines +#define FPEPC_IDX 7 //floating point exception PC +#define FPST_IDX 8 +#define FPCC_IDX 9 +#define FPCFG_IDX 10 +#define FPEC_IDX 11 + +#define EIIC_IDX 13 //EI level exception cause +#define FEIC_IDX 14 //FI level exception cause +#define CTPC_IDX 16 +#define CTPSW_IDX 17 +#define CTBP_IDX 20 +#define EIWR_IDX 28 +#define FEWR_IDX 29 +#define BSEL_IDX 31 + +// BANK ID 1, sys basic regs +#define MCFG0_IDX1 0 //machine configuration +#define RBASE_IDX1 2 //reset vector base address (if psw.ebv==0, this is also exception vector) +#define EBASE_IDX1 3 //exception handler vector address +#define INTBP_IDX1 4 +#define MCTL_IDX1 5 //CPU control +#define PID_IDX1 6 //processor ID +#define SCCFG_IDX1 11 // SYSCALL config +#define SCBP_IDX1 12 // SYSCALL base pointer + +// BANK ID 2, sys basic regs +#define HTCFG0_IDX2 0 //thread configuration +#define MEA_IDX2 6 //memory error address (when misaligned or MPU) +#define ASID_IDX2 7 //memory error address (when misaligned or MPU) +#define MEI_IDX2 8 //memory error info (info about instruction that caused exception) + +// BANK ID 1, 2 sysInterruptRegs indices +#define FPIPR_IDX1 7 +#define ISPR_IDX2 10 +#define PMR_IDX2 11 +#define ICSR_IDX2 12 //interrupt control status register +#define INTCFG_IDX2 13 //interrupt function setting + + +// BANK ID 5, 6, 7 system MPU regs indices +#define MPM_IDX5 0 //memory protection operation mode + + +#endif /* TARGET_RH850_REGISTER_INDICES_H_ */ diff --git a/qemu/target/rh850/translate.c b/qemu/target/rh850/translate.c new file mode 100644 index 0000000000..6f23597f3c --- /dev/null +++ b/qemu/target/rh850/translate.c @@ -0,0 +1,5219 @@ +/* + * RH850 emulation for qemu: main translation routines. + * + * Copyright (c) 2018 iSYSTEM Labs d.o.o. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "exec/exec-all.h" +#include "tcg/tcg-op.h" +#include "tcg/tcg-op-gvec.h" +#include "qemu/log.h" +#include "qemu/host-utils.h" +#include "exec/cpu_ldst.h" +#include "exec/gen-icount.h" +#include "exec/helper-proto.h" +#include "exec/helper-gen.h" +#include "exec/translator.h" + +#include "instmap.h" + +/* global register indices */ +static TCGv cpu_gpr[NUM_GP_REGS]; +static TCGv cpu_pc; +static TCGv cpu_sysRegs[NUM_SYS_REG_BANKS][MAX_SYS_REGS_IN_BANK]; +// static TCGv_i64 cpu_fpr[32]; /* assume F and D extensions */ +static TCGv cpu_sysDatabuffRegs[1], cpu_LLbit, cpu_LLAddress; +static TCGv load_res; +static TCGv load_val; + +// PSW register flags. These are for temporary use only during +// calculations. Before usage they should be set from PSW and +// stored back to PSW after changes. +// TODO: since PSW as a register is rarely used - only when ld/str sys reg and +// on some branches (TRAP, ...) it makes sense to compose/decompose PSW +// on these occcasions and not have PSW stored in registers below. +TCGv_i32 cpu_ZF, cpu_SF, cpu_OVF, cpu_CYF, cpu_SATF, cpu_ID, cpu_EP, cpu_NP, + cpu_EBV, cpu_CU0, cpu_CU1, cpu_CU2, cpu_UM; + + +//// system registers indices +//enum{ +// EIPC_IDX = 0, +// EIPSW_register = 1, +// FEPC_register = 2, +// FEPSW_register = 3, +// PSW_register = 4, +// FPSR_register = 5, +// FPEPC_register = 6, +// FPST_register = 7, +// FPCC_register = 8, +// FPCFG_register = 9, +// FPEC_register = 10, +// EIIC_register = 11, +// FEIC_register = 12, +// CTPC_register = 13, +// CTPSW_register = 14, +// CTBP_register = 15, +// EIWR_register = 16, +// FEWR_register = 17, +// BSEL_register = 18, +// MCFG0_register = 19, +// RBASE_register = 20, +// EBASE_register = 21, +// INTBP_register = 22, +// MCTL_register = 23, +// PID_register = 24, +// SCCFG_register = 25, +// SCBP_register = 26, +// HTCFG0_register = 27, +// MEA_register = 28, +// ASID_register = 29, +// MEI_register = 30, +//}; + +/** Const, RH850 does not have MMU. */ +const int MEM_IDX = 0; + +/** + * This structure contains data, which is needed to translate a + * sequence of instructions, usually inside one translation + * block. The most important member is therefore 'pc', which + * points to the instruction to be translated. This variable stores + * PC during compile time (guest instructions to TCG instructions). + * We must increment this variable manually during translation + * according to instruction size. + * Note: Consider renaming to TranslationContext, instead of DisasContext, + * because it contains information for translation, not disassembler. + */ +typedef struct DisasContext { + DisasContextBase base; + CPURH850State *env; + target_ulong pc; // pointer to instruction being translated + uint32_t opcode; + uint32_t opcode1; // used for 48 bit instructions + + // Unicorn + struct uc_struct *uc; +} DisasContext; + +/* is_jmp field values */ +#define DISAS_INDIRECT_JUMP DISAS_TARGET_0 /* only pc was modified dynamically */ +#define DISAS_EXIT_TB DISAS_TARGET_1 /* cpu state was modified dynamically */ +#define DISAS_TB_EXIT_ALREADY_GENERATED DISAS_TARGET_2 + +/* convert rh850 funct3 to qemu memop for load/store */ +/* +static const int tcg_memop_lookup[8] = { + [0 ... 7] = -1, + [0] = MO_UB, + [1] = MO_TEUW, + [2] = MO_TEUL, + [4] = MO_SB, + [5] = MO_TESW, + [6] = MO_TESL, +}; +*/ + + +enum { + V_COND = 0, //OV = 1 + C_COND = 1, //CY = 1 + Z_COND = 2, //Z = 1 + NH_COND = 3, //(CY or Z) = 1 + S_COND = 4, //S = 1 + T_COND = 5, //Always + LT_COND = 6, //(S xor OV) = 1 + LE_COND = 7, //((S xor OV) or Z) = 1 + + NV_COND = 8, //OV = 0 + NC_COND = 9, //CY = 0 + NZ_COND = 10, //Z = 0 + H_COND = 11, //(CY or Z) = 0 + NS_COND = 12, //S = 0 + SA_COND = 13, //SAT = 1 + GE_COND = 14, //(S xor OV) = 0 + GT_COND = 15, //((S xor OV) or Z) = 0 +}; + +//ENUMS FOR CACHE OP +enum { + CHBII = 0x0, + CIBII = 0x20, + CFALI = 0x40, + CISTI = 0x60, + CILDI = 0x61, + CLL = 0x7e, +}; + +enum { + OPC_RH850_BINS = 123456, +}; + +#define CASE_OP_32_64(X) case X +/* +static void generate_exception(DisasContext *ctx, int excp) +{ + tcg_gen_movi_tl(cpu_pc, ctx->pc); + TCGv_i32 helper_tmp = tcg_const_i32(excp); + gen_helper_raise_exception(cpu_env, helper_tmp); + tcg_temp_free_i32(helper_tmp); + ctx->bstate = BS_BRANCH; +} + +*/ + + +static void gen_exception_debug(DisasContext *dc) +{ + TCGContext *tcg_ctx = dc->uc->tcg_ctx; + + TCGv_i32 helper_tmp = tcg_const_i32(tcg_ctx, EXCP_DEBUG); + gen_helper_raise_exception(tcg_ctx, tcg_ctx->cpu_env, helper_tmp); + tcg_temp_free_i32(tcg_ctx, helper_tmp); + + //gen_helper_raise_exception(tcg_ctx, tcg_ctx->cpu_env, EXCP_DEBUG); + dc->base.is_jmp = DISAS_TB_EXIT_ALREADY_GENERATED; +} + + +static void gen_goto_tb_imm(DisasContext *ctx, int n, target_ulong dest) +{ + TCGContext *tcg_ctx = ctx->uc->tcg_ctx; + + if (unlikely(ctx->base.singlestep_enabled)) { + tcg_gen_movi_tl(tcg_ctx, cpu_pc, dest); + gen_exception_debug(ctx); + } else { + tcg_gen_goto_tb(tcg_ctx, n); + tcg_gen_movi_tl(tcg_ctx, cpu_pc, dest); + tcg_gen_exit_tb(tcg_ctx, ctx->base.tb, n); + } +} + +static void gen_goto_tb(DisasContext *ctx, int n, TCGv dest) +{ + TCGContext *tcg_ctx = ctx->uc->tcg_ctx; + + if (unlikely(ctx->base.singlestep_enabled)) { + tcg_gen_mov_tl(tcg_ctx, cpu_pc, dest); + gen_exception_debug(ctx); + } else { + tcg_gen_goto_tb(tcg_ctx, n); + tcg_gen_mov_tl(tcg_ctx, cpu_pc, dest); + tcg_gen_exit_tb(tcg_ctx, ctx->base.tb, n); + } +} + +/* Wrapper for getting reg values - need to check of reg is zero since + * cpu_gpr[0] is not actually allocated + */ +static inline void gen_get_gpr(TCGContext *tcg_ctx, TCGv t, int reg_num) +{ + if (reg_num == 0) { + tcg_gen_movi_tl(tcg_ctx, t, 0); + } else { + tcg_gen_mov_tl(tcg_ctx, t, cpu_gpr[reg_num]); + } + +} + + +/* Selection based on group ID needs to be added, once + * the system register groups are implemented +static inline void gen_get_sysreg(TCGv t, int reg_num) +{ +} +*/ + +/* Wrapper for setting reg values - need to check of reg is zero since + * cpu_gpr[0] is not actually allocated. this is more for safety purposes, + * since we usually avoid calling the OP_TYPE_gen function if we see a write to + * $zero + */ +static inline void gen_set_gpr(TCGContext *tcg_ctx, int reg_num_dst, TCGv t) +{ + if (reg_num_dst != 0) { + tcg_gen_mov_tl(tcg_ctx, cpu_gpr[reg_num_dst], t); + } +} + + +//static inline void gen_set_psw(TCGv t) +//{ +// tcg_gen_mov_tl(cpu_sysRegs[BANK_ID_BASIC_0][PSW_IDX], t); +//} +// +//static inline void gen_get_psw(TCGv t) +//{ +// tcg_gen_mov_tl(t, cpu_sysRegs[BANK_ID_BASIC_0][PSW_IDX]); +//} + + +static inline void tcgv_to_flags(TCGContext *tcg_ctx, TCGv reg) +{ + TCGv temp = tcg_temp_new_i32(tcg_ctx); + tcg_gen_mov_i32(tcg_ctx, temp, reg); + tcg_gen_andi_i32(tcg_ctx, cpu_ZF, temp, 0x1); + tcg_gen_shri_i32(tcg_ctx, temp, temp, 0x1); + tcg_gen_andi_i32(tcg_ctx, cpu_SF, temp, 0x1); + tcg_gen_shri_i32(tcg_ctx, temp, temp, 0x1); + tcg_gen_andi_i32(tcg_ctx, cpu_OVF, temp, 0x1); + tcg_gen_shri_i32(tcg_ctx, temp, temp, 0x1); + tcg_gen_andi_i32(tcg_ctx, cpu_CYF, temp, 0x1); + tcg_gen_shri_i32(tcg_ctx, temp, temp, 0x1); + tcg_gen_andi_i32(tcg_ctx, cpu_SATF, temp, 0x1); + + tcg_gen_shri_i32(tcg_ctx, temp, temp, 0x1); + tcg_gen_andi_i32(tcg_ctx, cpu_ID, temp, 0x1); + + tcg_gen_shri_i32(tcg_ctx, temp, temp, 0x1); + tcg_gen_andi_i32(tcg_ctx, cpu_EP, temp, 0x1); + + tcg_gen_shri_i32(tcg_ctx, temp, temp, 0x1); + tcg_gen_andi_i32(tcg_ctx, cpu_NP, temp, 0x1); + + tcg_gen_shri_i32(tcg_ctx, temp, temp, 0x8); + tcg_gen_andi_i32(tcg_ctx, cpu_EBV, temp, 0x1); + + tcg_gen_shri_i32(tcg_ctx, temp, temp, 0x1); + tcg_gen_andi_i32(tcg_ctx, cpu_CU0, temp, 0x1); + + tcg_gen_shri_i32(tcg_ctx, temp, temp, 0x1); + tcg_gen_andi_i32(tcg_ctx, cpu_CU1, temp, 0x1); + + tcg_gen_shri_i32(tcg_ctx, temp, temp, 0x1); + tcg_gen_andi_i32(tcg_ctx, cpu_CU2, temp, 0x1); + + tcg_gen_shri_i32(tcg_ctx, temp, temp, 0x12); + tcg_gen_andi_i32(tcg_ctx, cpu_UM, temp, 0x1); + + tcg_temp_free(tcg_ctx, temp); +} + + +//static void psw_to_flags_z_cy_ov_s_sat(void) +//{ +// TCGv temp = tcg_temp_new_i32(); +// tcg_gen_mov_i32(temp, cpu_sysRegs[BANK_ID_BASIC_0][PSW_IDX]); +// tcg_gen_andi_i32(cpu_ZF, temp, 0x1); +// tcg_gen_shri_i32(temp, temp, 0x1); +// tcg_gen_andi_i32(cpu_SF, temp, 0x1); +// tcg_gen_shri_i32(temp, temp, 0x1); +// tcg_gen_andi_i32(cpu_OVF, temp, 0x1); +// tcg_gen_shri_i32(temp, temp, 0x1); +// tcg_gen_andi_i32(cpu_CYF, temp, 0x1); +// tcg_gen_shri_i32(temp, temp, 0x1); +// tcg_gen_andi_i32(cpu_SATF, temp, 0x1); +// tcg_temp_free(temp); +//} + + +static void tcgv_to_flags_z_cy_ov_s_sat(TCGContext *tcg_ctx, TCGv reg) +{ + TCGv temp = tcg_temp_new_i32(tcg_ctx); + tcg_gen_mov_i32(tcg_ctx, temp, reg); + tcg_gen_andi_i32(tcg_ctx, cpu_ZF, temp, 0x1); + tcg_gen_shri_i32(tcg_ctx, temp, temp, 0x1); + tcg_gen_andi_i32(tcg_ctx, cpu_SF, temp, 0x1); + tcg_gen_shri_i32(tcg_ctx, temp, temp, 0x1); + tcg_gen_andi_i32(tcg_ctx, cpu_OVF, temp, 0x1); + tcg_gen_shri_i32(tcg_ctx, temp, temp, 0x1); + tcg_gen_andi_i32(tcg_ctx, cpu_CYF, temp, 0x1); + tcg_gen_shri_i32(tcg_ctx, temp, temp, 0x1); + tcg_gen_andi_i32(tcg_ctx, cpu_SATF, temp, 0x1); + tcg_temp_free(tcg_ctx, temp); +} +//static void psw_to_flags_ov(void) +//{ +// TCGv temp = tcg_temp_new_i32(); +// tcg_gen_mov_i32(temp, cpu_sysRegs[BANK_ID_BASIC_0][PSW_IDX]); +// tcg_gen_shri_i32(temp, temp, 0x2); +// tcg_gen_andi_i32(cpu_OVF, temp, 0x1); +//} + + +//static void psw_to_flags_ebv(void) +//{ +// TCGv temp = tcg_temp_new_i32(); +// tcg_gen_mov_i32(temp, cpu_sysRegs[BANK_ID_BASIC_0][PSW_IDX]); +// tcg_gen_shri_i32(temp, temp, 15); +// tcg_gen_andi_i32(cpu_EBV, temp, 1); +// tcg_temp_free(temp); +//} + + +static void flags_to_tcgv_id_ep_np_ebv_cu_um(TCGContext *tcg_ctx, TCGv reg) +{ + // Set flags in PSW to 0 so we can OR with new state + tcg_gen_andi_i32(tcg_ctx, reg, reg, 0xbff87f1f); + + TCGv temp = tcg_temp_new_i32(tcg_ctx); + + tcg_gen_shli_i32(tcg_ctx, temp, cpu_ID, 0x5); + tcg_gen_or_i32(tcg_ctx, reg, reg,temp); + + tcg_gen_shli_i32(tcg_ctx, temp, cpu_EP, 0x6); + tcg_gen_or_i32(tcg_ctx, reg, reg,temp); + + tcg_gen_shli_i32(tcg_ctx, temp, cpu_NP, 0x7); + tcg_gen_or_i32(tcg_ctx, reg, reg,temp); + + tcg_gen_shli_i32(tcg_ctx, temp, cpu_EBV, 0xF); + tcg_gen_or_i32(tcg_ctx, reg, reg,temp); + + tcg_gen_shli_i32(tcg_ctx, temp, cpu_CU0, 0x10); + tcg_gen_or_i32(tcg_ctx, reg, reg,temp); + + tcg_gen_shli_i32(tcg_ctx, temp, cpu_CU1, 0x11); + tcg_gen_or_i32(tcg_ctx, reg, reg,temp); + + tcg_gen_shli_i32(tcg_ctx, temp, cpu_CU2, 0x12); + tcg_gen_or_i32(tcg_ctx, reg, reg,temp); + + tcg_gen_shli_i32(tcg_ctx, temp, cpu_UM, 0x1E); + tcg_gen_or_i32(tcg_ctx, reg, reg,temp); + + tcg_temp_free(tcg_ctx, temp); +} + + +static void flags_to_tcgv_z_cy_ov_s_sat(TCGContext *tcg_ctx, TCGv reg) +{ + // update psw register, first reset flags before ORing new values + tcg_gen_andi_i32(tcg_ctx, reg, reg, 0xffffffe0); + TCGv temp = tcg_temp_new_i32(tcg_ctx); + tcg_gen_or_i32(tcg_ctx, reg, reg, cpu_ZF); + tcg_gen_shli_i32(tcg_ctx, temp, cpu_SF, 0x1); + tcg_gen_or_i32(tcg_ctx, reg,reg,temp); + tcg_gen_shli_i32(tcg_ctx, temp, cpu_OVF, 0x2); + tcg_gen_or_i32(tcg_ctx, reg,reg,temp); + tcg_gen_shli_i32(tcg_ctx, temp, cpu_CYF, 0x3); + tcg_gen_or_i32(tcg_ctx, reg,reg,temp); + tcg_gen_shli_i32(tcg_ctx, temp, cpu_SATF, 0x4); + tcg_gen_or_i32(tcg_ctx, reg,reg,temp); + tcg_temp_free(tcg_ctx, temp); +} + + +static void flags_to_tcgv(TCGContext *tcg_ctx, TCGv reg) +{ + flags_to_tcgv_z_cy_ov_s_sat(tcg_ctx, reg); + flags_to_tcgv_id_ep_np_ebv_cu_um(tcg_ctx, reg); +} + + +static TCGv condition_satisfied(TCGContext *tcg_ctx, int cond) +{ + TCGv condResult = tcg_temp_new_i32(tcg_ctx); + tcg_gen_movi_i32(tcg_ctx, condResult, 0x0); + // psw_to_flags_z_cy_ov_s_sat(); + + switch(cond) { + case GE_COND: + tcg_gen_xor_i32(tcg_ctx, condResult, cpu_SF, cpu_OVF); + tcg_gen_not_i32(tcg_ctx, condResult, condResult); + tcg_gen_andi_i32(tcg_ctx, condResult, condResult, 0x1); + break; + case GT_COND: + tcg_gen_xor_i32(tcg_ctx, condResult, cpu_SF, cpu_OVF); + tcg_gen_or_i32(tcg_ctx, condResult, condResult, cpu_ZF); + tcg_gen_not_i32(tcg_ctx, condResult, condResult); + tcg_gen_andi_i32(tcg_ctx, condResult, condResult, 0x1); + break; + case LE_COND: + tcg_gen_xor_i32(tcg_ctx, condResult, cpu_SF, cpu_OVF); + tcg_gen_or_i32(tcg_ctx, condResult, condResult, cpu_ZF); + break; + case LT_COND: + tcg_gen_xor_i32(tcg_ctx, condResult, cpu_SF, cpu_OVF); + break; + + case H_COND: + tcg_gen_or_i32(tcg_ctx, condResult, cpu_CYF, cpu_ZF); + tcg_gen_not_i32(tcg_ctx, condResult, condResult); + tcg_gen_andi_i32(tcg_ctx, condResult, condResult, 0x1); + break; + case NH_COND: + tcg_gen_or_i32(tcg_ctx, condResult, cpu_CYF, cpu_ZF); + break; + + case NS_COND: + tcg_gen_not_i32(tcg_ctx, condResult, cpu_SF); + tcg_gen_andi_i32(tcg_ctx, condResult, condResult, 0x1); + break; + + case S_COND: + tcg_gen_mov_i32(tcg_ctx, condResult, cpu_SF); + break; + + case C_COND: + tcg_gen_mov_i32(tcg_ctx, condResult, cpu_CYF); + break; + + case NC_COND: + tcg_gen_not_i32(tcg_ctx, condResult, cpu_CYF); + tcg_gen_andi_i32(tcg_ctx, condResult, condResult, 0x1); + break; + case NV_COND: + tcg_gen_not_i32(tcg_ctx, condResult, cpu_OVF); + tcg_gen_andi_i32(tcg_ctx, condResult, condResult, 0x1); + break; + case NZ_COND: + tcg_gen_not_i32(tcg_ctx, condResult, cpu_ZF); + tcg_gen_andi_i32(tcg_ctx, condResult, condResult, 0x1); + break; + + case SA_COND: + tcg_gen_mov_i32(tcg_ctx, condResult, cpu_SATF); + break; + case T_COND: + tcg_gen_movi_i32(tcg_ctx, condResult, 0x1); + break; + case V_COND: + tcg_gen_mov_i32(tcg_ctx, condResult, cpu_OVF); + break; + case Z_COND: + tcg_gen_mov_i32(tcg_ctx, condResult, cpu_ZF); + break; + } + + return condResult; +} + +static void gen_flags_on_add(TCGContext *tcg_ctx, TCGv_i32 t0, TCGv_i32 t1) +{ + TCGLabel *cont; + TCGLabel *end; + + TCGv_i32 tmp = tcg_temp_new_i32(tcg_ctx); + tcg_gen_movi_i32(tcg_ctx, tmp, 0); + // 'add2(rl, rh, al, ah, bl, bh) creates 64-bit values and adds them: + // [CYF : SF] = [tmp : t0] + [tmp : t1] + // While CYF is 0 or 1, SF bit 15 contains sign, so it + // must be shifted 31 bits to the right later. + tcg_gen_add2_i32(tcg_ctx, cpu_SF, cpu_CYF, t0, tmp, t1, tmp); + tcg_gen_mov_i32(tcg_ctx, cpu_ZF, cpu_SF); + + tcg_gen_xor_i32(tcg_ctx, cpu_OVF, cpu_SF, t0); + tcg_gen_xor_i32(tcg_ctx, tmp, t0, t1); + tcg_gen_andc_i32(tcg_ctx, cpu_OVF, cpu_OVF, tmp); + + tcg_gen_shri_i32(tcg_ctx, cpu_SF, cpu_SF, 0x1f); + tcg_gen_shri_i32(tcg_ctx, cpu_OVF, cpu_OVF, 0x1f); + + tcg_temp_free_i32(tcg_ctx, tmp); + + cont = gen_new_label(tcg_ctx); + end = gen_new_label(tcg_ctx); + + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_NE, cpu_ZF, 0x0, cont); + tcg_gen_movi_i32(tcg_ctx, cpu_ZF, 0x1); + tcg_gen_br(tcg_ctx, end); + + gen_set_label(tcg_ctx, cont); + tcg_gen_movi_i32(tcg_ctx, cpu_ZF, 0x0); + + gen_set_label(tcg_ctx, end); +} + + +static void gen_satadd_CC(TCGContext *tcg_ctx, TCGv_i32 t0, TCGv_i32 t1, TCGv_i32 result) +{ + TCGLabel *cont; + TCGLabel *end; + + TCGv_i32 tmp = tcg_temp_new_i32(tcg_ctx); + tcg_gen_movi_i32(tcg_ctx, tmp, 0); + tcg_gen_add2_i32(tcg_ctx, cpu_SF, cpu_CYF, t0, tmp, t1, tmp); + tcg_gen_mov_i32(tcg_ctx, cpu_ZF, cpu_SF); + tcg_gen_xor_i32(tcg_ctx, cpu_OVF, cpu_SF, t0); + tcg_gen_xor_i32(tcg_ctx, tmp, t0, t1); + tcg_gen_andc_i32(tcg_ctx, cpu_OVF, cpu_OVF, tmp); + + tcg_gen_shri_i32(tcg_ctx, cpu_SF, result, 0x1f); + tcg_gen_shri_i32(tcg_ctx, cpu_OVF, cpu_OVF, 0x1f); + tcg_temp_free_i32(tcg_ctx, tmp); + + cont = gen_new_label(tcg_ctx); + end = gen_new_label(tcg_ctx); + + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_NE, cpu_ZF, 0x0, cont); + tcg_gen_movi_i32(tcg_ctx, cpu_ZF, 0x1); + tcg_gen_br(tcg_ctx, end); + + gen_set_label(tcg_ctx, cont); + tcg_gen_movi_i32(tcg_ctx, cpu_ZF, 0x0); + + gen_set_label(tcg_ctx, end); +} + +static void gen_flags_on_sub(TCGContext *tcg_ctx, TCGv_i32 t0, TCGv_i32 t1) +{ + tcg_gen_sub_tl(tcg_ctx, cpu_SF, t0, t1); + tcg_gen_setcond_i32(tcg_ctx, TCG_COND_GTU, cpu_CYF, t1, t0); + tcg_gen_setcond_i32(tcg_ctx, TCG_COND_EQ, cpu_ZF, t0, t1); + tcg_gen_xor_i32(tcg_ctx, cpu_OVF, cpu_SF, t0); + TCGv_i32 tmp = tcg_temp_new_i32(tcg_ctx); + tcg_gen_xor_i32(tcg_ctx, tmp, t0, t1); + tcg_gen_and_i32(tcg_ctx, cpu_OVF, cpu_OVF, tmp); + + tcg_gen_shri_i32(tcg_ctx, cpu_SF, cpu_SF, 0x1f); + tcg_gen_shri_i32(tcg_ctx, cpu_OVF, cpu_OVF, 0x1f); + tcg_temp_free_i32(tcg_ctx, tmp); +} + +static void gen_satsub_CC(TCGContext *tcg_ctx, TCGv_i32 t0, TCGv_i32 t1, TCGv_i32 result) +{ + TCGLabel *cont; + TCGLabel *end; + + TCGv_i32 tmp; + tcg_gen_sub_tl(tcg_ctx, cpu_SF, t0, t1); + + tcg_gen_mov_i32(tcg_ctx, cpu_ZF, cpu_SF); + tcg_gen_setcond_i32(tcg_ctx, TCG_COND_GTU, cpu_CYF, t1, t0); + tcg_gen_xor_i32(tcg_ctx, cpu_OVF, cpu_SF, t0); + tmp = tcg_temp_new_i32(tcg_ctx); + tcg_gen_xor_i32(tcg_ctx, tmp, t0, t1); + tcg_gen_and_i32(tcg_ctx, cpu_OVF, cpu_OVF, tmp); + + tcg_gen_shri_i32(tcg_ctx, cpu_SF, result, 0x1f); + tcg_gen_shri_i32(tcg_ctx, cpu_OVF, cpu_OVF, 0x1f); + tcg_temp_free_i32(tcg_ctx, tmp); + + cont = gen_new_label(tcg_ctx); + end = gen_new_label(tcg_ctx); + + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_NE, cpu_ZF, 0x0, cont); + tcg_gen_movi_i32(tcg_ctx, cpu_ZF, 0x1); + tcg_gen_br(tcg_ctx, end); + + gen_set_label(tcg_ctx, cont); + tcg_gen_movi_i32(tcg_ctx, cpu_ZF, 0x0); + + gen_set_label(tcg_ctx, end); +} + +static void gen_logic_CC(TCGContext *tcg_ctx, TCGv_i32 result){ + + TCGLabel *cont; + TCGLabel *end; + + tcg_gen_movi_i32(tcg_ctx, cpu_OVF, 0x0); + tcg_gen_shri_i32(tcg_ctx, cpu_SF, result, 0x1f); + + cont = gen_new_label(tcg_ctx); + end = gen_new_label(tcg_ctx); + + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_NE, result, 0x0, cont); + tcg_gen_movi_i32(tcg_ctx, cpu_ZF, 0x1); + tcg_gen_br(tcg_ctx, end); + + gen_set_label(tcg_ctx, cont); + tcg_gen_movi_i32(tcg_ctx, cpu_ZF, 0x0); + + gen_set_label(tcg_ctx, end); +} + +/* + MO_UB => 8 unsigned + MO_SB => 8 signed + MO_TEUW => 16 unsigned + MO_TESW => 16 signed + MO_TEUL => 32 unsigned + MO_TESL => 32 signed + MO_TEQ => 64 +*/ + +static void gen_load(DisasContext *ctx, int memop, int rd, int rs1, + target_long imm, unsigned is_disp23) +{ + TCGContext *tcg_ctx = ctx->uc->tcg_ctx; + TCGv t0 = tcg_temp_new(tcg_ctx); + TCGv t1 = tcg_temp_new(tcg_ctx); + TCGv tcg_imm = tcg_temp_new(tcg_ctx); + TCGv_i64 t1_64 = tcg_temp_new_i64(tcg_ctx); + TCGv t1_high = tcg_temp_new(tcg_ctx); + + gen_get_gpr(tcg_ctx, t0, rs1); + tcg_gen_movi_i32(tcg_ctx, tcg_imm, imm); + + if (!is_disp23) + tcg_gen_ext16s_i32(tcg_ctx, tcg_imm, tcg_imm); + else { + tcg_gen_shli_i32(tcg_ctx, tcg_imm, tcg_imm, 9); + tcg_gen_sari_i32(tcg_ctx, tcg_imm, tcg_imm, 9); + } + + tcg_gen_add_tl(tcg_ctx, t0, t0, tcg_imm); + + if (memop == MO_TEQ) { + tcg_gen_qemu_ld_i64(tcg_ctx, t1_64, t0, MEM_IDX, memop); + tcg_gen_extrl_i64_i32(tcg_ctx, t1, t1_64); + tcg_gen_extrh_i64_i32(tcg_ctx, t1_high, t1_64); + gen_set_gpr(tcg_ctx, rd, t1); + gen_set_gpr(tcg_ctx, rd+1, t1_high); + } + else { + tcg_gen_qemu_ld_tl(tcg_ctx, t1, t0, MEM_IDX, memop); + gen_set_gpr(tcg_ctx, rd, t1); + } + + tcg_temp_free(tcg_ctx, t0); + tcg_temp_free(tcg_ctx, t1); + tcg_temp_free(tcg_ctx, tcg_imm); + tcg_temp_free_i64(tcg_ctx, t1_64); + tcg_temp_free(tcg_ctx, t1_high); +} + +static void gen_store(DisasContext *ctx, int memop, int rs1, int rs2, + target_long imm, unsigned is_disp23) +{ + TCGContext *tcg_ctx = ctx->uc->tcg_ctx; + TCGv t0 = tcg_temp_new(tcg_ctx); + TCGv dat = tcg_temp_new(tcg_ctx); + TCGv tcg_imm = tcg_temp_new(tcg_ctx); + TCGv dat_high = tcg_temp_new(tcg_ctx); + TCGv_i64 dat64 = tcg_temp_new_i64(tcg_ctx); + + gen_get_gpr(tcg_ctx, t0, rs1); // loading rs1 to t0 + tcg_gen_movi_i32(tcg_ctx, tcg_imm, imm); + + if (!is_disp23) + tcg_gen_ext16s_i32(tcg_ctx, tcg_imm, tcg_imm); + else { + tcg_gen_shli_i32(tcg_ctx, tcg_imm, tcg_imm, 9); + tcg_gen_sari_i32(tcg_ctx, tcg_imm, tcg_imm, 9); + } + + tcg_gen_add_tl(tcg_ctx, t0, t0, tcg_imm); // adding displacement to t0 + + gen_get_gpr(tcg_ctx, dat, rs2); // getting data from rs2 + + if (memop == MO_TEQ) { + gen_get_gpr(tcg_ctx, dat_high, rs2+1); + tcg_gen_concat_i32_i64(tcg_ctx, dat64, dat, dat_high); + tcg_gen_qemu_st_i64(tcg_ctx, dat64, t0, MEM_IDX, memop); + } + else { + tcg_gen_qemu_st_tl(tcg_ctx, dat, t0, MEM_IDX, memop); + } + + // clear possible mutex + TCGLabel *l = gen_new_label(tcg_ctx); + tcg_gen_brcond_i32(tcg_ctx, TCG_COND_NE, t0, cpu_LLAddress, l); + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_NE, cpu_LLbit, 0x1, l); + tcg_gen_movi_i32(tcg_ctx, cpu_LLbit, 0); + gen_set_label(tcg_ctx, l); + + tcg_temp_free(tcg_ctx, t0); + tcg_temp_free(tcg_ctx, dat); + tcg_temp_free(tcg_ctx, tcg_imm); + tcg_temp_free_i64(tcg_ctx, dat64); + tcg_temp_free(tcg_ctx, dat_high); +} + +static void gen_mutual_exclusion(DisasContext *ctx, int rs3, int rs1, int operation) +{ + /* LDL.W, STC.W, CLL: Implement as described. + Add two additional global CPU registers called LLBit and LLAddress. + Set them with LDL.W, and reset them with STC.W. + If LLBit is not set or LLAddress does not match STC.W address, make STC.W fail. + CLL clears LLBit. + Since we do not implement multicore CPU emulation, this implementation should be OK. */ + TCGContext *tcg_ctx = ctx->uc->tcg_ctx; + + if (operation == operation_LDL_W) + { + TCGv adr = tcg_temp_new(tcg_ctx); + TCGv dat = tcg_temp_new(tcg_ctx); + + gen_get_gpr(tcg_ctx, adr, rs1); + tcg_gen_qemu_ld_tl(tcg_ctx, dat, adr, MEM_IDX, MO_TESL); + gen_set_gpr(tcg_ctx, rs3, dat); + + tcg_temp_free(tcg_ctx, adr); + tcg_temp_free(tcg_ctx, dat); + + tcg_gen_movi_i32(tcg_ctx, cpu_LLbit, 1); + tcg_gen_mov_i32(tcg_ctx, cpu_LLAddress, adr); + } + else if (operation == operation_STC_W) + { + TCGv adr = tcg_temp_local_new(tcg_ctx); + TCGv dat = tcg_temp_local_new(tcg_ctx); + TCGv token = tcg_temp_local_new(tcg_ctx); + TCGLabel *l_fail = gen_new_label(tcg_ctx); + TCGLabel *l_ok = gen_new_label(tcg_ctx); + + tcg_gen_mov_i32(tcg_ctx, token, cpu_LLbit); + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_NE, token, 0x1, l_fail); + gen_get_gpr(tcg_ctx, adr, rs1); + gen_get_gpr(tcg_ctx, dat, rs3); + tcg_gen_brcond_i32(tcg_ctx, TCG_COND_NE, adr, cpu_LLAddress, l_fail); + tcg_gen_qemu_st_tl(tcg_ctx, dat, adr, MEM_IDX, MO_TESL); + tcg_gen_movi_i32(tcg_ctx, dat, 1); + tcg_gen_br(tcg_ctx, l_ok); + + gen_set_label(tcg_ctx, l_fail); + tcg_gen_movi_i32(tcg_ctx, dat, 0); + gen_set_label(tcg_ctx, l_ok); + gen_set_gpr(tcg_ctx, rs3, dat); + + tcg_gen_movi_tl(tcg_ctx, cpu_LLbit, 0); + + tcg_temp_free(tcg_ctx, adr); + tcg_temp_free(tcg_ctx, dat); + tcg_temp_free(tcg_ctx, token); + } + else if (operation == operation_CLL) + { + tcg_gen_movi_i32(tcg_ctx, cpu_LLbit, 0); + } + else + printf("ERROR gen_mutual_exclusion \n"); +} + + +static void gen_multiply(DisasContext *ctx, int rs1, int rs2, int operation) +{ + TCGContext *tcg_ctx = ctx->uc->tcg_ctx; + + TCGv r1 = tcg_temp_new(tcg_ctx); //temp + TCGv r2 = tcg_temp_new(tcg_ctx); //temp + + gen_get_gpr(tcg_ctx, r1, rs1); //loading rs1 to t0 + gen_get_gpr(tcg_ctx, r2, rs2); //loading rs2 to t1 + int imm = rs1; + int imm_32; + int int_rs3; + + TCGv tcg_imm = tcg_temp_new(tcg_ctx); + TCGv tcg_imm32 = tcg_temp_new(tcg_ctx); + TCGv tcg_r3 = tcg_temp_new(tcg_ctx); + TCGv tcg_temp = tcg_temp_new(tcg_ctx); + + switch(operation){ + case OPC_RH850_MUL_reg1_reg2_reg3: + int_rs3 = extract32(ctx->opcode, 27, 5); + gen_get_gpr(tcg_ctx, tcg_r3,int_rs3); + + tcg_gen_muls2_i32(tcg_ctx, r2, tcg_r3, r1, r2); + if(rs2!=int_rs3){ + gen_set_gpr(tcg_ctx, rs2, r2); + } + gen_set_gpr(tcg_ctx, int_rs3,tcg_r3); + break; + + case OPC_RH850_MUL_imm9_reg2_reg3: + int_rs3 = extract32(ctx->opcode, 27, 5); + gen_get_gpr(tcg_ctx, tcg_r3,int_rs3); + + imm_32 = extract32(ctx->opcode, 18, 4); + imm_32 = imm | (imm_32 << 5); + + // sign extension + if((imm_32 & 0x100) == 0x100){ + imm_32 = imm_32 | (0x7f << 9); + } + tcg_gen_movi_tl(tcg_ctx, tcg_imm32, imm_32); + tcg_gen_ext16s_tl(tcg_ctx, tcg_imm32, tcg_imm32); + + tcg_gen_muls2_i32(tcg_ctx, r2, tcg_r3, tcg_imm32, r2); + + if(rs2!=int_rs3){ + gen_set_gpr(tcg_ctx, rs2, r2); + } + gen_set_gpr(tcg_ctx, int_rs3, tcg_r3); + break; + + case OPC_RH850_MULH_reg1_reg2: + + tcg_gen_andi_tl(tcg_ctx, r1, r1,0x0000FFFF); + tcg_gen_andi_tl(tcg_ctx, r2, r2,0x0000FFFF); + tcg_gen_ext16s_i32(tcg_ctx, r1, r1); + tcg_gen_ext16s_i32(tcg_ctx, r2, r2); + + tcg_gen_mul_tl(tcg_ctx, r2, r2, r1); + gen_set_gpr(tcg_ctx, rs2, r2); + break; + + case OPC_RH850_MULH_imm5_reg2: + + if ((imm & 0x10) == 0x10){ + imm = imm | (0x7 << 5); + } + tcg_gen_andi_tl(tcg_ctx, r2, r2,0x0000FFFF); + tcg_gen_ext16s_i32(tcg_ctx, r2, r2); + + tcg_gen_movi_tl(tcg_ctx, tcg_imm, imm); + tcg_gen_ext8s_i32(tcg_ctx, tcg_imm, tcg_imm); + tcg_gen_mul_tl(tcg_ctx, r2, r2, tcg_imm); + gen_set_gpr(tcg_ctx, rs2, r2); + break; + + case OPC_RH850_MULHI_imm16_reg1_reg2: + + imm_32 = extract32(ctx->opcode, 16, 16); + tcg_gen_movi_tl(tcg_ctx, tcg_imm32, imm_32); + tcg_gen_ext16s_i32(tcg_ctx, tcg_imm32, tcg_imm32); + + tcg_gen_andi_tl(tcg_ctx, r1, r1, 0x0000FFFF); + tcg_gen_ext16s_i32(tcg_ctx, r1, r1); + + tcg_gen_mul_tl(tcg_ctx, r2, r1, tcg_imm32); + + gen_set_gpr(tcg_ctx, rs2, r2); + break; + + case OPC_RH850_MULU_reg1_reg2_reg3: + + int_rs3 = extract32(ctx->opcode, 27, 5); + gen_get_gpr(tcg_ctx, tcg_r3,int_rs3); + + tcg_gen_mulu2_i32(tcg_ctx, r2, tcg_r3, r2, r1); + + if(rs2!=int_rs3){ + gen_set_gpr(tcg_ctx, rs2, r2); + } + gen_set_gpr(tcg_ctx, int_rs3,tcg_r3); + break; + + case OPC_RH850_MULU_imm9_reg2_reg3: + + int_rs3 = extract32(ctx->opcode, 27, 5); + gen_get_gpr(tcg_ctx, tcg_r3,int_rs3); + + imm_32 = extract32(ctx->opcode, 18, 4); + imm_32 = imm | (imm_32 << 5); + tcg_gen_movi_tl(tcg_ctx, tcg_imm32, imm_32); + + tcg_gen_ext16u_tl(tcg_ctx, tcg_imm32, tcg_imm32); + + tcg_gen_mulu2_i32(tcg_ctx, r2, tcg_r3, tcg_imm32, r2); + + if(rs2!=int_rs3){ + gen_set_gpr(tcg_ctx, rs2, r2); + } + gen_set_gpr(tcg_ctx, int_rs3,tcg_r3); + break; + } + + tcg_temp_free(tcg_ctx, r1); + tcg_temp_free(tcg_ctx, r2); + tcg_temp_free(tcg_ctx, tcg_r3); + tcg_temp_free(tcg_ctx, tcg_temp); + tcg_temp_free(tcg_ctx, tcg_imm); + tcg_temp_free(tcg_ctx, tcg_imm32); +} + +static void gen_mul_accumulate(DisasContext *ctx, int rs1, int rs2, int operation) +{ + TCGContext *tcg_ctx = ctx->uc->tcg_ctx; + + TCGv r1 = tcg_temp_new(tcg_ctx); + TCGv r2 = tcg_temp_new(tcg_ctx); + TCGv addLo = tcg_temp_new(tcg_ctx); + TCGv addHi = tcg_temp_new(tcg_ctx); + TCGv resLo = tcg_temp_new(tcg_ctx); + TCGv resHi = tcg_temp_new(tcg_ctx); + TCGv destLo = tcg_temp_new(tcg_ctx); + TCGv destHi = tcg_temp_new(tcg_ctx); + + gen_get_gpr(tcg_ctx, r1, rs1); + gen_get_gpr(tcg_ctx, r2, rs2); + + int rs3; + int rs4; + + rs3 = extract32(ctx->opcode, 28, 4) << 1; + rs4 = extract32(ctx->opcode, 17, 4) << 1; + + gen_get_gpr(tcg_ctx, addLo, rs3); + gen_get_gpr(tcg_ctx, addHi, rs3+1); + + switch(operation){ + case OPC_RH850_MAC_reg1_reg2_reg3_reg4: + + tcg_gen_muls2_i32(tcg_ctx, resLo, resHi, r1, r2); + tcg_gen_add2_i32(tcg_ctx, destLo, destHi, resLo, resHi, addLo, addHi); + + gen_set_gpr(tcg_ctx, rs4, destLo); + gen_set_gpr(tcg_ctx, rs4+1, destHi); + break; + + case OPC_RH850_MACU_reg1_reg2_reg3_reg4: + tcg_gen_mulu2_i32(tcg_ctx, resLo, resHi, r1, r2); + tcg_gen_add2_i32(tcg_ctx, destLo, destHi, resLo, resHi, addLo, addHi); + + gen_set_gpr(tcg_ctx, rs4, destLo); + gen_set_gpr(tcg_ctx, (rs4+1), destHi); + break; + } + + tcg_temp_free(tcg_ctx, r1); + tcg_temp_free(tcg_ctx, r2); + tcg_temp_free(tcg_ctx, addLo); + tcg_temp_free(tcg_ctx, addHi); + tcg_temp_free(tcg_ctx, resLo); + tcg_temp_free(tcg_ctx, resHi); + tcg_temp_free(tcg_ctx, destLo); + tcg_temp_free(tcg_ctx, destHi); + +} + +static void gen_arithmetic(DisasContext *ctx, int rs1, int rs2, int operation) +{ + TCGContext *tcg_ctx = ctx->uc->tcg_ctx; + + TCGv r1 = tcg_temp_new(tcg_ctx); + TCGv r2 = tcg_temp_new(tcg_ctx); + gen_get_gpr(tcg_ctx, r1, rs1); + gen_get_gpr(tcg_ctx, r2, rs2); + + int imm = rs1; + int imm_32; + uint64_t opcode48; + + TCGv tcg_imm = tcg_temp_new(tcg_ctx); + TCGv tcg_r3 = tcg_temp_new(tcg_ctx); + TCGv tcg_result = tcg_temp_new(tcg_ctx); + + switch(operation) { + + case OPC_RH850_ADD_reg1_reg2: { + + tcg_gen_add_tl(tcg_ctx, tcg_result, r2, r1); + gen_set_gpr(tcg_ctx, rs2, tcg_result); + + gen_flags_on_add(tcg_ctx, r1, r2); + + } break; + + case OPC_RH850_ADD_imm5_reg2: + if((imm & 0x10) == 0x10){ + imm = imm | (0x7 << 5); + } + tcg_gen_movi_i32(tcg_ctx, tcg_imm, imm); + tcg_gen_ext8s_i32(tcg_ctx, tcg_imm, tcg_imm); + tcg_gen_add_tl(tcg_ctx, tcg_result, r2, tcg_imm); + gen_set_gpr(tcg_ctx, rs2, tcg_result); + + gen_flags_on_add(tcg_ctx, r2, tcg_imm); + + break; + + case OPC_RH850_ADDI_imm16_reg1_reg2: + imm_32 = extract32(ctx->opcode, 16, 16); + tcg_gen_movi_tl(tcg_ctx, tcg_imm, imm_32); + tcg_gen_ext16s_tl(tcg_ctx, tcg_imm, tcg_imm); + tcg_gen_add_tl(tcg_ctx, r2,r1, tcg_imm); + gen_set_gpr(tcg_ctx, rs2, r2); + + gen_flags_on_add(tcg_ctx, r1, tcg_imm); + + break; + + case OPC_RH850_CMP_reg1_reg2: { + gen_flags_on_sub(tcg_ctx, r2, r1); + } break; + + case OPC_RH850_CMP_imm5_reg2: { + + if ((imm & 0x10) == 0x10){ + imm = imm | (0x7 << 5); + } + tcg_gen_movi_tl(tcg_ctx, tcg_imm, imm); + tcg_gen_ext8s_i32(tcg_ctx, tcg_imm, tcg_imm); + + gen_flags_on_sub(tcg_ctx, r2, tcg_imm); + + } break; + + case OPC_RH850_MOV_reg1_reg2: + tcg_gen_mov_tl(tcg_ctx, r2, r1); + gen_set_gpr(tcg_ctx, rs2, r2); + break; + + case OPC_RH850_MOV_imm5_reg2: + if ((imm & 0x10) == 0x10){ + imm = imm | (0x7 << 5); + } + tcg_gen_movi_tl(tcg_ctx, r2, imm); + tcg_gen_ext8s_i32(tcg_ctx, r2, r2); + + gen_set_gpr(tcg_ctx, rs2, r2); + break; + + case OPC_RH850_MOV_imm32_reg1: // 48bit instruction + opcode48 = (ctx->opcode1); + opcode48 = (ctx->opcode) | (opcode48 << 0x20); + imm_32 = extract64(opcode48, 16, 32) & 0xffffffff; + tcg_gen_movi_i32(tcg_ctx, r2, imm_32); + gen_set_gpr(tcg_ctx, rs2, r2); + break; + + case OPC_RH850_MOVEA_imm16_reg1_reg2: + imm_32 = extract32(ctx->opcode, 16, 16); + tcg_gen_movi_i32(tcg_ctx, tcg_imm, imm_32); + tcg_gen_ext16s_i32(tcg_ctx, tcg_imm, tcg_imm); + + tcg_gen_add_i32(tcg_ctx, r2, tcg_imm, r1); + gen_set_gpr(tcg_ctx, rs2, r2); + break; + + case OPC_RH850_MOVHI_imm16_reg1_reg2: + imm_32 = extract32(ctx->opcode, 16, 16); + tcg_gen_movi_i32(tcg_ctx, tcg_imm, imm_32); + tcg_gen_shli_i32(tcg_ctx, tcg_imm, tcg_imm, 0x10); + + tcg_gen_add_i32(tcg_ctx, r2, tcg_imm, r1); + gen_set_gpr(tcg_ctx, rs2, r2); + break; + + case OPC_RH850_SUB_reg1_reg2: + + tcg_gen_sub_tl(tcg_ctx, tcg_result, r2, r1); + gen_set_gpr(tcg_ctx, rs2, tcg_result); + gen_flags_on_sub(tcg_ctx, r2, r1); + break; + + case OPC_RH850_SUBR_reg1_reg2: + tcg_gen_sub_tl(tcg_ctx, tcg_result, r1, r2); + gen_set_gpr(tcg_ctx, rs2, tcg_result); + gen_flags_on_sub(tcg_ctx, r1, r2); + break; + } + + tcg_temp_free(tcg_ctx, r1); + tcg_temp_free(tcg_ctx, r2); + tcg_temp_free(tcg_ctx, tcg_imm); + tcg_temp_free(tcg_ctx, tcg_r3); + tcg_temp_free(tcg_ctx, tcg_result); +} + +static void gen_cond_arith(DisasContext *ctx, int rs1, int rs2, int operation) +{ + TCGContext *tcg_ctx = ctx->uc->tcg_ctx; + + TCGv r1 = tcg_temp_local_new(tcg_ctx); + TCGv r2 = tcg_temp_local_new(tcg_ctx); + + TCGLabel *cont; + + gen_get_gpr(tcg_ctx, r1, rs1); + gen_get_gpr(tcg_ctx, r2, rs2); + + int int_rs3; + int int_cond; + + switch(operation){ + + case OPC_RH850_ADF_cccc_reg1_reg2_reg3:{ + + TCGv r1_local = tcg_temp_local_new_i32(tcg_ctx); + TCGv r2_local = tcg_temp_local_new_i32(tcg_ctx); + TCGv r3_local = tcg_temp_local_new_i32(tcg_ctx); + TCGv addIfCond = tcg_temp_local_new_i32(tcg_ctx); + TCGv carry = tcg_temp_local_new_i32(tcg_ctx); + TCGv overflow = tcg_temp_local_new_i32(tcg_ctx); + + tcg_gen_movi_tl(tcg_ctx, carry, 0); + tcg_gen_movi_tl(tcg_ctx, overflow, 0); + + int_rs3 = extract32(ctx->opcode, 27, 5); + int_cond = extract32(ctx->opcode, 17, 4); + if(int_cond == 0xd){ + //throw exception/warning for inappropriate condition (SA) + break; + } + + tcg_gen_mov_i32(tcg_ctx, r1_local, r1); + tcg_gen_mov_i32(tcg_ctx, r2_local, r2); + gen_get_gpr(tcg_ctx, r3_local,int_rs3); + tcg_gen_movi_i32(tcg_ctx, addIfCond, 0x1); + + TCGv condResult = condition_satisfied(tcg_ctx, int_cond); + cont = gen_new_label(tcg_ctx); + + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_NE, condResult, 0x1, cont); + // calc and store CY and OV flags to be used to obtain final values + gen_flags_on_add(tcg_ctx, r2_local, addIfCond); + tcg_gen_mov_tl(tcg_ctx, carry, cpu_CYF); + tcg_gen_mov_tl(tcg_ctx, overflow, cpu_OVF); + // on cond true, add 1 + tcg_gen_add_tl(tcg_ctx, r2_local, r2_local, addIfCond); + + gen_set_label(tcg_ctx, cont); + tcg_gen_add_tl(tcg_ctx, r3_local, r1_local, r2_local); + gen_set_gpr(tcg_ctx, int_rs3, r3_local); + + gen_flags_on_add(tcg_ctx, r1_local, r2_local); + tcg_gen_or_tl(tcg_ctx, cpu_CYF, cpu_CYF, carry); + tcg_gen_or_tl(tcg_ctx, cpu_OVF, cpu_OVF, overflow); + + tcg_temp_free(tcg_ctx, condResult); + tcg_temp_free_i32(tcg_ctx, r1_local); + tcg_temp_free_i32(tcg_ctx, r2_local); + tcg_temp_free_i32(tcg_ctx, r3_local); + tcg_temp_free_i32(tcg_ctx, addIfCond); + } + break; + + case OPC_RH850_SBF_cccc_reg1_reg2_reg3:{ + + int_rs3 = extract32(ctx->opcode, 27, 5); + int_cond = extract32(ctx->opcode, 17, 4); + if(int_cond == 0xd){ + //throw exception/warning for inappropriate condition (SA) + break; + } + //TCGLabel *skip_cy_ov; + + // tcg_gen_mov_i32(cpu_gpr[25], cpu_CYF); + // tcg_gen_mov_i32(cpu_gpr[24], cpu_OVF); + + //TCGv r1_local = tcg_temp_new(); + //TCGv r2_local = tcg_temp_new(); + TCGv r3_local = tcg_temp_local_new(tcg_ctx); + TCGv tmpReg = tcg_temp_local_new(tcg_ctx); + TCGv carry = tcg_temp_local_new(tcg_ctx); + TCGv overflow = tcg_temp_local_new(tcg_ctx); + cont = gen_new_label(tcg_ctx); + + tcg_gen_movi_tl(tcg_ctx, carry, 0); + tcg_gen_movi_tl(tcg_ctx, overflow, 0); + + //tcg_gen_mov_i32(r1_local, r1); + //tcg_gen_mov_i32(r2_local, r2); + tcg_gen_mov_i32(tcg_ctx, r3_local, r2); + + TCGv condResult = condition_satisfied(tcg_ctx, int_cond); + // store to local temp, because condResult is valid only until branch in gen_flags_on_sub + tcg_gen_mov_tl(tcg_ctx, tmpReg, condResult); + + gen_flags_on_sub(tcg_ctx, r3_local, r1); + tcg_gen_mov_tl(tcg_ctx, carry, cpu_CYF); + tcg_gen_mov_tl(tcg_ctx, overflow, cpu_OVF); + tcg_gen_sub_tl(tcg_ctx, r3_local, r3_local, r1); + + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_NE, tmpReg, 0x1, cont); + tcg_gen_movi_i32(tcg_ctx, tmpReg, 0x1); + gen_flags_on_sub(tcg_ctx, r3_local, tmpReg); + tcg_gen_subi_tl(tcg_ctx, r3_local, r3_local, 1); + tcg_gen_or_tl(tcg_ctx, cpu_CYF, cpu_CYF, carry); + // overflow twice means no overflow + tcg_gen_xor_tl(tcg_ctx, cpu_OVF, cpu_OVF, overflow); + + gen_set_label(tcg_ctx, cont); + + gen_set_gpr(tcg_ctx, int_rs3, r3_local); + + tcg_temp_free(tcg_ctx, condResult); + // tcg_temp_free_i32(r1_local); + // tcg_temp_free_i32(r2_local); + tcg_temp_free_i32(tcg_ctx, r3_local); + tcg_temp_free_i32(tcg_ctx, tmpReg); + tcg_temp_free_i32(tcg_ctx, overflow); + tcg_temp_free_i32(tcg_ctx, carry); + } + break; + } + + tcg_temp_free_i32(tcg_ctx, r1); + tcg_temp_free_i32(tcg_ctx, r2); +} + +static void gen_sat_op(DisasContext *ctx, int rs1, int rs2, int operation) +{ + TCGContext *tcg_ctx = ctx->uc->tcg_ctx; + + TCGv r1 = tcg_temp_new(tcg_ctx); + TCGv r2 = tcg_temp_new(tcg_ctx); + gen_get_gpr(tcg_ctx, r1, rs1); + gen_get_gpr(tcg_ctx, r2, rs2); + + int imm = rs1; + int int_rs3; + + TCGLabel *end; + TCGLabel *cont; + TCGLabel *cont2; + TCGLabel *setMax; + TCGLabel *dontChange; + + switch(operation){ + + case OPC_RH850_SATADD_reg1_reg2: { + + TCGv r1_local = tcg_temp_local_new(tcg_ctx); + TCGv r2_local = tcg_temp_local_new(tcg_ctx); + TCGv result = tcg_temp_local_new(tcg_ctx); + TCGv check = tcg_temp_local_new(tcg_ctx); + TCGv min = tcg_temp_local_new(tcg_ctx); + TCGv max = tcg_temp_local_new(tcg_ctx); + TCGv zero = tcg_temp_local_new(tcg_ctx); + tcg_gen_movi_i32(tcg_ctx, min, 0x80000000); + tcg_gen_movi_i32(tcg_ctx, max, 0x7fffffff); + tcg_gen_mov_i32(tcg_ctx, r1_local, r1); + tcg_gen_mov_i32(tcg_ctx, r2_local, r2); + tcg_gen_movi_i32(tcg_ctx, zero, 0x0); + end = gen_new_label(tcg_ctx); + cont = gen_new_label(tcg_ctx); + cont2 = gen_new_label(tcg_ctx); + + + tcg_gen_add_i32(tcg_ctx, result, r1_local, r2_local); + + tcg_gen_brcond_tl(tcg_ctx, TCG_COND_LT, r1_local, zero, cont); + + tcg_gen_sub_i32(tcg_ctx, check, max, r1_local); + tcg_gen_brcond_tl(tcg_ctx, TCG_COND_LE, r2_local, check, end); + tcg_gen_mov_i32(tcg_ctx, result, max); + tcg_gen_movi_i32(tcg_ctx, cpu_SATF, 0x1); + tcg_gen_br(tcg_ctx, end); + + //--------------------------------------------------------------------------------- + gen_set_label(tcg_ctx, cont); + tcg_gen_sub_i32(tcg_ctx, check, min, r1_local); + tcg_gen_brcond_tl(tcg_ctx, TCG_COND_GE, r2_local, check, cont2); + tcg_gen_mov_i32(tcg_ctx, result, min); + tcg_gen_movi_i32(tcg_ctx, cpu_SATF, 0x1); + + gen_set_label(tcg_ctx, cont2); + gen_set_label(tcg_ctx, end); + gen_set_gpr(tcg_ctx, rs2, result); + + gen_satadd_CC(tcg_ctx, r1_local, r2_local, result); // moves also SET flag to psw + + tcg_temp_free(tcg_ctx, result); + tcg_temp_free(tcg_ctx, check); + tcg_temp_free(tcg_ctx, min); + tcg_temp_free(tcg_ctx, max); + tcg_temp_free(tcg_ctx, r1_local); + tcg_temp_free(tcg_ctx, r2_local); + tcg_temp_free(tcg_ctx, zero); + + } break; + + case OPC_RH850_SATADD_imm5_reg2: { + + TCGv imm_local = tcg_temp_local_new(tcg_ctx); + TCGv r2_local = tcg_temp_local_new(tcg_ctx); + TCGv result = tcg_temp_local_new(tcg_ctx); + TCGv check = tcg_temp_local_new(tcg_ctx); + TCGv min = tcg_temp_local_new(tcg_ctx); + TCGv max = tcg_temp_local_new(tcg_ctx); + TCGv zero = tcg_temp_local_new(tcg_ctx); + tcg_gen_movi_i32(tcg_ctx, min, 0x80000000); + tcg_gen_movi_i32(tcg_ctx, max, 0x7fffffff); + tcg_gen_mov_i32(tcg_ctx, r2_local, r2); + tcg_gen_movi_i32(tcg_ctx, zero, 0x0); + end = gen_new_label(tcg_ctx); + cont = gen_new_label(tcg_ctx); + cont2 = gen_new_label(tcg_ctx); + + if ((imm & 0x10) == 0x10){ + imm = imm | (0x7 << 5); + } + + tcg_gen_movi_tl(tcg_ctx, imm_local, imm); + tcg_gen_ext8s_tl(tcg_ctx, imm_local, imm_local); + + tcg_gen_add_i32(tcg_ctx, result, imm_local, r2_local); + + tcg_gen_brcond_tl(tcg_ctx, TCG_COND_LT, imm_local, zero, cont); + + tcg_gen_sub_i32(tcg_ctx, check, max, imm_local); + tcg_gen_brcond_tl(tcg_ctx, TCG_COND_LE, r2_local, check, end); + tcg_gen_mov_i32(tcg_ctx, result, max); + tcg_gen_movi_i32(tcg_ctx, cpu_SATF, 0x1); + tcg_gen_br(tcg_ctx, end); + + //--------------------------------------------------------------------------------- + gen_set_label(tcg_ctx, cont); + tcg_gen_sub_i32(tcg_ctx, check, min, imm_local); + tcg_gen_brcond_tl(tcg_ctx, TCG_COND_GE, r2_local, check, cont2); + tcg_gen_mov_i32(tcg_ctx, result, min); + tcg_gen_movi_i32(tcg_ctx, cpu_SATF, 0x1); + + gen_set_label(tcg_ctx, cont2); + gen_set_label(tcg_ctx, end); + gen_set_gpr(tcg_ctx, rs2, result); + + gen_satadd_CC(tcg_ctx, r2_local, imm_local, result); + + tcg_temp_free(tcg_ctx, result); + tcg_temp_free(tcg_ctx, check); + tcg_temp_free(tcg_ctx, min); + tcg_temp_free(tcg_ctx, max); + tcg_temp_free(tcg_ctx, imm_local); + tcg_temp_free(tcg_ctx, r2_local); + tcg_temp_free(tcg_ctx, zero); + + } break; + + case OPC_RH850_SATADD_reg1_reg2_reg3: { + + TCGv r1_local = tcg_temp_local_new(tcg_ctx); + TCGv r2_local = tcg_temp_local_new(tcg_ctx); + TCGv result = tcg_temp_local_new(tcg_ctx); + TCGv check = tcg_temp_local_new(tcg_ctx); + TCGv min = tcg_temp_local_new(tcg_ctx); + TCGv max = tcg_temp_local_new(tcg_ctx); + TCGv zero = tcg_temp_local_new(tcg_ctx); + tcg_gen_movi_i32(tcg_ctx, min, 0x80000000); + tcg_gen_movi_i32(tcg_ctx, max, 0x7fffffff); + tcg_gen_mov_i32(tcg_ctx, r1_local, r1); + tcg_gen_mov_i32(tcg_ctx, r2_local, r2); + tcg_gen_movi_i32(tcg_ctx, zero, 0x0); + end = gen_new_label(tcg_ctx); + cont = gen_new_label(tcg_ctx); + cont2 = gen_new_label(tcg_ctx); + + int_rs3 = extract32(ctx->opcode, 27, 5); + tcg_gen_add_i32(tcg_ctx, result, r1_local, r2_local); + + tcg_gen_brcond_tl(tcg_ctx, TCG_COND_LT, r1_local, zero, cont); //if (r1 > 0) + + tcg_gen_sub_i32(tcg_ctx, check, max, r1_local); + tcg_gen_brcond_tl(tcg_ctx, TCG_COND_LE, r2_local, check, end); //if (r2 > MAX-r1) + tcg_gen_mov_i32(tcg_ctx, result, max); //return MAX; + tcg_gen_movi_i32(tcg_ctx, cpu_SATF, 0x1); + tcg_gen_br(tcg_ctx, end); + + //--------------------------------------------------------------------------------- + gen_set_label(tcg_ctx, cont); //else + tcg_gen_sub_i32(tcg_ctx, check, min, r1_local); + tcg_gen_brcond_tl(tcg_ctx, TCG_COND_GE, r2_local, check, cont2); //if (r2 < MIN-r1) + tcg_gen_mov_i32(tcg_ctx, result, min); //return MIN; + tcg_gen_movi_i32(tcg_ctx, cpu_SATF, 0x1); + + gen_set_label(tcg_ctx, cont2); + gen_set_label(tcg_ctx, end); + gen_set_gpr(tcg_ctx, int_rs3, result); + + gen_satadd_CC(tcg_ctx, r1_local, r2_local, result); + + tcg_temp_free(tcg_ctx, result); + tcg_temp_free(tcg_ctx, check); + tcg_temp_free(tcg_ctx, min); + tcg_temp_free(tcg_ctx, max); + tcg_temp_free(tcg_ctx, r1_local); + tcg_temp_free(tcg_ctx, r2_local); + tcg_temp_free(tcg_ctx, zero); + + } break; + + case OPC_RH850_SATSUB_reg1_reg2: { + + TCGv r1_local = tcg_temp_local_new(tcg_ctx); + TCGv r2_local = tcg_temp_local_new(tcg_ctx); + TCGv result = tcg_temp_local_new(tcg_ctx); + TCGv check = tcg_temp_local_new(tcg_ctx); + TCGv min = tcg_temp_local_new(tcg_ctx); + TCGv max = tcg_temp_local_new(tcg_ctx); + TCGv zero = tcg_temp_local_new(tcg_ctx); + tcg_gen_movi_i32(tcg_ctx, min, 0x80000000); + tcg_gen_movi_i32(tcg_ctx, max, 0x7fffffff); + tcg_gen_mov_i32(tcg_ctx, r1_local, r1); + tcg_gen_mov_i32(tcg_ctx, r2_local, r2); + tcg_gen_movi_i32(tcg_ctx, zero, 0x0); + end = gen_new_label(tcg_ctx); + cont = gen_new_label(tcg_ctx); + cont2 = gen_new_label(tcg_ctx); + setMax = gen_new_label(tcg_ctx); + dontChange = gen_new_label(tcg_ctx); + + /* + * Negating second operand and using satadd code. When negating an operand + * with value 0x80000000, the result overflows positive numbers and is not + * negated. If this happens, the operand is first incremented, and then negated. + * The second operand is as well incremented, if it's value is less than 0x7fffffff. + * Otherwise, the result is set to MAX and SATF is set. + * This was done in all following saturated subtraction functions. + */ + + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_NE, r1_local, 0x80000000, dontChange); + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_EQ, r2_local, 0x7fffffff, setMax); + + tcg_gen_addi_i32(tcg_ctx, r1_local, r1_local, 0x1); + tcg_gen_addi_i32(tcg_ctx, r2_local, r2_local, 0x1); + gen_set_label(tcg_ctx, dontChange); + + tcg_gen_neg_i32(tcg_ctx, r1_local, r1_local); + tcg_gen_add_i32(tcg_ctx, result, r1_local, r2_local); + + tcg_gen_brcond_tl(tcg_ctx, TCG_COND_LT, r1_local, zero, cont); + + tcg_gen_sub_i32(tcg_ctx, check, max, r1_local); + tcg_gen_brcond_tl(tcg_ctx, TCG_COND_LE, r2_local, check, end); + gen_set_label(tcg_ctx, setMax); + tcg_gen_mov_i32(tcg_ctx, result, max); + tcg_gen_movi_i32(tcg_ctx, cpu_SATF, 0x1); + tcg_gen_br(tcg_ctx, end); + + //--------------------------------------------------------------------------------- + gen_set_label(tcg_ctx, cont); + tcg_gen_sub_i32(tcg_ctx, check, min, r1_local); + tcg_gen_brcond_tl(tcg_ctx, TCG_COND_GE, r2_local, check, cont2); + tcg_gen_mov_i32(tcg_ctx, result, min); + tcg_gen_movi_i32(tcg_ctx, cpu_SATF, 0x1); + + gen_set_label(tcg_ctx, cont2); + gen_set_label(tcg_ctx, end); + gen_set_gpr(tcg_ctx, rs2, result); + + // second negation is needed for appropriate flag calculation + tcg_gen_neg_i32(tcg_ctx, r1_local, r1_local); + gen_satsub_CC(tcg_ctx, r2_local, r1_local, result); + + tcg_temp_free(tcg_ctx, result); + tcg_temp_free(tcg_ctx, check); + tcg_temp_free(tcg_ctx, min); + tcg_temp_free(tcg_ctx, max); + tcg_temp_free(tcg_ctx, r1_local); + tcg_temp_free(tcg_ctx, r2_local); + tcg_temp_free(tcg_ctx, zero); + + } break; + + case OPC_RH850_SATSUB_reg1_reg2_reg3: { + TCGv r1_local = tcg_temp_local_new(tcg_ctx); + TCGv r2_local = tcg_temp_local_new(tcg_ctx); + TCGv result = tcg_temp_local_new(tcg_ctx); + TCGv check = tcg_temp_local_new(tcg_ctx); + TCGv min = tcg_temp_local_new(tcg_ctx); + TCGv max = tcg_temp_local_new(tcg_ctx); + TCGv zero = tcg_temp_local_new(tcg_ctx); + tcg_gen_movi_i32(tcg_ctx, min, 0x80000000); + tcg_gen_movi_i32(tcg_ctx, max, 0x7fffffff); + tcg_gen_mov_i32(tcg_ctx, r1_local, r1); + tcg_gen_mov_i32(tcg_ctx, r2_local, r2); + tcg_gen_movi_i32(tcg_ctx, zero, 0x0); + end = gen_new_label(tcg_ctx); + cont = gen_new_label(tcg_ctx); + cont2 = gen_new_label(tcg_ctx); + setMax = gen_new_label(tcg_ctx); + dontChange = gen_new_label(tcg_ctx); + int_rs3 = extract32(ctx->opcode, 27, 5); + + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_NE, r1_local, 0x80000000, dontChange); + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_EQ, r2_local, 0x7fffffff, setMax); + + tcg_gen_addi_i32(tcg_ctx, r1_local, r1_local, 0x1); + tcg_gen_addi_i32(tcg_ctx, r2_local, r2_local, 0x1); + gen_set_label(tcg_ctx, dontChange); + + tcg_gen_neg_i32(tcg_ctx, r1_local, r1_local); + tcg_gen_add_i32(tcg_ctx, result, r1_local, r2_local); + + tcg_gen_brcond_tl(tcg_ctx, TCG_COND_LT, r1_local, zero, cont); + + tcg_gen_sub_i32(tcg_ctx, check, max, r1_local); + tcg_gen_brcond_tl(tcg_ctx, TCG_COND_LE, r2_local, check, end); + gen_set_label(tcg_ctx, setMax); + tcg_gen_mov_i32(tcg_ctx, result, max); + tcg_gen_movi_i32(tcg_ctx, cpu_SATF, 0x1); + tcg_gen_br(tcg_ctx, end); + + //--------------------------------------------------------------------------------- + gen_set_label(tcg_ctx, cont); + tcg_gen_sub_i32(tcg_ctx, check, min, r1_local); + tcg_gen_brcond_tl(tcg_ctx, TCG_COND_GE, r2_local, check, cont2); + tcg_gen_mov_i32(tcg_ctx, result, min); + tcg_gen_movi_i32(tcg_ctx, cpu_SATF, 0x1); + + gen_set_label(tcg_ctx, cont2); + gen_set_label(tcg_ctx, end); + gen_set_gpr(tcg_ctx, int_rs3, result); + + tcg_gen_neg_i32(tcg_ctx, r1_local, r1_local); + gen_satsub_CC(tcg_ctx, r2_local, r1_local, result); + + tcg_temp_free(tcg_ctx, result); + tcg_temp_free(tcg_ctx, check); + tcg_temp_free(tcg_ctx, min); + tcg_temp_free(tcg_ctx, max); + tcg_temp_free(tcg_ctx, r1_local); + tcg_temp_free(tcg_ctx, r2_local); + tcg_temp_free(tcg_ctx, zero); + + } break; + + case OPC_RH850_SATSUBI_imm16_reg1_reg2: { + TCGv r1_local = tcg_temp_local_new(tcg_ctx); + TCGv imm_local = tcg_temp_local_new(tcg_ctx); + TCGv result = tcg_temp_local_new(tcg_ctx); + TCGv check = tcg_temp_local_new(tcg_ctx); + TCGv min = tcg_temp_local_new(tcg_ctx); + TCGv max = tcg_temp_local_new(tcg_ctx); + TCGv zero = tcg_temp_local_new(tcg_ctx); + tcg_gen_movi_i32(tcg_ctx, min, 0x80000000); + tcg_gen_movi_i32(tcg_ctx, max, 0x7fffffff); + tcg_gen_mov_i32(tcg_ctx, r1_local, r1); + imm = extract32(ctx->opcode, 16, 16); + tcg_gen_movi_i32(tcg_ctx, imm_local, imm); + tcg_gen_ext16s_i32(tcg_ctx, imm_local, imm_local); + tcg_gen_movi_i32(tcg_ctx, zero, 0x0); + end = gen_new_label(tcg_ctx); + cont = gen_new_label(tcg_ctx); + cont2 = gen_new_label(tcg_ctx); + setMax = gen_new_label(tcg_ctx); + dontChange = gen_new_label(tcg_ctx); + + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_NE, r1_local, 0x80000000, dontChange); + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_EQ, imm_local, 0x7fffffff, setMax); + + tcg_gen_addi_i32(tcg_ctx, r1_local, r1_local, 0x1); + tcg_gen_addi_i32(tcg_ctx, imm_local, imm_local, 0x1); + gen_set_label(tcg_ctx, dontChange); + + + tcg_gen_neg_i32(tcg_ctx, imm_local, imm_local); + + tcg_gen_add_i32(tcg_ctx, result, r1_local, imm_local); + + tcg_gen_brcond_tl(tcg_ctx, TCG_COND_LT, r1_local, zero, cont); + + tcg_gen_sub_i32(tcg_ctx, check, max, r1_local); + tcg_gen_brcond_tl(tcg_ctx, TCG_COND_LE, imm_local, check, end); + gen_set_label(tcg_ctx, setMax); + tcg_gen_mov_i32(tcg_ctx, result, max); + tcg_gen_movi_i32(tcg_ctx, cpu_SATF, 0x1); + tcg_gen_br(tcg_ctx, end); + + //--------------------------------------------------------------------------------- + gen_set_label(tcg_ctx, cont); + tcg_gen_sub_i32(tcg_ctx, check, min, r1_local); + tcg_gen_brcond_tl(tcg_ctx, TCG_COND_GE, imm_local, check, cont2); + tcg_gen_mov_i32(tcg_ctx, result, min); + tcg_gen_movi_i32(tcg_ctx, cpu_SATF, 0x1); + + gen_set_label(tcg_ctx, cont2); + gen_set_label(tcg_ctx, end); + gen_set_gpr(tcg_ctx, rs2, result); + + tcg_gen_neg_i32(tcg_ctx, imm_local, imm_local); + gen_satsub_CC(tcg_ctx, r1_local, imm_local, result); + + tcg_temp_free(tcg_ctx, result); + tcg_temp_free(tcg_ctx, check); + tcg_temp_free(tcg_ctx, min); + tcg_temp_free(tcg_ctx, max); + tcg_temp_free(tcg_ctx, r1_local); + tcg_temp_free(tcg_ctx, imm_local); + tcg_temp_free(tcg_ctx, zero); + + } break; + + case OPC_RH850_SATSUBR_reg1_reg2: { + + TCGv r1_local = tcg_temp_local_new(tcg_ctx); + TCGv r2_local = tcg_temp_local_new(tcg_ctx); + TCGv result = tcg_temp_local_new(tcg_ctx); + TCGv check = tcg_temp_local_new(tcg_ctx); + TCGv min = tcg_temp_local_new(tcg_ctx); + TCGv max = tcg_temp_local_new(tcg_ctx); + TCGv zero = tcg_temp_local_new(tcg_ctx); + tcg_gen_movi_i32(tcg_ctx, min, 0x80000000); + tcg_gen_movi_i32(tcg_ctx, max, 0x7fffffff); + tcg_gen_mov_i32(tcg_ctx, r1_local, r2); + tcg_gen_mov_i32(tcg_ctx, r2_local, r1); + tcg_gen_movi_i32(tcg_ctx, zero, 0x0); + end = gen_new_label(tcg_ctx); + cont = gen_new_label(tcg_ctx); + cont2 = gen_new_label(tcg_ctx); + setMax = gen_new_label(tcg_ctx); + dontChange = gen_new_label(tcg_ctx); + + /* + * Negating second operand and using satadd code. When negating an operand + * with value 0x80000000, the result overflows positive numbers and is not + * negated. If this happens, the operand is first incremented, and then negated. + * The second operand is as well incremented, if it's value is less than 0x7fffffff. + * Otherwise, the result is set to MAX and SATF is set. + * This was done in all following saturated subtraction functions. + */ + + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_NE, r1_local, 0x80000000, dontChange); + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_EQ, r2_local, 0x7fffffff, setMax); + + tcg_gen_addi_i32(tcg_ctx, r1_local, r1_local, 0x1); + tcg_gen_addi_i32(tcg_ctx, r2_local, r2_local, 0x1); + gen_set_label(tcg_ctx, dontChange); + + tcg_gen_neg_i32(tcg_ctx, r1_local, r1_local); + tcg_gen_add_i32(tcg_ctx, result, r1_local, r2_local); + + tcg_gen_brcond_tl(tcg_ctx, TCG_COND_LT, r1_local, zero, cont); + + tcg_gen_sub_i32(tcg_ctx, check, max, r1_local); + tcg_gen_brcond_tl(tcg_ctx, TCG_COND_LE, r2_local, check, end); + gen_set_label(tcg_ctx, setMax); + tcg_gen_mov_i32(tcg_ctx, result, max); + tcg_gen_movi_i32(tcg_ctx, cpu_SATF, 0x1); + tcg_gen_br(tcg_ctx, end); + + //--------------------------------------------------------------------------------- + gen_set_label(tcg_ctx, cont); + tcg_gen_sub_i32(tcg_ctx, check, min, r1_local); + tcg_gen_brcond_tl(tcg_ctx, TCG_COND_GE, r2_local, check, cont2); + tcg_gen_mov_i32(tcg_ctx, result, min); + tcg_gen_movi_i32(tcg_ctx, cpu_SATF, 0x1); + + gen_set_label(tcg_ctx, cont2); + gen_set_label(tcg_ctx, end); + gen_set_gpr(tcg_ctx, rs2, result); + + tcg_gen_neg_i32(tcg_ctx, r1_local, r1_local); + gen_satsub_CC(tcg_ctx, r2_local, r1_local, result); + + tcg_temp_free(tcg_ctx, result); + tcg_temp_free(tcg_ctx, check); + tcg_temp_free(tcg_ctx, min); + tcg_temp_free(tcg_ctx, max); + tcg_temp_free(tcg_ctx, r1_local); + tcg_temp_free(tcg_ctx, r2_local); + tcg_temp_free(tcg_ctx, zero); + + } break; + } + + tcg_temp_free(tcg_ctx, r1); + tcg_temp_free(tcg_ctx, r2); +} + +static void gen_logical(DisasContext *ctx, int rs1, int rs2, int operation) +{ + TCGContext *tcg_ctx = ctx->uc->tcg_ctx; + + TCGv r1 = tcg_temp_new(tcg_ctx); + TCGv r2 = tcg_temp_new(tcg_ctx); + TCGv result = tcg_temp_new(tcg_ctx); + gen_get_gpr(tcg_ctx, r1, rs1); + gen_get_gpr(tcg_ctx, r2, rs2); + + int imm_32; + TCGv tcg_imm = tcg_temp_new(tcg_ctx); + + switch(operation){ + + case OPC_RH850_AND_reg1_reg2: + tcg_gen_and_tl(tcg_ctx, r2, r2, r1); + gen_set_gpr(tcg_ctx, rs2, r2); + gen_logic_CC(tcg_ctx, r2); + break; + + case OPC_RH850_ANDI_imm16_reg1_reg2: + imm_32 = extract32(ctx->opcode, 16, 16); + tcg_gen_movi_tl(tcg_ctx, tcg_imm, imm_32); + tcg_gen_ext16u_i32(tcg_ctx, tcg_imm, tcg_imm); + tcg_gen_and_i32(tcg_ctx, r2, r1, tcg_imm); + gen_set_gpr(tcg_ctx, rs2, r2); + gen_logic_CC(tcg_ctx, r2); + break; + + case OPC_RH850_NOT_reg1_reg2: + tcg_gen_not_i32(tcg_ctx, r2, r1); + gen_set_gpr(tcg_ctx, rs2, r2); + gen_logic_CC(tcg_ctx, r2); + break; + + case OPC_RH850_OR_reg1_reg2: + tcg_gen_or_tl(tcg_ctx, r2, r2, r1); + gen_set_gpr(tcg_ctx, rs2, r2); + gen_logic_CC(tcg_ctx, r2); + break; + + case OPC_RH850_ORI_imm16_reg1_reg2: + imm_32 = extract32(ctx->opcode, 16, 16); + tcg_gen_movi_i32(tcg_ctx, tcg_imm, imm_32); + tcg_gen_ext16u_i32(tcg_ctx, tcg_imm,tcg_imm); + + tcg_gen_or_i32(tcg_ctx, r2, r1, tcg_imm); + gen_set_gpr(tcg_ctx, rs2, r2); + gen_logic_CC(tcg_ctx, r2); + break; + + case OPC_RH850_TST_reg1_reg2: + tcg_gen_and_i32(tcg_ctx, result, r1, r2); + gen_logic_CC(tcg_ctx, result); + break; + + case OPC_RH850_XOR_reg1_reg2: + tcg_gen_xor_i32(tcg_ctx, result, r2, r1); + gen_set_gpr(tcg_ctx, rs2, result); + gen_logic_CC(tcg_ctx, result); + break; + + case OPC_RH850_XORI_imm16_reg1_reg2: + imm_32 = extract32(ctx->opcode, 16, 16); + tcg_gen_movi_i32(tcg_ctx, tcg_imm, imm_32); + tcg_gen_ext16u_i32(tcg_ctx, tcg_imm,tcg_imm); + + tcg_gen_xor_i32(tcg_ctx, result, r1, tcg_imm); + gen_set_gpr(tcg_ctx, rs2, result); + gen_logic_CC(tcg_ctx, result); + break; + } + + tcg_temp_free(tcg_ctx, r1); + tcg_temp_free(tcg_ctx, r2); + tcg_temp_free(tcg_ctx, tcg_imm); + tcg_temp_free(tcg_ctx, result); +} + +static void gen_data_manipulation(DisasContext *ctx, int rs1, int rs2, int operation) +{ + TCGContext *tcg_ctx = ctx->uc->tcg_ctx; + + TCGv tcg_r1 = tcg_temp_new(tcg_ctx); + TCGv tcg_r2 = tcg_temp_new(tcg_ctx); + TCGv tcg_r3 = tcg_temp_new(tcg_ctx); + TCGv tcg_imm = tcg_temp_new(tcg_ctx); + TCGv tcg_temp = tcg_temp_new(tcg_ctx); + TCGv tcg_temp2 = tcg_temp_new(tcg_ctx); + TCGv insert = tcg_temp_new(tcg_ctx); + + TCGLabel *cont; + TCGLabel *end; + TCGLabel *set; + + int int_imm = rs1; + int int_rs3; + int int_cond; + int pos; + int lsb; + int msb; + int width; + int mask; + int group; + + gen_get_gpr(tcg_ctx, tcg_r1, rs1); + gen_get_gpr(tcg_ctx, tcg_r2, rs2); + + switch(operation) { + + case OPC_RH850_BINS: + + group = extract32(ctx->opcode, 21, 2); + + mask = 0; + pos = extract32(ctx->opcode, 17, 3) | (extract32(ctx->opcode, 27, 1) << 3); + lsb = pos; + + msb = extract32(ctx->opcode, 28, 4); + width = extract32(ctx->opcode, 28, 4) - pos + 1; + + switch(group){ + case 0: //bins0 + pos += 16; + break; + case 1: //bins1 + width += 16; + msb+=16; + break; + case 2: //bins2 + break; + } + + if(msbopcode, 27, 5); + tcg_gen_mov_tl(tcg_ctx, tcg_temp2, tcg_r2); + tcg_gen_movi_i32(tcg_ctx, tcg_r3, 0x0); + + tcg_gen_andi_tl(tcg_ctx, tcg_temp, tcg_temp2, 0xff000000); + tcg_gen_shri_tl(tcg_ctx, tcg_temp, tcg_temp, 0x8); + tcg_gen_or_tl(tcg_ctx, tcg_r3, tcg_r3, tcg_temp); + + tcg_gen_andi_tl(tcg_ctx, tcg_temp, tcg_temp2, 0x00ff0000); + tcg_gen_shli_tl(tcg_ctx, tcg_temp, tcg_temp, 0x8); + tcg_gen_or_tl(tcg_ctx, tcg_r3, tcg_r3, tcg_temp); + + tcg_gen_andi_tl(tcg_ctx, tcg_temp, tcg_temp2, 0x0000ff00); + tcg_gen_shri_tl(tcg_ctx, tcg_temp, tcg_temp, 0x8); + tcg_gen_or_tl(tcg_ctx, tcg_r3, tcg_r3, tcg_temp); + + tcg_gen_andi_tl(tcg_ctx, tcg_temp, tcg_temp2, 0x000000ff); + tcg_gen_shli_tl(tcg_ctx, tcg_temp, tcg_temp, 0x8); + tcg_gen_or_tl(tcg_ctx, tcg_r3, tcg_r3, tcg_temp); + + gen_set_gpr(tcg_ctx, int_rs3, tcg_r3); + + tcg_gen_mov_i32(tcg_ctx, r2_local, tcg_r2); + tcg_gen_mov_i32(tcg_ctx, r3_local, tcg_r3); + + cont = gen_new_label(tcg_ctx); + end = gen_new_label(tcg_ctx); + set = gen_new_label(tcg_ctx); + tcg_gen_andi_i32(tcg_ctx, temp_local, r3_local, 0x0000ffff); + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_EQ, cpu_ZF, temp_local, 0x0); + tcg_gen_shri_i32(tcg_ctx, cpu_SF, r3_local, 0x1f); + + tcg_gen_movi_i32(tcg_ctx, count_local, 0x0); + + //gen_set_label(cont);//// + + tcg_gen_andi_i32(tcg_ctx, temp_local, r3_local, 0x000000ff); + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_EQ, temp_local, 0x0, set);//// + //tcg_gen_addi_i32(count_local, count_local, 0x1); + //tcg_gen_shri_i32(r3_local, r3_local, 0x1); + //tcg_gen_brcondi_i32(TCG_COND_NE, count_local, 0x9, cont);//// + tcg_gen_andi_i32(tcg_ctx, temp_local, r3_local, 0x0000ff00); + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_EQ, temp_local, 0x0, set);//// + + tcg_gen_movi_i32(tcg_ctx, cpu_CYF, 0x0); + tcg_gen_br(tcg_ctx, end); + + gen_set_label(tcg_ctx, set);//// + tcg_gen_movi_i32(tcg_ctx, cpu_CYF, 0x1); + + gen_set_label(tcg_ctx, end);//// + tcg_gen_movi_i32(tcg_ctx, cpu_OVF, 0x0); + + tcg_temp_free(tcg_ctx, r2_local); + tcg_temp_free(tcg_ctx, r3_local); + tcg_temp_free(tcg_ctx, count_local); + tcg_temp_free(tcg_ctx, temp_local); + } break; + + case OPC_RH850_BSW_reg2_reg3: { + + TCGv r2_local = tcg_temp_local_new_i32(tcg_ctx); + TCGv r3_local = tcg_temp_local_new_i32(tcg_ctx); + TCGv count_local = tcg_temp_local_new_i32(tcg_ctx); + TCGv temp_local = tcg_temp_local_new_i32(tcg_ctx); + + cont = gen_new_label(tcg_ctx); + end = gen_new_label(tcg_ctx); + set = gen_new_label(tcg_ctx); + + int_rs3 = extract32(ctx->opcode, 27, 5); + gen_get_gpr(tcg_ctx, tcg_r3,int_rs3); + tcg_gen_bswap32_i32(tcg_ctx, tcg_r3, tcg_r2); + gen_set_gpr(tcg_ctx, int_rs3, tcg_r3); + + tcg_gen_mov_i32(tcg_ctx, r2_local, tcg_r2); + tcg_gen_mov_i32(tcg_ctx, r3_local, tcg_r3); + + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_EQ, cpu_ZF, r3_local, 0x0); + tcg_gen_shri_i32(tcg_ctx, cpu_SF, r3_local, 0x1f); + + tcg_gen_movi_i32(tcg_ctx, count_local, 0x0); + + gen_set_label(tcg_ctx, cont);//// + + tcg_gen_andi_i32(tcg_ctx, temp_local, r3_local, 0x000000ff); + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_EQ, temp_local, 0x0, set);//// + tcg_gen_addi_i32(tcg_ctx, count_local, count_local, 0x1); + tcg_gen_shri_i32(tcg_ctx, r3_local, r3_local, 0x8); + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_NE, count_local, 0x4, cont);//// + tcg_gen_movi_i32(tcg_ctx, cpu_CYF, 0x0); + tcg_gen_br(tcg_ctx, end); + + gen_set_label(tcg_ctx, set);//// + tcg_gen_movi_i32(tcg_ctx, cpu_CYF, 0x1); + + gen_set_label(tcg_ctx, end);//// + tcg_gen_movi_i32(tcg_ctx, cpu_OVF, 0x0); + + tcg_temp_free(tcg_ctx, r2_local); + tcg_temp_free(tcg_ctx, r3_local); + tcg_temp_free(tcg_ctx, count_local); + tcg_temp_free(tcg_ctx, temp_local); + } + break; + + case OPC_RH850_CMOV_cccc_reg1_reg2_reg3: { + + TCGv r1_local = tcg_temp_local_new_i32(tcg_ctx); + TCGv r2_local = tcg_temp_local_new_i32(tcg_ctx); + TCGv r3_local = tcg_temp_local_new_i32(tcg_ctx); + + int_rs3 = extract32(ctx->opcode, 27, 5); + + tcg_gen_mov_i32(tcg_ctx, r1_local, tcg_r1); + tcg_gen_mov_i32(tcg_ctx, r2_local, tcg_r2); + + int_cond = extract32(ctx->opcode, 17, 4); + TCGv condResult = condition_satisfied(tcg_ctx, int_cond); + cont = gen_new_label(tcg_ctx); + + tcg_gen_mov_tl(tcg_ctx, r3_local, r2_local); + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_NE, condResult, 0x1, cont); + tcg_gen_mov_tl(tcg_ctx, r3_local, r1_local); + gen_set_label(tcg_ctx, cont); + + gen_set_gpr(tcg_ctx, int_rs3, r3_local); + + tcg_temp_free(tcg_ctx, condResult); + tcg_temp_free_i32(tcg_ctx, r1_local); + tcg_temp_free_i32(tcg_ctx, r2_local); + tcg_temp_free_i32(tcg_ctx, r3_local); + } + break; + + case OPC_RH850_CMOV_cccc_imm5_reg2_reg3: { + + TCGv r3_local = tcg_temp_local_new_i32(tcg_ctx); + + if (int_imm & 0x10) { // if is sign bit in imm5 set + int_imm = int_imm | 0xffffffe0; + } + + int_cond = extract32(ctx->opcode, 17, 4); + TCGv condResult = condition_satisfied(tcg_ctx, int_cond); + cont = gen_new_label(tcg_ctx); + + tcg_gen_mov_tl(tcg_ctx, r3_local, tcg_r2); + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_NE, condResult, 0x1, cont); + tcg_gen_movi_tl(tcg_ctx, r3_local, int_imm); + + gen_set_label(tcg_ctx, cont); + + int_rs3 = extract32(ctx->opcode, 27, 5); + gen_set_gpr(tcg_ctx, int_rs3, r3_local); + + tcg_temp_free(tcg_ctx, condResult); + tcg_temp_free_i32(tcg_ctx, r3_local); + } + break; + + case OPC_RH850_HSH_reg2_reg3: + + int_rs3 = extract32(ctx->opcode, 27, 5); + gen_set_gpr(tcg_ctx, int_rs3, tcg_r2); + + tcg_gen_shri_i32(tcg_ctx, cpu_SF, tcg_r2, 0x1f); + tcg_gen_andi_i32(tcg_ctx, tcg_temp, tcg_r2, 0x0000ffff); + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_EQ, cpu_ZF, tcg_temp, 0x0); + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_EQ, cpu_CYF, tcg_temp, 0x0); + tcg_gen_movi_i32(tcg_ctx, cpu_OVF, 0x0); + break; + + case OPC_RH850_HSW_reg2_reg3: { + TCGv r2_local = tcg_temp_local_new_i32(tcg_ctx); + TCGv r3_local = tcg_temp_local_new_i32(tcg_ctx); + TCGv temp_local = tcg_temp_local_new_i32(tcg_ctx); + TCGv temp2_local = tcg_temp_local_new_i32(tcg_ctx); + TCGv temp3_local = tcg_temp_local_new_i32(tcg_ctx); + TCGv count_local = tcg_temp_local_new_i32(tcg_ctx); + + cont = gen_new_label(tcg_ctx); + end = gen_new_label(tcg_ctx); + set = gen_new_label(tcg_ctx); + + tcg_gen_mov_i32(tcg_ctx, r2_local, tcg_r2); + + int_rs3 = extract32(ctx->opcode, 27, 5); + gen_get_gpr(tcg_ctx, r3_local,int_rs3); + + tcg_gen_andi_tl(tcg_ctx, temp_local, r2_local, 0xffff); + tcg_gen_shli_tl(tcg_ctx, temp_local, temp_local, 0x10); + tcg_gen_andi_tl(tcg_ctx, temp2_local, r2_local, 0xffff0000); + tcg_gen_shri_tl(tcg_ctx, temp2_local, temp2_local, 0x10); + + tcg_gen_or_tl(tcg_ctx, r3_local, temp2_local, temp_local); + gen_set_gpr(tcg_ctx, int_rs3, r3_local); + + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_EQ, cpu_ZF, r3_local, 0x0); + tcg_gen_shri_i32(tcg_ctx, cpu_SF, r3_local, 0x1f); + + tcg_gen_movi_i32(tcg_ctx, count_local, 0x0); + + gen_set_label(tcg_ctx, cont);//// + + tcg_gen_andi_i32(tcg_ctx, temp3_local, r3_local, 0x0000ffff); + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_EQ, temp3_local, 0x0, set);//// + tcg_gen_andi_i32(tcg_ctx, temp3_local, r3_local, 0xffff0000); + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_EQ, temp3_local, 0x0, set); + //tcg_gen_addi_i32(count_local, count_local, 0x1); + //tcg_gen_shri_i32(r3_local, r3_local, 0x1); + //tcg_gen_brcondi_i32(TCG_COND_NE, count_local, 0x11, cont);//// + tcg_gen_movi_i32(tcg_ctx, cpu_CYF, 0x0); + tcg_gen_br(tcg_ctx, end); + + gen_set_label(tcg_ctx, set);//// + tcg_gen_movi_i32(tcg_ctx, cpu_CYF, 0x1); + + gen_set_label(tcg_ctx, end);//// + tcg_gen_movi_i32(tcg_ctx, cpu_OVF, 0x0); + + tcg_temp_free(tcg_ctx, r2_local); + tcg_temp_free(tcg_ctx, r3_local); + tcg_temp_free(tcg_ctx, count_local); + tcg_temp_free(tcg_ctx, temp_local); + tcg_temp_free(tcg_ctx, temp2_local); + tcg_temp_free(tcg_ctx, temp3_local); + } + break; + + case OPC_RH850_ROTL_imm5_reg2_reg3: + { + TCGv r3_local = tcg_temp_local_new_i32(tcg_ctx); + TCGv imm_local = tcg_temp_local_new_i32(tcg_ctx); + cont = gen_new_label(tcg_ctx); + + tcg_gen_movi_tl(tcg_ctx, tcg_imm, int_imm); + tcg_gen_ext8u_tl(tcg_ctx, tcg_imm, tcg_imm); + int_rs3 = extract32(ctx->opcode, 27, 5); + gen_get_gpr(tcg_ctx, tcg_r3,int_rs3); + tcg_gen_rotl_tl(tcg_ctx, tcg_r3, tcg_r2, tcg_imm); + gen_set_gpr(tcg_ctx, int_rs3, tcg_r3); + + tcg_gen_andi_i32(tcg_ctx, cpu_CYF, tcg_r3, 0x1); + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_EQ, cpu_ZF, tcg_r3, 0x0); + tcg_gen_shri_i32(tcg_ctx, cpu_SF, tcg_r3, 0x1f); + tcg_gen_movi_i32(tcg_ctx, cpu_OVF, 0x0); + + tcg_gen_mov_i32(tcg_ctx, r3_local, tcg_r3); + tcg_gen_mov_i32(tcg_ctx, imm_local, tcg_imm); + + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_NE, tcg_imm, 0x0, cont); + tcg_gen_movi_i32(tcg_ctx, cpu_CYF, 0x0); + gen_set_label(tcg_ctx, cont); + + tcg_temp_free(tcg_ctx, r3_local); + tcg_temp_free(tcg_ctx, imm_local); + } break; + + case OPC_RH850_ROTL_reg1_reg2_reg3: + { + TCGv r3_local = tcg_temp_local_new_i32(tcg_ctx); + TCGv r1_local = tcg_temp_local_new_i32(tcg_ctx); + cont = gen_new_label(tcg_ctx); + + int_rs3 = extract32(ctx->opcode, 27, 5); + gen_get_gpr(tcg_ctx, tcg_r3,int_rs3); + tcg_gen_rotl_tl(tcg_ctx, tcg_r3, tcg_r2, tcg_r1); + gen_set_gpr(tcg_ctx, int_rs3, tcg_r3); + + tcg_gen_andi_i32(tcg_ctx, cpu_CYF, tcg_r3, 0x1); + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_EQ, cpu_ZF, tcg_r3, 0x0); + tcg_gen_shri_i32(tcg_ctx, cpu_SF, tcg_r3, 0x1f); + tcg_gen_movi_i32(tcg_ctx, cpu_OVF, 0x0); + + tcg_gen_mov_i32(tcg_ctx, r3_local, tcg_r3); + tcg_gen_mov_i32(tcg_ctx, r1_local, tcg_r1); + + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_NE, tcg_r1, 0x0, cont); + tcg_gen_movi_i32(tcg_ctx, cpu_CYF, 0x0); + gen_set_label(tcg_ctx, cont); + + tcg_temp_free(tcg_ctx, r3_local); + tcg_temp_free(tcg_ctx, r1_local); + } break; + + case OPC_RH850_SAR_reg1_reg2: { + + TCGv r1_local = tcg_temp_local_new_i32(tcg_ctx); + TCGv r2_local = tcg_temp_local_new_i32(tcg_ctx); + cont = gen_new_label(tcg_ctx); + end = gen_new_label(tcg_ctx); + + tcg_gen_mov_i32(tcg_ctx, r1_local, tcg_r1); + tcg_gen_andi_i32(tcg_ctx, r1_local, r1_local, 0x1f); //shift by value of lower 5 bits of reg1 + tcg_gen_mov_i32(tcg_ctx, r2_local, tcg_r2); + + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_NE, r1_local, 0x0, cont); + tcg_gen_movi_i32(tcg_ctx, cpu_CYF, 0x0); + tcg_gen_br(tcg_ctx, end); + + gen_set_label(tcg_ctx, cont); + + tcg_gen_subi_i32(tcg_ctx, r1_local, r1_local, 0x1); //shift by r1-1 + + tcg_gen_sar_i32(tcg_ctx, r2_local, r2_local, r1_local); + tcg_gen_andi_i32(tcg_ctx, cpu_CYF, r2_local, 0x1); //LSB here is the last bit to be shifted + tcg_gen_sari_i32(tcg_ctx, r2_local, r2_local, 0x1); + + gen_set_label(tcg_ctx, end); + + gen_set_gpr(tcg_ctx, rs2, r2_local); + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_EQ, cpu_ZF, r2_local, 0x0); + tcg_gen_shri_i32(tcg_ctx, cpu_SF, r2_local, 0x1f); + tcg_gen_movi_i32(tcg_ctx, cpu_OVF, 0x0); + + tcg_temp_free(tcg_ctx, r2_local); + tcg_temp_free(tcg_ctx, r1_local); + } + break; + + case OPC_RH850_SAR_imm5_reg2: { + + TCGv r1_local = tcg_temp_local_new_i32(tcg_ctx); + TCGv r2_local = tcg_temp_local_new_i32(tcg_ctx); + cont = gen_new_label(tcg_ctx); + end = gen_new_label(tcg_ctx); + + tcg_gen_movi_tl(tcg_ctx, r1_local, int_imm); + tcg_gen_ext8u_i32(tcg_ctx, r1_local, r1_local); + tcg_gen_mov_i32(tcg_ctx, r2_local, tcg_r2); + + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_NE, r1_local, 0x0, cont); + tcg_gen_movi_i32(tcg_ctx, cpu_CYF, 0x0); + tcg_gen_br(tcg_ctx, end); + + gen_set_label(tcg_ctx, cont); + + tcg_gen_subi_i32(tcg_ctx, r1_local, r1_local, 0x1); //shift by one less + tcg_gen_sar_i32(tcg_ctx, r2_local, r2_local, r1_local); + tcg_gen_andi_i32(tcg_ctx, cpu_CYF, r2_local, 0x1); //LSB here is the last bit to be shifted + tcg_gen_sari_i32(tcg_ctx, r2_local, r2_local, 0x1); + + gen_set_label(tcg_ctx, end); + + gen_set_gpr(tcg_ctx, rs2, r2_local); + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_EQ, cpu_ZF, r2_local, 0x0); + tcg_gen_shri_i32(tcg_ctx, cpu_SF, r2_local, 0x1f); + tcg_gen_movi_i32(tcg_ctx, cpu_OVF, 0x0); + + tcg_temp_free(tcg_ctx, r2_local); + tcg_temp_free(tcg_ctx, r1_local); + } + break; + + case OPC_RH850_SAR_reg1_reg2_reg3: { + + TCGv r1_local = tcg_temp_local_new_i32(tcg_ctx); + TCGv r2_local = tcg_temp_local_new_i32(tcg_ctx); + TCGv r3_local = tcg_temp_local_new_i32(tcg_ctx); + cont = gen_new_label(tcg_ctx); + end = gen_new_label(tcg_ctx); + + tcg_gen_mov_i32(tcg_ctx, r1_local, tcg_r1); + tcg_gen_andi_i32(tcg_ctx, r1_local, r1_local, 0x1f); //shift by only lower 5 bits of reg1 + tcg_gen_mov_i32(tcg_ctx, r2_local, tcg_r2); + int_rs3 = extract32(ctx->opcode, 27, 5); + gen_get_gpr(tcg_ctx, r3_local, int_rs3); + + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_NE, r1_local, 0x0, cont); //is non-shift? + tcg_gen_movi_i32(tcg_ctx, cpu_CYF, 0x0); + tcg_gen_mov_i32(tcg_ctx, r3_local, r2_local); + tcg_gen_br(tcg_ctx, end); + + gen_set_label(tcg_ctx, cont); + + + tcg_gen_subi_i32(tcg_ctx, r1_local, r1_local, 0x1); //shift by one less + tcg_gen_sar_i32(tcg_ctx, r3_local, r2_local, r1_local); + tcg_gen_andi_i32(tcg_ctx, cpu_CYF, r3_local, 0x1); //LSB here is the last bit to be shifted + tcg_gen_sari_i32(tcg_ctx, r3_local, r3_local, 0x1); + + gen_set_label(tcg_ctx, end); + + gen_set_gpr(tcg_ctx, int_rs3, r3_local); + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_EQ, cpu_ZF, r3_local, 0x0); + tcg_gen_shri_i32(tcg_ctx, cpu_SF, r3_local, 0x1f); + tcg_gen_movi_i32(tcg_ctx, cpu_OVF, 0x0); + + tcg_temp_free(tcg_ctx, r3_local); + tcg_temp_free(tcg_ctx, r2_local); + tcg_temp_free(tcg_ctx, r1_local); + } + break; + + case OPC_RH850_SASF_cccc_reg2: { + TCGv r2_local = tcg_temp_local_new_i32(tcg_ctx); + TCGv operand_local = tcg_temp_local_new_i32(tcg_ctx); + + int_cond = extract32(ctx->opcode,0,4); + TCGv condResult = condition_satisfied(tcg_ctx, int_cond); + cont = gen_new_label(tcg_ctx); + + tcg_gen_shli_tl(tcg_ctx, r2_local, tcg_r2, 0x1); + + tcg_gen_movi_i32(tcg_ctx, operand_local, 0x00000000); + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_NE, condResult, 0x1, cont); + tcg_gen_movi_i32(tcg_ctx, operand_local, 0x00000001); + + gen_set_label(tcg_ctx, cont); + tcg_gen_or_tl(tcg_ctx, r2_local, r2_local, operand_local); + + gen_set_gpr(tcg_ctx, rs2, r2_local); + + tcg_temp_free(tcg_ctx, r2_local); + tcg_temp_free(tcg_ctx, operand_local); + tcg_temp_free(tcg_ctx, condResult); + } + break; + + case OPC_RH850_SETF_cccc_reg2:{ + + TCGv operand_local = tcg_temp_local_new_i32(tcg_ctx); + int_cond = extract32(ctx->opcode,0,4); + TCGv condResult = condition_satisfied(tcg_ctx, int_cond); + cont = gen_new_label(tcg_ctx); + + tcg_gen_movi_i32(tcg_ctx, operand_local, 0x00000000); + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_NE, condResult, 0x1, cont); + tcg_gen_movi_i32(tcg_ctx, operand_local, 0x00000001); + + gen_set_label(tcg_ctx, cont); + + gen_set_gpr(tcg_ctx, rs2, operand_local); + + tcg_temp_free(tcg_ctx, condResult); + tcg_temp_free(tcg_ctx, operand_local); + } + break; + + case OPC_RH850_SHL_reg1_reg2: { + + TCGv r1_local = tcg_temp_local_new_i32(tcg_ctx); + TCGv r2_local = tcg_temp_local_new_i32(tcg_ctx); + TCGv temp_local = tcg_temp_local_new_i32(tcg_ctx); + + tcg_gen_mov_i32(tcg_ctx, r1_local, tcg_r1); + tcg_gen_mov_i32(tcg_ctx, r2_local, tcg_r2); + + cont = gen_new_label(tcg_ctx); + end = gen_new_label(tcg_ctx); + + tcg_gen_andi_i32(tcg_ctx, r1_local, r1_local, 0x1f); //get only lower 5 bits + + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_EQ, r1_local, 0x0, cont); + + tcg_gen_subi_i32(tcg_ctx, temp_local, r1_local, 0x1); // shifting for [r1]-1 + tcg_gen_shl_tl(tcg_ctx, r2_local, r2_local, temp_local); + + tcg_gen_shri_i32(tcg_ctx, cpu_CYF, r2_local, 0x1f); // checking the last bit to shift + tcg_gen_shli_i32(tcg_ctx, r2_local, r2_local, 0x1); // shifting for that remaining 1 + + gen_set_gpr(tcg_ctx, rs2, r2_local); + tcg_gen_br(tcg_ctx, end); + + gen_set_label(tcg_ctx, cont);//// + tcg_gen_movi_i32(tcg_ctx, cpu_CYF, 0x0); + + gen_set_label(tcg_ctx, end);//// + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_EQ, cpu_ZF, r2_local, 0x0); + tcg_gen_shri_i32(tcg_ctx, cpu_SF, r2_local, 0x1f); + tcg_gen_movi_i32(tcg_ctx, cpu_OVF, 0x0); + + tcg_temp_free(tcg_ctx, r1_local); + tcg_temp_free(tcg_ctx, r2_local); + tcg_temp_free(tcg_ctx, temp_local); + } + break; + + case OPC_RH850_SHL_imm5_reg2: { + + TCGv r1_local = tcg_temp_local_new_i32(tcg_ctx); + TCGv r2_local = tcg_temp_local_new_i32(tcg_ctx); + TCGv temp_local = tcg_temp_local_new_i32(tcg_ctx); + + tcg_gen_mov_i32(tcg_ctx, r2_local, tcg_r2); + + tcg_gen_movi_tl(tcg_ctx, r1_local, int_imm); + tcg_gen_ext8u_tl(tcg_ctx, r1_local, r1_local); + + cont = gen_new_label(tcg_ctx); + end = gen_new_label(tcg_ctx); + + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_EQ, r1_local, 0x0, cont); + + tcg_gen_subi_i32(tcg_ctx, temp_local, r1_local, 0x1); + tcg_gen_shl_tl(tcg_ctx, r2_local, r2_local, temp_local); + tcg_gen_shri_i32(tcg_ctx, cpu_CYF, r2_local, 0x1f); + tcg_gen_shli_tl(tcg_ctx, r2_local, r2_local, 0x1); + tcg_gen_br(tcg_ctx, end); + + gen_set_label(tcg_ctx, cont);//// + tcg_gen_movi_i32(tcg_ctx, cpu_CYF, 0x0); + + gen_set_label(tcg_ctx, end);//// + gen_set_gpr(tcg_ctx, rs2, r2_local); + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_EQ, cpu_ZF, r2_local, 0x0); + tcg_gen_shri_i32(tcg_ctx, cpu_SF, r2_local, 0x1f); + tcg_gen_movi_i32(tcg_ctx, cpu_OVF, 0x0); + + tcg_temp_free(tcg_ctx, r1_local); + tcg_temp_free(tcg_ctx, r2_local); + tcg_temp_free(tcg_ctx, temp_local); + } + break; + + case OPC_RH850_SHL_reg1_reg2_reg3: { + + TCGv r1_local = tcg_temp_local_new_i32(tcg_ctx); + TCGv r2_local = tcg_temp_local_new_i32(tcg_ctx); + TCGv r3_local = tcg_temp_local_new_i32(tcg_ctx); + TCGv temp_local = tcg_temp_local_new_i32(tcg_ctx); + + tcg_gen_mov_i32(tcg_ctx, r1_local, tcg_r1); + tcg_gen_andi_i32(tcg_ctx, r1_local, r1_local, 0x1f); + tcg_gen_mov_i32(tcg_ctx, r2_local, tcg_r2); + + int_rs3 = extract32(ctx->opcode, 27, 5); + gen_get_gpr(tcg_ctx, r3_local,int_rs3); + + cont = gen_new_label(tcg_ctx); + end = gen_new_label(tcg_ctx); + + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_EQ, r1_local, 0x0, cont); // when reg1 = 0, do not shift + + tcg_gen_subi_i32(tcg_ctx, temp_local, r1_local, 0x1); + tcg_gen_shl_tl(tcg_ctx, r3_local, r2_local, temp_local); + + tcg_gen_shri_i32(tcg_ctx, cpu_CYF, r3_local, 0x1f); + tcg_gen_shli_tl(tcg_ctx, r3_local, r3_local, 0x1); + tcg_gen_br(tcg_ctx, end); + + gen_set_label(tcg_ctx, cont);//// + tcg_gen_mov_i32(tcg_ctx, r3_local, r2_local); + tcg_gen_movi_i32(tcg_ctx, cpu_CYF, 0x0); + + gen_set_label(tcg_ctx, end);//// + gen_set_gpr(tcg_ctx, int_rs3, r3_local); + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_EQ, cpu_ZF, r3_local, 0x0); + tcg_gen_shri_i32(tcg_ctx, cpu_SF, r3_local, 0x1f); + tcg_gen_movi_i32(tcg_ctx, cpu_OVF, 0x0); + + tcg_temp_free(tcg_ctx, r1_local); + tcg_temp_free(tcg_ctx, r2_local); + tcg_temp_free(tcg_ctx, r3_local); + tcg_temp_free(tcg_ctx, temp_local); + } + break; + + case OPC_RH850_SHR_reg1_reg2: { + + TCGv r1_local = tcg_temp_local_new_i32(tcg_ctx); + TCGv r2_local = tcg_temp_local_new_i32(tcg_ctx); + TCGv temp_local = tcg_temp_local_new_i32(tcg_ctx); + cont = gen_new_label(tcg_ctx); + end = gen_new_label(tcg_ctx); + + tcg_gen_mov_i32(tcg_ctx, r1_local, tcg_r1); + tcg_gen_andi_i32(tcg_ctx, r1_local, r1_local, 0x1f); // + tcg_gen_mov_i32(tcg_ctx, r2_local, tcg_r2); + + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_EQ, r1_local, 0x0, cont); //checking for non-shift + + tcg_gen_subi_i32(tcg_ctx, temp_local, r1_local, 0x1); // shifting for [r1]-1 + tcg_gen_shr_tl(tcg_ctx, r2_local, r2_local, temp_local); + + + tcg_gen_andi_i32(tcg_ctx, cpu_CYF, r2_local, 0x1); // checking the last bit to shift (LSB) + tcg_gen_shri_i32(tcg_ctx, r2_local, r2_local, 0x1); + + tcg_gen_br(tcg_ctx, end); + + gen_set_label(tcg_ctx, cont); + tcg_gen_movi_i32(tcg_ctx, cpu_CYF, 0x0); + + gen_set_label(tcg_ctx, end); + gen_set_gpr(tcg_ctx, rs2, r2_local); + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_EQ, cpu_ZF, r2_local, 0x0); + tcg_gen_shri_i32(tcg_ctx, cpu_SF, r2_local, 0x1f); + tcg_gen_movi_i32(tcg_ctx, cpu_OVF, 0x0); + + tcg_temp_free(tcg_ctx, r1_local); + tcg_temp_free(tcg_ctx, r2_local); + tcg_temp_free(tcg_ctx, temp_local); + } + break; + + case OPC_RH850_SHR_imm5_reg2: { + + TCGv r1_local = tcg_temp_local_new_i32(tcg_ctx); + TCGv r2_local = tcg_temp_local_new_i32(tcg_ctx); + TCGv temp_local = tcg_temp_local_new_i32(tcg_ctx); + + tcg_gen_mov_i32(tcg_ctx, r2_local, tcg_r2); + + tcg_gen_movi_tl(tcg_ctx, r1_local, int_imm); + tcg_gen_ext8u_tl(tcg_ctx, r1_local, r1_local); + + cont = gen_new_label(tcg_ctx); + end = gen_new_label(tcg_ctx); + + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_EQ, r1_local, 0x0, cont); //checking for non-shift + + tcg_gen_subi_i32(tcg_ctx, temp_local, r1_local, 0x1); // shifting for [r1]-1 + tcg_gen_shr_tl(tcg_ctx, r2_local, r2_local, temp_local); + + tcg_gen_andi_i32(tcg_ctx, cpu_CYF, r2_local, 0x1); // checking the last bit to shift (LSB) + tcg_gen_shri_i32(tcg_ctx, r2_local, r2_local, 0x1); + + tcg_gen_br(tcg_ctx, end); + + gen_set_label(tcg_ctx, cont); + tcg_gen_movi_i32(tcg_ctx, cpu_CYF, 0x0); + + gen_set_label(tcg_ctx, end); + gen_set_gpr(tcg_ctx, rs2, r2_local); + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_EQ, cpu_ZF, r2_local, 0x0); + tcg_gen_shri_i32(tcg_ctx, cpu_SF, r2_local, 0x1f); + tcg_gen_movi_i32(tcg_ctx, cpu_OVF, 0x0); + + tcg_temp_free(tcg_ctx, r1_local); + tcg_temp_free(tcg_ctx, r2_local); + tcg_temp_free(tcg_ctx, temp_local); + } + break; + + case OPC_RH850_SHR_reg1_reg2_reg3: { + + TCGv r1_local = tcg_temp_local_new_i32(tcg_ctx); + TCGv r2_local = tcg_temp_local_new_i32(tcg_ctx); + TCGv r3_local = tcg_temp_local_new_i32(tcg_ctx); + TCGv temp_local = tcg_temp_local_new_i32(tcg_ctx); + cont = gen_new_label(tcg_ctx); + end = gen_new_label(tcg_ctx); + + tcg_gen_mov_i32(tcg_ctx, r1_local, tcg_r1); + tcg_gen_andi_i32(tcg_ctx, r1_local, r1_local, 0x1f); + tcg_gen_mov_i32(tcg_ctx, r2_local, tcg_r2); + int_rs3 = extract32(ctx->opcode, 27, 5); + gen_get_gpr(tcg_ctx, r3_local, int_rs3); + + + + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_EQ, r1_local, 0x0, cont); //checking for non-shift + + tcg_gen_subi_i32(tcg_ctx, temp_local, r1_local, 0x1); // shifting for [r1]-1 + tcg_gen_shr_tl(tcg_ctx, r3_local, r2_local, temp_local); + + tcg_gen_andi_i32(tcg_ctx, cpu_CYF, r3_local, 0x1); // checking the last bit to shift (LSB) + tcg_gen_shri_i32(tcg_ctx, r3_local, r3_local, 0x1); + + tcg_gen_br(tcg_ctx, end); + + gen_set_label(tcg_ctx, cont); + tcg_gen_movi_i32(tcg_ctx, cpu_CYF, 0x0); + tcg_gen_mov_i32(tcg_ctx, r3_local, r2_local); + + gen_set_label(tcg_ctx, end); + gen_set_gpr(tcg_ctx, int_rs3, r3_local); + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_EQ, cpu_ZF, r3_local, 0x0); + tcg_gen_shri_i32(tcg_ctx, cpu_SF, r3_local, 0x1f); + tcg_gen_movi_i32(tcg_ctx, cpu_OVF, 0x0); + + tcg_temp_free(tcg_ctx, r1_local); + tcg_temp_free(tcg_ctx, r2_local); + tcg_temp_free(tcg_ctx, r3_local); + tcg_temp_free(tcg_ctx, temp_local); + } + break; + + case OPC_RH850_SXB_reg1: + tcg_gen_andi_tl(tcg_ctx, tcg_r1, tcg_r1,0xFF); + tcg_gen_ext8s_tl(tcg_ctx, tcg_r1, tcg_r1); + gen_set_gpr(tcg_ctx, rs1, tcg_r1); + break; + + case OPC_RH850_SXH_reg1: + tcg_gen_andi_tl(tcg_ctx, tcg_r1, tcg_r1,0xFFFF); + tcg_gen_ext16s_tl(tcg_ctx, tcg_r1, tcg_r1); + gen_set_gpr(tcg_ctx, rs1, tcg_r1); + break; + + case OPC_RH850_ZXH_reg1: + tcg_gen_andi_tl(tcg_ctx, tcg_r1, tcg_r1,0xFFFF); + tcg_gen_ext16u_tl(tcg_ctx, tcg_r1, tcg_r1); + gen_set_gpr(tcg_ctx, rs1, tcg_r1); + break; + + case OPC_RH850_ZXB_reg1: + tcg_gen_andi_tl(tcg_ctx, tcg_r1, tcg_r1,0xFF); + tcg_gen_ext8u_tl(tcg_ctx, tcg_r1, tcg_r1); + gen_set_gpr(tcg_ctx, rs1, tcg_r1); + break; + } + + tcg_temp_free(tcg_ctx, tcg_r1); + tcg_temp_free(tcg_ctx, tcg_r2); + tcg_temp_free(tcg_ctx, tcg_r3); + tcg_temp_free(tcg_ctx, tcg_imm); + tcg_temp_free(tcg_ctx, tcg_temp); + tcg_temp_free(tcg_ctx, tcg_temp2); + tcg_temp_free(tcg_ctx, insert); +} + +static void gen_bit_search(DisasContext *ctx, int rs2, int operation) +{ + TCGContext *tcg_ctx = ctx->uc->tcg_ctx; + + TCGv tcg_r2 = tcg_temp_new(tcg_ctx); + TCGv tcg_r3 = tcg_temp_new(tcg_ctx); + int int_rs3; + int_rs3 = extract32(ctx->opcode, 27, 5); + + gen_get_gpr(tcg_ctx, tcg_r2, rs2); + gen_get_gpr(tcg_ctx, tcg_r3, int_rs3); + + TCGLabel *end; + TCGLabel *found; + TCGLabel *loop; + + switch(operation){ + case OPC_RH850_SCH0L_reg2_reg3: { + + TCGv foundFlag = tcg_temp_local_new(tcg_ctx); + TCGv r2_local = tcg_temp_local_new(tcg_ctx); + TCGv r3_local = tcg_temp_local_new(tcg_ctx); + TCGv result = tcg_temp_local_new(tcg_ctx); + TCGv check = tcg_temp_local_new(tcg_ctx); + TCGv count = tcg_temp_local_new(tcg_ctx); + tcg_gen_mov_i32(tcg_ctx, r2_local, tcg_r2); + tcg_gen_mov_i32(tcg_ctx, r3_local, tcg_r3); + tcg_gen_movi_i32(tcg_ctx, count, 0x0); + + end = gen_new_label(tcg_ctx); + found = gen_new_label(tcg_ctx); + loop = gen_new_label(tcg_ctx); + + gen_set_label(tcg_ctx, loop);//--------------------------------------------------- + + tcg_gen_shl_i32(tcg_ctx, check, r2_local, count); + tcg_gen_ori_i32(tcg_ctx, check, check, 0x7fffffff); // check MSB bit + tcg_gen_brcondi_tl(tcg_ctx, TCG_COND_EQ, check, 0x7fffffff, found); + + tcg_gen_addi_i32(tcg_ctx, count, count, 0x1); + tcg_gen_brcondi_tl(tcg_ctx, TCG_COND_NE, count, 0x20, loop);//-------------------- + + tcg_gen_movi_i32(tcg_ctx, result, 0x0); + tcg_gen_movi_i32(tcg_ctx, foundFlag, 0x0); + tcg_gen_br(tcg_ctx, end); + + gen_set_label(tcg_ctx, found); + tcg_gen_movi_i32(tcg_ctx, foundFlag, 0x1); + tcg_gen_addi_i32(tcg_ctx, result, count, 0x1); + + //tcg_gen_brcondi_tl(TCG_COND_NE, result, 0x20, end); + //tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_CYF, foundFlag, 0x1); //setting CY if found at the end + + gen_set_label(tcg_ctx, end); + + gen_set_gpr(tcg_ctx, int_rs3, result); + + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_NE, cpu_ZF, foundFlag, 0x1); //setting Z if not found + tcg_gen_movi_i32(tcg_ctx, cpu_OVF, 0x0); + tcg_gen_movi_i32(tcg_ctx, cpu_SF, 0x0); + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_EQ, cpu_CYF, r2_local, 0xfffffffe); //setting CY if found at the end + + tcg_temp_free(tcg_ctx, foundFlag); + tcg_temp_free(tcg_ctx, r2_local); + tcg_temp_free(tcg_ctx, r3_local); + tcg_temp_free(tcg_ctx, check); + tcg_temp_free(tcg_ctx, count); + tcg_temp_free(tcg_ctx, result); + } break; + + case OPC_RH850_SCH0R_reg2_reg3: { + + TCGv foundFlag = tcg_temp_local_new(tcg_ctx); + TCGv r2_local = tcg_temp_local_new(tcg_ctx); + TCGv r3_local = tcg_temp_local_new(tcg_ctx); + TCGv result = tcg_temp_local_new(tcg_ctx); + TCGv check = tcg_temp_local_new(tcg_ctx); + TCGv count = tcg_temp_local_new(tcg_ctx); + tcg_gen_mov_i32(tcg_ctx, r2_local, tcg_r2); + tcg_gen_mov_i32(tcg_ctx, r3_local, tcg_r3); + tcg_gen_movi_i32(tcg_ctx, count, 0x0); + + end = gen_new_label(tcg_ctx); + found = gen_new_label(tcg_ctx); + loop = gen_new_label(tcg_ctx); + + gen_set_label(tcg_ctx, loop);//--------------------------------------------------- + + tcg_gen_shr_i32(tcg_ctx, check, r2_local, count); + tcg_gen_ori_i32(tcg_ctx, check, check, 0xfffffffe); // check MSB bit + tcg_gen_brcondi_tl(tcg_ctx, TCG_COND_EQ, check, 0xfffffffe, found); + + tcg_gen_addi_i32(tcg_ctx, count, count, 0x1); + tcg_gen_brcondi_tl(tcg_ctx, TCG_COND_NE, count, 0x20, loop);//-------------------- + + tcg_gen_movi_i32(tcg_ctx, result, 0x0); + tcg_gen_movi_i32(tcg_ctx, foundFlag, 0x0); + tcg_gen_br(tcg_ctx, end); + + gen_set_label(tcg_ctx, found); + tcg_gen_movi_i32(tcg_ctx, foundFlag, 0x1); + tcg_gen_addi_i32(tcg_ctx, result, count, 0x1); + + //tcg_gen_brcondi_tl(TCG_COND_NE, result, 0x20, end); + //tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_CYF, foundFlag, 0x1); //setting CY if found + + gen_set_label(tcg_ctx, end); + + gen_set_gpr(tcg_ctx, int_rs3, result); + + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_NE, cpu_ZF, foundFlag, 0x1); //setting Z if not found + tcg_gen_movi_i32(tcg_ctx, cpu_OVF, 0x0); + tcg_gen_movi_i32(tcg_ctx, cpu_SF, 0x0); + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_EQ, cpu_CYF, r2_local, 0x7fffffff); + + tcg_temp_free(tcg_ctx, foundFlag); + tcg_temp_free(tcg_ctx, r2_local); + tcg_temp_free(tcg_ctx, r3_local); + tcg_temp_free(tcg_ctx, check); + tcg_temp_free(tcg_ctx, count); + tcg_temp_free(tcg_ctx, result); + } break; + + case OPC_RH850_SCH1L_reg2_reg3: { + + TCGv foundFlag = tcg_temp_local_new(tcg_ctx); + TCGv r2_local = tcg_temp_local_new(tcg_ctx); + TCGv r3_local = tcg_temp_local_new(tcg_ctx); + TCGv result = tcg_temp_local_new(tcg_ctx); + TCGv check = tcg_temp_local_new(tcg_ctx); + TCGv count = tcg_temp_local_new(tcg_ctx); + tcg_gen_mov_i32(tcg_ctx, r2_local, tcg_r2); + tcg_gen_mov_i32(tcg_ctx, r3_local, tcg_r3); + tcg_gen_movi_i32(tcg_ctx, count, 0x0); + + end = gen_new_label(tcg_ctx); + found = gen_new_label(tcg_ctx); + loop = gen_new_label(tcg_ctx); + + gen_set_label(tcg_ctx, loop);//--------------------------------------------------- + + tcg_gen_shl_i32(tcg_ctx, check, r2_local, count); + tcg_gen_andi_i32(tcg_ctx, check, check, 0x80000000); // check MSB bit + tcg_gen_brcondi_tl(tcg_ctx, TCG_COND_EQ, check, 0x80000000, found); + + tcg_gen_addi_i32(tcg_ctx, count, count, 0x1); + tcg_gen_brcondi_tl(tcg_ctx, TCG_COND_NE, count, 0x20, loop);//-------------------- + + tcg_gen_movi_i32(tcg_ctx, result, 0x0); + tcg_gen_movi_i32(tcg_ctx, foundFlag, 0x0); + tcg_gen_br(tcg_ctx, end); + + gen_set_label(tcg_ctx, found); + tcg_gen_movi_i32(tcg_ctx, foundFlag, 0x1); + tcg_gen_addi_i32(tcg_ctx, result, count, 0x1); + + //tcg_gen_brcondi_tl(TCG_COND_NE, result, 0x20, end); + //tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_CYF, foundFlag, 0x1); //setting CY if found + + gen_set_label(tcg_ctx, end); + + gen_set_gpr(tcg_ctx, int_rs3, result); + + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_NE, cpu_ZF, foundFlag, 0x1); //setting Z if not found + tcg_gen_movi_i32(tcg_ctx, cpu_OVF, 0x0); + tcg_gen_movi_i32(tcg_ctx, cpu_SF, 0x0); + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_EQ, cpu_CYF, r2_local, 0x1); + + tcg_temp_free(tcg_ctx, foundFlag); + tcg_temp_free(tcg_ctx, r2_local); + tcg_temp_free(tcg_ctx, r3_local); + tcg_temp_free(tcg_ctx, check); + tcg_temp_free(tcg_ctx, count); + tcg_temp_free(tcg_ctx, result); + } break; + + case OPC_RH850_SCH1R_reg2_reg3: { + + TCGv foundFlag = tcg_temp_local_new(tcg_ctx); + TCGv r2_local = tcg_temp_local_new(tcg_ctx); + TCGv r3_local = tcg_temp_local_new(tcg_ctx); + TCGv result = tcg_temp_local_new(tcg_ctx); + TCGv check = tcg_temp_local_new(tcg_ctx); + TCGv count = tcg_temp_local_new(tcg_ctx); + + tcg_gen_mov_i32(tcg_ctx, r2_local, tcg_r2); + tcg_gen_mov_i32(tcg_ctx, r3_local, tcg_r3); + tcg_gen_movi_i32(tcg_ctx, count, 0x0); + + end = gen_new_label(tcg_ctx); + found = gen_new_label(tcg_ctx); + loop = gen_new_label(tcg_ctx); + + gen_set_label(tcg_ctx, loop);//--------------------------------------------------- + + tcg_gen_shr_i32(tcg_ctx, check, r2_local, count); + tcg_gen_andi_i32(tcg_ctx, check, check, 0x1); // check MSB bit + tcg_gen_brcondi_tl(tcg_ctx, TCG_COND_EQ, check, 0x1, found); + + tcg_gen_addi_i32(tcg_ctx, count, count, 0x1); + tcg_gen_brcondi_tl(tcg_ctx, TCG_COND_NE, count, 0x20, loop);//-------------------- + + tcg_gen_movi_i32(tcg_ctx, result, 0x0); + tcg_gen_movi_i32(tcg_ctx, foundFlag, 0x0); + tcg_gen_br(tcg_ctx, end); + + gen_set_label(tcg_ctx, found); + tcg_gen_movi_i32(tcg_ctx, foundFlag, 0x1); + tcg_gen_addi_i32(tcg_ctx, result, count, 0x1); + + //tcg_gen_brcondi_tl(TCG_COND_NE, result, 0x20, end); + //tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_CYF, foundFlag, 0x1); //setting CY if found + + gen_set_label(tcg_ctx, end); + + gen_set_gpr(tcg_ctx, int_rs3, result); + + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_NE, cpu_ZF, foundFlag, 0x1); //setting Z if not found + tcg_gen_movi_i32(tcg_ctx, cpu_OVF, 0x0); + tcg_gen_movi_i32(tcg_ctx, cpu_SF, 0x0); + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_EQ, cpu_CYF, r2_local, 0x80000000); + + tcg_temp_free(tcg_ctx, foundFlag); + tcg_temp_free(tcg_ctx, r2_local); + tcg_temp_free(tcg_ctx, r3_local); + tcg_temp_free(tcg_ctx, check); + tcg_temp_free(tcg_ctx, count); + tcg_temp_free(tcg_ctx, result); + } break; + } + + tcg_temp_free(tcg_ctx, tcg_r2); + tcg_temp_free(tcg_ctx, tcg_r3); +} + +static void gen_divide(DisasContext *ctx, int rs1, int rs2, int operation) +{ + TCGContext *tcg_ctx = ctx->uc->tcg_ctx; + + TCGv tcg_r1 = tcg_temp_new(tcg_ctx); + TCGv tcg_r2 = tcg_temp_new(tcg_ctx); + + gen_get_gpr(tcg_ctx, tcg_r1, rs1); + gen_get_gpr(tcg_ctx, tcg_r2, rs2); + + int int_rs3; + + TCGv tcg_r3 = tcg_temp_new(tcg_ctx); + + switch(operation){ + + case OPC_RH850_DIV_reg1_reg2_reg3:{ + + TCGLabel *cont; + TCGLabel *end; + TCGLabel *fin; + + TCGv r1_local = tcg_temp_local_new(tcg_ctx); + TCGv r2_local = tcg_temp_local_new(tcg_ctx); + TCGv r3_local = tcg_temp_local_new(tcg_ctx); + + tcg_gen_mov_i32(tcg_ctx, r1_local, tcg_r1); + tcg_gen_mov_i32(tcg_ctx, r2_local, tcg_r2); + + int_rs3 = extract32(ctx->opcode, 27, 5); + gen_get_gpr(tcg_ctx, tcg_r3, int_rs3); + tcg_gen_mov_i32(tcg_ctx, r3_local, tcg_r3); + TCGv overflowed = tcg_temp_local_new(tcg_ctx); + TCGv overflowed2 = tcg_temp_local_new(tcg_ctx); + + cont = gen_new_label(tcg_ctx); + end = gen_new_label(tcg_ctx); + fin = gen_new_label(tcg_ctx); + + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_EQ, cpu_OVF, r1_local, 0x0); + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_NE, cpu_OVF, 0x1, cont); //if r1=0 jump to end + ///// regs should be undefined!! + tcg_gen_movi_i32(tcg_ctx, r2_local, 0x80000000); + ///// + tcg_gen_br(tcg_ctx, fin); + + gen_set_label(tcg_ctx, cont); + + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_EQ, overflowed, r2_local, 0x80000000); + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_EQ, overflowed2, r1_local, 0xffffffff); + tcg_gen_and_i32(tcg_ctx, overflowed, overflowed, overflowed2); //if both + + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_EQ, cpu_OVF, overflowed, 0x1); //are 1 + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_NE, cpu_OVF, 0x1, end); + tcg_gen_movi_i32(tcg_ctx, r2_local, 0x80000000); //DO THIS + tcg_gen_movi_i32(tcg_ctx, r3_local, 0x0000); + gen_set_gpr(tcg_ctx, rs2, r2_local); //write zeros if undefined + gen_set_gpr(tcg_ctx, int_rs3, r3_local); + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_EQ, cpu_ZF, r2_local, 0x0); + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_LT, cpu_SF, r2_local, 0x0); + tcg_gen_br(tcg_ctx, fin); + + gen_set_label(tcg_ctx, end); + + tcg_gen_rem_i32(tcg_ctx, r3_local, r2_local, r1_local); + tcg_gen_div_i32(tcg_ctx, r2_local, r2_local, r1_local); + + if(rs2==int_rs3){ + gen_set_gpr(tcg_ctx, rs2, r3_local); + } else { + gen_set_gpr(tcg_ctx, rs2, r2_local); + gen_set_gpr(tcg_ctx, int_rs3, r3_local); + } + + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_LT, cpu_SF, r2_local, 0x0); + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_EQ, cpu_ZF, r2_local, 0x0); + + gen_set_label(tcg_ctx, fin); + + tcg_temp_free(tcg_ctx, overflowed); + tcg_temp_free(tcg_ctx, overflowed2); + tcg_temp_free(tcg_ctx, r1_local); + tcg_temp_free(tcg_ctx, r2_local); + tcg_temp_free(tcg_ctx, r3_local); + } break; + + case OPC_RH850_DIVH_reg1_reg2:{ + + TCGLabel *cont; + TCGLabel *end; + TCGLabel *fin; + + tcg_gen_andi_i32(tcg_ctx, tcg_r1, tcg_r1, 0x0000FFFF); + tcg_gen_ext16s_i32(tcg_ctx, tcg_r1, tcg_r1); + + TCGv r1_local = tcg_temp_local_new(tcg_ctx); + TCGv r2_local = tcg_temp_local_new(tcg_ctx); + TCGv overflowed = tcg_temp_local_new(tcg_ctx); + TCGv overflowed2 = tcg_temp_local_new(tcg_ctx); + + tcg_gen_mov_i32(tcg_ctx, r1_local, tcg_r1); + tcg_gen_mov_i32(tcg_ctx, r2_local, tcg_r2); + + cont = gen_new_label(tcg_ctx); + end = gen_new_label(tcg_ctx); + fin = gen_new_label(tcg_ctx); + + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_EQ, cpu_OVF, r1_local, 0x0); + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_NE, cpu_OVF, 0x1, cont); //if r1=0 jump to cont + tcg_gen_br(tcg_ctx, fin); + + gen_set_label(tcg_ctx, cont); + + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_EQ, overflowed, r2_local, 0x80000000); + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_EQ, overflowed2, r1_local, 0xffffffff); + tcg_gen_and_i32(tcg_ctx, overflowed, overflowed, overflowed2); //if both + + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_EQ, cpu_OVF, overflowed, 0x1); //are 1 + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_NE, cpu_OVF, 0x1, end); + tcg_gen_movi_i32(tcg_ctx, r2_local, 0x80000000); //DO THIS + tcg_gen_movi_i32(tcg_ctx, cpu_OVF, 0x1); + gen_set_gpr(tcg_ctx, rs2, r2_local); + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_LT, cpu_SF, r2_local, 0x0); + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_EQ, cpu_ZF, r2_local, 0x0); + tcg_gen_br(tcg_ctx, fin); + + gen_set_label(tcg_ctx, end); + + tcg_gen_div_i32(tcg_ctx, r2_local, r2_local, r1_local); + gen_set_gpr(tcg_ctx, rs2, r2_local); + + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_LT, cpu_SF, r2_local, 0x0); + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_EQ, cpu_ZF, r2_local, 0x0); + + gen_set_label(tcg_ctx, fin); + + tcg_temp_free(tcg_ctx, overflowed); + tcg_temp_free(tcg_ctx, overflowed2); + tcg_temp_free(tcg_ctx, r1_local); + tcg_temp_free(tcg_ctx, r2_local); + } break; + + case OPC_RH850_DIVH_reg1_reg2_reg3: { + // 0x80000000/0xffffffff=0x80000000; cpu_OVF=1, cpu_Z=1? + // reg2/0x0000=undefined; cpu_OVF=1 + // if reg2==reg3; reg2=remainder + + TCGLabel *cont; + TCGLabel *end; + TCGLabel *fin; + + TCGv r1_local = tcg_temp_local_new(tcg_ctx); + TCGv r2_local = tcg_temp_local_new(tcg_ctx); + TCGv r3_local = tcg_temp_local_new(tcg_ctx); + + tcg_gen_andi_i32(tcg_ctx, tcg_r1, tcg_r1, 0x0000FFFF); + tcg_gen_ext16s_i32(tcg_ctx, tcg_r1, tcg_r1); + tcg_gen_mov_i32(tcg_ctx, r1_local, tcg_r1); + tcg_gen_mov_i32(tcg_ctx, r2_local, tcg_r2); + + int_rs3 = extract32(ctx->opcode, 27, 5); + gen_get_gpr(tcg_ctx, tcg_r3, int_rs3); + tcg_gen_mov_i32(tcg_ctx, r3_local, tcg_r3); + TCGv overflowed = tcg_temp_local_new(tcg_ctx); + TCGv overflowed2 = tcg_temp_local_new(tcg_ctx); + + cont = gen_new_label(tcg_ctx); + end = gen_new_label(tcg_ctx); + fin = gen_new_label(tcg_ctx); + + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_EQ, cpu_OVF, r1_local, 0x0); + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_NE, cpu_OVF, 0x1, cont); + tcg_gen_br(tcg_ctx, fin); + + gen_set_label(tcg_ctx, cont); ///// + + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_EQ, overflowed, r2_local, 0x80000000); + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_EQ, overflowed2, r1_local, 0xffffffff); + tcg_gen_and_i32(tcg_ctx, overflowed, overflowed, overflowed2); // if result is 1, cpu_OVF = 1 + + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_EQ, cpu_OVF, overflowed, 0x1); + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_NE, cpu_OVF, 0x1, end); + tcg_gen_movi_i32(tcg_ctx, r2_local, 0x80000000); + tcg_gen_movi_i32(tcg_ctx, r3_local, 0x0000); + tcg_gen_movi_i32(tcg_ctx, cpu_OVF, 0x1); + gen_set_gpr(tcg_ctx, rs2, r2_local); + gen_set_gpr(tcg_ctx, int_rs3, r3_local); + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_LT, cpu_SF, r2_local, 0x0); + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_EQ, cpu_ZF, r2_local, 0x0); + tcg_gen_br(tcg_ctx, fin); + + gen_set_label(tcg_ctx, end); ///// + + tcg_gen_rem_i32(tcg_ctx, r3_local, r2_local, r1_local); + tcg_gen_div_i32(tcg_ctx, r2_local, r2_local, r1_local); + + if(rs2==int_rs3){ + gen_set_gpr(tcg_ctx, rs2, r3_local); + } else { + gen_set_gpr(tcg_ctx, rs2, r2_local); + gen_set_gpr(tcg_ctx, int_rs3, r3_local); + } + + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_LT, cpu_SF, r2_local, 0x0); + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_EQ, cpu_ZF, r2_local, 0x0); + + gen_set_label(tcg_ctx, fin); ///// + + tcg_temp_free(tcg_ctx, overflowed); + tcg_temp_free(tcg_ctx, overflowed2); + tcg_temp_free(tcg_ctx, r1_local); + tcg_temp_free(tcg_ctx, r2_local); + tcg_temp_free(tcg_ctx, r3_local); + } break; + + case OPC_RH850_DIVHU_reg1_reg2_reg3:{ + + TCGLabel *cont; + TCGLabel *fin; + + TCGv r1_local = tcg_temp_local_new(tcg_ctx); + TCGv r2_local = tcg_temp_local_new(tcg_ctx); + TCGv r3_local = tcg_temp_local_new(tcg_ctx); + + tcg_gen_andi_i32(tcg_ctx, tcg_r1, tcg_r1, 0x0000FFFF); + tcg_gen_ext16u_i32(tcg_ctx, tcg_r1, tcg_r1); + tcg_gen_mov_i32(tcg_ctx, r1_local, tcg_r1); + + tcg_gen_mov_i32(tcg_ctx, r2_local, tcg_r2); + + int_rs3 = extract32(ctx->opcode, 27, 5); + gen_get_gpr(tcg_ctx, tcg_r3, int_rs3); + tcg_gen_mov_i32(tcg_ctx, r3_local, tcg_r3); + + cont = gen_new_label(tcg_ctx); + fin = gen_new_label(tcg_ctx); + + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_EQ, cpu_OVF, r1_local, 0x0); + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_NE, cpu_OVF, 0x1, cont); + tcg_gen_br(tcg_ctx, fin); + + gen_set_label(tcg_ctx, cont); ///// + tcg_gen_remu_i32(tcg_ctx, r3_local, r2_local, r1_local); + tcg_gen_divu_i32(tcg_ctx, r2_local, r2_local, r1_local); + + if(rs2==int_rs3){ + gen_set_gpr(tcg_ctx, rs2, r3_local); + } else { + gen_set_gpr(tcg_ctx, rs2, r2_local); + gen_set_gpr(tcg_ctx, int_rs3, r3_local); + } + + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_LT, cpu_SF, r2_local, 0x0); + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_EQ, cpu_ZF, r2_local, 0x0); + + gen_set_label(tcg_ctx, fin); ///// + + tcg_temp_free(tcg_ctx, r1_local); + tcg_temp_free(tcg_ctx, r2_local); + tcg_temp_free(tcg_ctx, r3_local); + } + break; + + case OPC_RH850_DIVU_reg1_reg2_reg3:{ + + // reg2/0x0000=undefined; cpu_OVF=1 + // if reg2==reg3; reg2=remainder + + TCGLabel *cont; + TCGLabel *fin; + + TCGv r1_local = tcg_temp_local_new(tcg_ctx); + TCGv r2_local = tcg_temp_local_new(tcg_ctx); + TCGv r3_local = tcg_temp_local_new(tcg_ctx); + TCGv check = tcg_temp_local_new(tcg_ctx); + + tcg_gen_mov_i32(tcg_ctx, r1_local, tcg_r1); + tcg_gen_mov_i32(tcg_ctx, r2_local, tcg_r2); + + int_rs3 = extract32(ctx->opcode, 27, 5); + gen_get_gpr(tcg_ctx, tcg_r3, int_rs3); + tcg_gen_mov_i32(tcg_ctx, r3_local, tcg_r3); + + cont = gen_new_label(tcg_ctx); + fin = gen_new_label(tcg_ctx); + + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_EQ, cpu_OVF, r1_local, 0x0); + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_NE, cpu_OVF, 0x1, cont); + tcg_gen_br(tcg_ctx, fin); + + gen_set_label(tcg_ctx, cont); ///// + + tcg_gen_remu_i32(tcg_ctx, r3_local, r2_local, r1_local); + tcg_gen_divu_i32(tcg_ctx, r2_local, r2_local, r1_local); + + if(rs2==int_rs3){ + gen_set_gpr(tcg_ctx, rs2, r3_local); + } else { + gen_set_gpr(tcg_ctx, rs2, r2_local); + gen_set_gpr(tcg_ctx, int_rs3, r3_local); + } + + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_EQ, cpu_ZF, r2_local, 0x0); + tcg_gen_andi_i32(tcg_ctx, check, r2_local, 0x80000000); + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_EQ, cpu_SF, check, 0x80000000); + + gen_set_label(tcg_ctx, fin); ///// + + tcg_temp_free(tcg_ctx, r1_local); + tcg_temp_free(tcg_ctx, r2_local); + tcg_temp_free(tcg_ctx, r3_local); + tcg_temp_free(tcg_ctx, check); + } + break; + } + + tcg_temp_free_i32(tcg_ctx, tcg_r1); + tcg_temp_free_i32(tcg_ctx, tcg_r2); + tcg_temp_free_i32(tcg_ctx, tcg_r3); +} + +static void gen_branch(CPURH850State *env, DisasContext *ctx, uint32_t cond, + int rs1, int rs2, target_long bimm) +{ + TCGContext *tcg_ctx = ctx->uc->tcg_ctx; + + TCGLabel *l = gen_new_label(tcg_ctx); + TCGv condOK = tcg_temp_new(tcg_ctx); + TCGv condResult = condition_satisfied(tcg_ctx, cond); + tcg_gen_movi_i32(tcg_ctx, condOK, 0x1); + + tcg_gen_brcond_tl(tcg_ctx, TCG_COND_EQ, condResult, condOK, l); + + tcg_temp_free(tcg_ctx, condResult); + tcg_temp_free(tcg_ctx, condOK); + + gen_goto_tb_imm(ctx, 1, ctx->base.pc_next); // no jump, continue with next instr. + gen_set_label(tcg_ctx, l); /* branch taken */ + gen_goto_tb_imm(ctx, 0, ctx->pc + bimm); // jump + ctx->base.is_jmp = DISAS_TB_EXIT_ALREADY_GENERATED; +} + +static void gen_jmp(DisasContext *ctx, int rs1, uint32_t disp32, int operation) +{ + TCGContext *tcg_ctx = ctx->uc->tcg_ctx; + + // disp32 is already generated when entering calling this function + int rs2, rs3; + TCGv link_addr = tcg_temp_new(tcg_ctx); + TCGv dest_addr = tcg_temp_new(tcg_ctx); + + switch(operation){ + case OPC_RH850_JR_imm22: + case OPC_RH850_JR_imm32: + tcg_gen_mov_i32(tcg_ctx, dest_addr, cpu_pc); + break; + case OPC_RH850_JARL_disp22_reg2: + tcg_gen_mov_i32(tcg_ctx, dest_addr, cpu_pc); + rs2 = extract32(ctx->opcode, 11, 5); + tcg_gen_addi_i32(tcg_ctx, link_addr, cpu_pc, 0x4); + gen_set_gpr(tcg_ctx, rs2, link_addr); + break; + case OPC_RH850_JARL_disp32_reg1: + tcg_gen_mov_i32(tcg_ctx, dest_addr, cpu_pc); + tcg_gen_addi_i32(tcg_ctx, link_addr, cpu_pc, 0x6); + gen_set_gpr(tcg_ctx, rs1, link_addr); + break; + case OPC_RH850_JARL_reg1_reg3: + gen_get_gpr(tcg_ctx, dest_addr, rs1); + rs3 = extract32(ctx->opcode, 27, 5); + tcg_gen_addi_i32(tcg_ctx, link_addr, cpu_pc, 0x4); + gen_set_gpr(tcg_ctx, rs3, link_addr); + break; + default: // JMP instruction + gen_get_gpr(tcg_ctx, dest_addr, rs1); + } + + if (disp32) { + tcg_gen_addi_tl(tcg_ctx, dest_addr, dest_addr, disp32); + } + + tcg_gen_andi_i32(tcg_ctx, dest_addr, dest_addr, 0xfffffffe); + + tcg_gen_mov_i32(tcg_ctx, cpu_pc, dest_addr); + tcg_temp_free(tcg_ctx, link_addr); + tcg_temp_free(tcg_ctx, dest_addr); + + gen_goto_tb(ctx, 0, cpu_pc); + ctx->base.is_jmp = DISAS_TB_EXIT_ALREADY_GENERATED; +} + +static void gen_loop(DisasContext *ctx, int rs1, int32_t disp16) +{ + TCGContext *tcg_ctx = ctx->uc->tcg_ctx; + + TCGLabel *l = gen_new_label(tcg_ctx); + TCGv zero_local = tcg_temp_local_new(tcg_ctx); + TCGv r1_local = tcg_temp_local_new(tcg_ctx); + TCGv minusone_local = tcg_temp_local_new(tcg_ctx); + + tcg_gen_movi_i32(tcg_ctx, zero_local, 0); + tcg_gen_movi_i32(tcg_ctx, minusone_local, 0xffffffff); + gen_get_gpr(tcg_ctx, r1_local, rs1); + gen_flags_on_add(tcg_ctx, r1_local, minusone_local); //set flags + tcg_gen_add_i32(tcg_ctx, r1_local, r1_local, minusone_local); + gen_set_gpr(tcg_ctx, rs1, r1_local); + + tcg_gen_brcond_tl(tcg_ctx, TCG_COND_NE, r1_local, zero_local, l); + + tcg_temp_free(tcg_ctx, r1_local); + tcg_temp_free(tcg_ctx, zero_local); + tcg_temp_free(tcg_ctx, minusone_local); + + gen_goto_tb_imm(ctx, 0, ctx->base.pc_next); // no jump, continue with next instr. + gen_set_label(tcg_ctx, l); // branch taken + gen_goto_tb_imm(ctx, 1, ctx->pc - disp16); + + ctx->base.is_jmp = DISAS_TB_EXIT_ALREADY_GENERATED; +} + +static void gen_bit_manipulation(DisasContext *ctx, int rs1, int rs2, int operation) +{ + TCGContext *tcg_ctx = ctx->uc->tcg_ctx; + + TCGv r1 = tcg_temp_new_i32(tcg_ctx); + TCGv r2 = tcg_temp_new_i32(tcg_ctx); + TCGv tcg_disp = tcg_temp_new_i32(tcg_ctx); + TCGv one = tcg_temp_new_i32(tcg_ctx); + + TCGv temp = tcg_temp_new_i32(tcg_ctx); + TCGv test = tcg_temp_new_i32(tcg_ctx); + TCGv adr = tcg_temp_new_i32(tcg_ctx); + uint32_t disp16 = extract32(ctx->opcode, 16, 16); + + int bit; + + switch(operation){ + case OPC_RH850_SET1_reg2_reg1: + + gen_get_gpr(tcg_ctx, adr, rs1); + gen_get_gpr(tcg_ctx, r2, rs2); + tcg_gen_movi_i32(tcg_ctx, one, 0x1); + + tcg_gen_qemu_ld_i32(tcg_ctx, temp, adr, MEM_IDX, MO_UB); + + tcg_gen_shl_i32(tcg_ctx, r2, one, r2); + + tcg_gen_and_i32(tcg_ctx, test, temp, r2); + tcg_gen_setcond_i32(tcg_ctx, TCG_COND_NE, cpu_ZF, test, r2); + + tcg_gen_or_i32(tcg_ctx, temp, temp, r2); + + tcg_gen_qemu_st_i32(tcg_ctx, temp, adr, MEM_IDX, MO_UB); + + break; + case OPC_RH850_SET1_bit3_disp16_reg1: + + gen_get_gpr(tcg_ctx, r1, rs1); + tcg_gen_movi_i32(tcg_ctx, tcg_disp, disp16); + tcg_gen_ext16s_i32(tcg_ctx, tcg_disp, tcg_disp); + tcg_gen_add_i32(tcg_ctx, adr, r1, tcg_disp); + + bit = extract32(ctx->opcode, 11, 3); + + tcg_gen_qemu_ld_i32(tcg_ctx, temp, adr, MEM_IDX, MO_UB); + + tcg_gen_andi_i32(tcg_ctx, test, temp, (0x1 << bit)); + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_NE, cpu_ZF, test, (0x1 << bit)); + + tcg_gen_ori_i32(tcg_ctx, temp, temp, (0x1 << bit)); + + tcg_gen_qemu_st_i32(tcg_ctx, temp, adr, MEM_IDX, MO_UB); + break; + + case OPC_RH850_NOT1_reg2_reg1: + + gen_get_gpr(tcg_ctx, adr, rs1); + gen_get_gpr(tcg_ctx, r2, rs2); + tcg_gen_movi_i32(tcg_ctx, one, 0x1); + + tcg_gen_qemu_ld_i32(tcg_ctx, temp, adr, MEM_IDX, MO_UB); + + tcg_gen_shl_i32(tcg_ctx, r2, one, r2); // r2 = mask + + tcg_gen_and_i32(tcg_ctx, test, temp, r2); + tcg_gen_setcond_i32(tcg_ctx, TCG_COND_NE, cpu_ZF, test, r2); + + //test = temp & mask + tcg_gen_and_i32(tcg_ctx, test, temp, r2); + //test = not (test) & mask + tcg_gen_not_i32(tcg_ctx, test, test); + tcg_gen_and_i32(tcg_ctx, test, test, r2); + //temp = temp & not(mask) + tcg_gen_not_i32(tcg_ctx, r2, r2); + tcg_gen_and_i32(tcg_ctx, temp, temp, r2); + //temp = temp or test + tcg_gen_or_i32(tcg_ctx, temp, temp, test); + + tcg_gen_qemu_st_i32(tcg_ctx, temp, adr, MEM_IDX, MO_UB); + break; + + case OPC_RH850_NOT1_bit3_disp16_reg1: + + gen_get_gpr(tcg_ctx, r1, rs1); + tcg_gen_movi_i32(tcg_ctx, tcg_disp, disp16); + tcg_gen_ext16s_i32(tcg_ctx, tcg_disp, tcg_disp); + tcg_gen_add_i32(tcg_ctx, adr, r1, tcg_disp); + + bit = extract32(ctx->opcode, 11, 3); + + tcg_gen_qemu_ld_i32(tcg_ctx, temp, adr, MEM_IDX, MO_UB); + + tcg_gen_andi_i32(tcg_ctx, test, temp, (0x1 << bit)); + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_NE, cpu_ZF, test, (0x1 << bit)); + + tcg_gen_movi_i32(tcg_ctx, r2, (0x1 << bit)); // r2 = mask + + //test = temp & mask + tcg_gen_and_i32(tcg_ctx, test, temp, r2); + //test = not (test) & mask + tcg_gen_not_i32(tcg_ctx, test, test); + tcg_gen_and_i32(tcg_ctx, test, test, r2); + //temp = temp & not(mask) + tcg_gen_not_i32(tcg_ctx, r2, r2); + tcg_gen_and_i32(tcg_ctx, temp, temp, r2); + //temp = temp or test + tcg_gen_or_i32(tcg_ctx, temp, temp, test); + + tcg_gen_qemu_st_i32(tcg_ctx, temp, adr, MEM_IDX, MO_UB); + break; + + case OPC_RH850_CLR1_reg2_reg1: + + gen_get_gpr(tcg_ctx, adr, rs1); + gen_get_gpr(tcg_ctx, r2, rs2); + tcg_gen_movi_i32(tcg_ctx, one, 0x1); + + tcg_gen_qemu_ld_i32(tcg_ctx, temp, adr, MEM_IDX, MO_UB); + tcg_gen_andi_i32(tcg_ctx, r2, r2, 0x7); + tcg_gen_shl_i32(tcg_ctx, r2, one, r2); + + tcg_gen_and_i32(tcg_ctx, test, temp, r2); + tcg_gen_setcond_i32(tcg_ctx, TCG_COND_NE, cpu_ZF, test, r2); + + tcg_gen_not_i32(tcg_ctx, r2, r2); + tcg_gen_and_i32(tcg_ctx, temp, temp, r2); + + tcg_gen_qemu_st_i32(tcg_ctx, temp, adr, MEM_IDX, MO_UB); + break; + + case OPC_RH850_CLR1_bit3_disp16_reg1: + + gen_get_gpr(tcg_ctx, r1, rs1); + tcg_gen_movi_i32(tcg_ctx, tcg_disp, disp16); + tcg_gen_ext16s_i32(tcg_ctx, tcg_disp, tcg_disp); + tcg_gen_add_i32(tcg_ctx, adr, r1, tcg_disp); + + bit = extract32(ctx->opcode, 11, 3); + + tcg_gen_qemu_ld_i32(tcg_ctx, temp, adr, MEM_IDX, MO_UB); + + tcg_gen_movi_i32(tcg_ctx, test, (0x1 << bit)); + tcg_gen_andi_i32(tcg_ctx, test, temp, (0x1 << bit)); + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_NE, cpu_ZF, test, (0x1 << bit)); + + tcg_gen_movi_i32(tcg_ctx, test, (0x1 << bit)); + tcg_gen_not_i32(tcg_ctx, test, test); + tcg_gen_and_i32(tcg_ctx, temp, temp, test); + + tcg_gen_qemu_st_i32(tcg_ctx, temp, adr, MEM_IDX, MO_UB); + break; + + case OPC_RH850_TST1_reg2_reg1: + + gen_get_gpr(tcg_ctx, adr, rs1); + gen_get_gpr(tcg_ctx, r2, rs2); + tcg_gen_movi_i32(tcg_ctx, one, 0x1); + + tcg_gen_qemu_ld_i32(tcg_ctx, temp, adr, MEM_IDX, MO_UB); + + tcg_gen_shl_i32(tcg_ctx, r2, one, r2); + + tcg_gen_and_i32(tcg_ctx, test, temp, r2); + tcg_gen_setcond_i32(tcg_ctx, TCG_COND_NE, cpu_ZF, test, r2); + break; + + case OPC_RH850_TST1_bit3_disp16_reg1: + + gen_get_gpr(tcg_ctx, r1, rs1); + tcg_gen_movi_i32(tcg_ctx, tcg_disp, disp16); + tcg_gen_ext16s_i32(tcg_ctx, tcg_disp, tcg_disp); + tcg_gen_add_i32(tcg_ctx, adr, r1, tcg_disp); + + bit = extract32(ctx->opcode, 11, 3); + + tcg_gen_qemu_ld_i32(tcg_ctx, temp, adr, MEM_IDX, MO_UB); + + tcg_gen_movi_i32(tcg_ctx, test, (0x1 << bit)); + tcg_gen_andi_i32(tcg_ctx, test, temp, (0x1 << bit)); + tcg_gen_setcondi_i32(tcg_ctx, TCG_COND_NE, cpu_ZF, test, (0x1 << bit)); + break; + } + + tcg_temp_free_i32(tcg_ctx, r1); + tcg_temp_free_i32(tcg_ctx, r2); + tcg_temp_free_i32(tcg_ctx, tcg_disp); + tcg_temp_free_i32(tcg_ctx, one); + tcg_temp_free_i32(tcg_ctx, temp); + tcg_temp_free_i32(tcg_ctx, test); + tcg_temp_free_i32(tcg_ctx, adr); + +} + + +static void gen_special(DisasContext *ctx, CPURH850State *env, int rs1, int rs2, int operation) +{ + TCGContext *tcg_ctx = ctx->uc->tcg_ctx; + + TCGLabel *storeReg3; + TCGLabel *cont; + TCGLabel *excFromEbase; + TCGLabel * add_scbp; + int regID; + int selID = 0; + int imm; + + switch(operation){ + case OPC_RH850_CALLT_imm6: { + TCGv temp = tcg_temp_new_i32(tcg_ctx); + TCGv adr = tcg_temp_new_i32(tcg_ctx); + + //setting CTPC to PC+2 + tcg_gen_addi_i32(tcg_ctx, cpu_sysRegs[BANK_ID_BASIC_0][CTPC_IDX], cpu_pc, 0x2); + //setting CPTSW bits 0:4 + flags_to_tcgv_z_cy_ov_s_sat(tcg_ctx, cpu_sysRegs[BANK_ID_BASIC_0][CTPSW_IDX]); + + imm = extract32(ctx->opcode, 0, 6); + tcg_gen_movi_i32(tcg_ctx, adr, imm); + tcg_gen_shli_i32(tcg_ctx, adr, adr, 0x1); + tcg_gen_ext8s_i32(tcg_ctx, adr, adr); + tcg_gen_add_i32(tcg_ctx, adr, cpu_sysRegs[BANK_ID_BASIC_0][CTBP_IDX], adr); + + tcg_gen_qemu_ld16u(tcg_ctx, temp, adr, 0); + + tcg_gen_add_i32(tcg_ctx, cpu_pc, temp, cpu_sysRegs[BANK_ID_BASIC_0][CTBP_IDX]); + ctx->base.is_jmp = DISAS_EXIT_TB; + + tcg_temp_free(tcg_ctx, temp); + tcg_temp_free(tcg_ctx, adr); + } break; + + case OPC_RH850_CAXI_reg1_reg2_reg3: { + TCGv temp = tcg_temp_new_i32(tcg_ctx); + TCGv adr = tcg_temp_new_i32(tcg_ctx); + TCGv r2 = tcg_temp_new(tcg_ctx); + TCGv r3 = tcg_temp_new(tcg_ctx); + + storeReg3 = gen_new_label(tcg_ctx); + gen_get_gpr(tcg_ctx, adr, rs1); + gen_get_gpr(tcg_ctx, r2, rs2); + int rs3 = extract32(ctx->opcode, 27, 5); + gen_get_gpr(tcg_ctx, r3, rs3); + tcg_gen_qemu_ld32u(tcg_ctx, temp, adr, 0); + storeReg3 = gen_new_label(tcg_ctx); + cont = gen_new_label(tcg_ctx); + + TCGv local_adr = tcg_temp_local_new_i32(tcg_ctx); + TCGv local_r2 = tcg_temp_local_new_i32(tcg_ctx); + TCGv local_r3 = tcg_temp_local_new_i32(tcg_ctx); + TCGv local_temp = tcg_temp_local_new_i32(tcg_ctx); + + tcg_gen_mov_i32(tcg_ctx, local_adr, adr); + tcg_gen_mov_i32(tcg_ctx, local_r2, r2); + tcg_gen_mov_i32(tcg_ctx, local_r3, r3); + tcg_gen_mov_i32(tcg_ctx, local_temp, temp); + + gen_flags_on_sub(tcg_ctx, local_r2, local_temp); + + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_EQ, cpu_ZF, 0x1, storeReg3); + tcg_gen_qemu_st_tl(tcg_ctx, local_temp, local_adr, MEM_IDX, MO_TESL); + tcg_gen_br(tcg_ctx, cont); + + gen_set_label(tcg_ctx, storeReg3); + tcg_gen_qemu_st_tl(tcg_ctx, local_r3, local_adr, MEM_IDX, MO_TESL); + + gen_set_label(tcg_ctx, cont); + gen_set_gpr(tcg_ctx, rs3, local_temp); + + tcg_temp_free(tcg_ctx, temp); + tcg_temp_free(tcg_ctx, adr); + tcg_temp_free(tcg_ctx, r2); + tcg_temp_free(tcg_ctx, r3); + break; + } + + case OPC_RH850_CTRET: { + TCGv temp = tcg_temp_new_i32(tcg_ctx); + + tcg_gen_mov_i32(tcg_ctx, cpu_pc, cpu_sysRegs[BANK_ID_BASIC_0][CTPC_IDX]); + tcgv_to_flags_z_cy_ov_s_sat(tcg_ctx, cpu_sysRegs[BANK_ID_BASIC_0][CTPSW_IDX]); + + ctx->base.is_jmp = DISAS_EXIT_TB; + + tcg_temp_free(tcg_ctx, temp); + } break; + + case OPC_RH850_DI: + tcg_gen_movi_i32(tcg_ctx, cpu_ID, 0x1); + break; + + case OPC_RH850_DISPOSE_imm5_list12: { + TCGv temp = tcg_temp_new_i32(tcg_ctx); + TCGv adr = tcg_temp_new_i32(tcg_ctx); + + int list [12] = {31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20}; + int numOfListItems = sizeof(list) / sizeof(list[0]); + int list12 = extract32(ctx->opcode, 0, 1) | ( (extract32(ctx->opcode, 21, 11)) << 1); + + // reorganising bits that indicate the registers to load + // doing this for easier looping in correct order + int dispList = ((list12 & 0x80) << 4) | + ((list12 & 0x40) << 4) | + ((list12 & 0x20) << 4) | + ((list12 & 0x10) << 4) | + ((list12 & 0x800) >> 4) | + ((list12 & 0x400) >> 4) | + ((list12 & 0x200) >> 4) | + ((list12 & 0x100) >> 4) | + ((list12 & 0x8) << 0) | + ((list12 & 0x4) << 0) | + ((list12 & 0x2) >> 1) | + ((list12 & 0x1) << 1) ; + + int test = 0x1; + gen_get_gpr(tcg_ctx, temp, 3); // stack pointer (sp) register is cpu_gpr[3] + tcg_gen_addi_i32(tcg_ctx, temp, temp, (extract32(ctx->opcode, 1, 5) << 2)); + + TCGv regToLoad = tcg_temp_new_i32(tcg_ctx); + + for(int i=0; iopcode, 0, 1) | ( (extract32(ctx->opcode, 21, 11)) << 1); + TCGv jmpAddr = tcg_temp_new_i32(tcg_ctx); + + // reorganising bits that indicate the registers to load + // doing this for easier looping in correct order + int dispList = ((list12 & 0x80) << 4) | + ((list12 & 0x40) << 4) | + ((list12 & 0x20) << 4) | + ((list12 & 0x10) << 4) | + ((list12 & 0x800) >> 4) | + ((list12 & 0x400) >> 4) | + ((list12 & 0x200) >> 4) | + ((list12 & 0x100) >> 4) | + ((list12 & 0x8) << 0) | + ((list12 & 0x4) << 0) | + ((list12 & 0x2) >> 1) | + ((list12 & 0x1) << 1) ; + + int test = 0x1; + gen_get_gpr(tcg_ctx, temp, 3); // stack pointer (sp) register is cpu_gpr[3] + tcg_gen_addi_i32(tcg_ctx, temp, temp, (extract32(ctx->opcode, 1, 5) << 2)); + + TCGv regToLoad = tcg_temp_new_i32(tcg_ctx); + + for(int i=0; iopcode, 16, 5))); + tcg_gen_mov_i32(tcg_ctx, cpu_pc, jmpAddr); + + gen_set_gpr(tcg_ctx, 3, temp); + ctx->base.is_jmp = DISAS_EXIT_TB; + + tcg_temp_free(tcg_ctx, temp); + tcg_temp_free(tcg_ctx, adr); + } + break; + + case OPC_RH850_EI: + tcg_gen_movi_i32(tcg_ctx, cpu_ID, 0x0); + break; + case OPC_RH850_EIRET: + tcg_gen_mov_i32(tcg_ctx, cpu_pc, cpu_sysRegs[BANK_ID_BASIC_0][EIPC_IDX]); + tcgv_to_flags(tcg_ctx, cpu_sysRegs[BANK_ID_BASIC_0][EIPSW_IDX]); + ctx->base.is_jmp = DISAS_EXIT_TB; + break; + case OPC_RH850_FERET: + tcg_gen_mov_i32(tcg_ctx, cpu_pc, cpu_sysRegs[BANK_ID_BASIC_0][FEPC_IDX]); + tcgv_to_flags(tcg_ctx, cpu_sysRegs[BANK_ID_BASIC_0][FEPSW_IDX]); + ctx->base.is_jmp = DISAS_EXIT_TB; + break; + + case OPC_RH850_FETRAP_vector4: { + + cont = gen_new_label(tcg_ctx); + excFromEbase = gen_new_label(tcg_ctx); + int vector = extract32(ctx->opcode, 11, 4); + tcg_gen_addi_i32(tcg_ctx, cpu_sysRegs[BANK_ID_BASIC_0][FEPC_IDX], cpu_pc, 0x2); + flags_to_tcgv(tcg_ctx, cpu_sysRegs[BANK_ID_BASIC_0][FEPSW_IDX]); + + //writing the exception cause code + vector += 0x30; + tcg_gen_movi_i32(tcg_ctx, cpu_sysRegs[BANK_ID_BASIC_0][FEIC_IDX], vector); + tcg_gen_movi_i32(tcg_ctx, cpu_UM, 0x0); + tcg_gen_movi_i32(tcg_ctx, cpu_NP, 0x1); + tcg_gen_movi_i32(tcg_ctx, cpu_EP, 0x1); + tcg_gen_movi_i32(tcg_ctx, cpu_ID, 0x1); + + //writing the except. handler address based on PSW.EBV + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_EQ, cpu_EBV, 0x1, excFromEbase); + tcg_gen_addi_i32(tcg_ctx, cpu_pc, cpu_sysRegs[BANK_ID_BASIC_1][RBASE_IDX1], 0x30); //RBASE + 0x30 + tcg_gen_br(tcg_ctx, cont); + + gen_set_label(tcg_ctx, excFromEbase); + tcg_gen_addi_i32(tcg_ctx, cpu_pc, cpu_sysRegs[BANK_ID_BASIC_1][EBASE_IDX1], 0x30); //EBASE + 0x30 + + gen_set_label(tcg_ctx, cont); + //branch to exception handler + ctx->base.is_jmp = DISAS_EXIT_TB; + } break; + + case OPC_RH850_HALT: + // nop, interupts are not implemented, so HALT would never continue + // tcg_abort(); + break; + + case OPC_RH850_LDSR_reg2_regID_selID: + selID = extract32(ctx->opcode, 27, 5); + regID = rs2; + + // Modify only sytem regs, which exist. Real device executes instruction, but + // value is not stored for system regs, which do not exist. No exception is + // thrown. + if(cpu_sysRegs[selID][regID] != NULL || (selID == BANK_ID_BASIC_0 && regID == PSW_IDX)) { + + TCGv tmp = tcg_temp_new(tcg_ctx); + gen_get_gpr(tcg_ctx, tmp, rs1); + + if(selID == BANK_ID_BASIC_0 && regID == PSW_IDX){ + tcgv_to_flags(tcg_ctx, tmp); + } else { + // clear read-only bits in value, all other bits in sys reg. This way + // read-only bits preserve their value given at reset + tcg_gen_andi_i32(tcg_ctx, tmp, tmp, rh850_sys_reg_read_only_masks[selID][regID]); + tcg_gen_andi_i32(tcg_ctx, cpu_sysRegs[selID][regID], cpu_sysRegs[selID][regID], ~rh850_sys_reg_read_only_masks[selID][regID]); + tcg_gen_or_i32(tcg_ctx, cpu_sysRegs[selID][regID], cpu_sysRegs[selID][regID], tmp); + } + tcg_temp_free(tcg_ctx, tmp); + } + break; + + //case OPC_RH850_LDLW: + //break; + + //case OPC_RH850_NOP: + //break; + + case OPC_RH850_POPSP_rh_rt: { + TCGv temp = tcg_temp_new_i32(tcg_ctx); + TCGv adr = tcg_temp_new_i32(tcg_ctx); + + uint32_t rs3 = extract32(ctx->opcode, 27, 5); + + int numOfRegs = (rs3-rs1)+1; + + gen_get_gpr(tcg_ctx, temp, 3); // stack pointer register is cpu_gpr[3] + TCGv regToLoad = tcg_temp_new_i32(tcg_ctx); + + if(rs1<=rs3){ + + for(int i=0; iopcode, 21, 11) << 1) | (extract32(ctx->opcode, 0, 1) ) ) ; + int numOfListItems = sizeof(list) / sizeof(list[0]); + int prepList = ((list12 & 0x80) >> 7) | + ((list12 & 0x40) >> 5) | + ((list12 & 0x20) >> 3) | + ((list12 & 0x10) >> 1) | + ((list12 & 0x800) >> 7) | + ((list12 & 0x400) >> 5) | + ((list12 & 0x200) >> 3) | + ((list12 & 0x100) >> 1) | + ((list12 & 0x8) << 5) | + ((list12 & 0x4) << 7) | + ((list12 & 0x2) << 10) | + ((list12 & 0x1) << 10) ; + + int test = 0x1; + gen_get_gpr(tcg_ctx, temp, 3); // stack pointer register is cpu_gpr[3] + TCGv regToStore = tcg_temp_new_i32(tcg_ctx); + + for(int i=0; iopcode, 1, 5) << 2)); + gen_set_gpr(tcg_ctx, 3, temp); + + tcg_temp_free(tcg_ctx, temp); + tcg_temp_free(tcg_ctx, adr); + } break; + + case OPC_RH850_PREPARE_list12_imm5_sp:{ + TCGv temp = tcg_temp_new_i32(tcg_ctx); + TCGv adr = tcg_temp_new_i32(tcg_ctx); + + int list [12] = {20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + uint32_t list12 = extract32(ctx->opcode, 0, 1) | ( (extract32(ctx->opcode, 21, 11)) << 1); + int numOfListItems = sizeof(list) / sizeof(list[0]); + int prepList = ((list12 & 0x80) >> 7) | + ((list12 & 0x40) >> 5) | + ((list12 & 0x20) >> 3) | + ((list12 & 0x10) >> 1) | + ((list12 & 0x800) >> 7) | + ((list12 & 0x400) >> 5) | + ((list12 & 0x200) >> 3) | + ((list12 & 0x100) >> 1) | + ((list12 & 0x8) << 5) | + ((list12 & 0x4) << 7) | + ((list12 & 0x2) << 10) | + ((list12 & 0x1) << 10) ; + + uint32_t imm = 0x0; + + int test = 0x1; + int ff = extract32(ctx->opcode, 19, 2); + gen_get_gpr(tcg_ctx, temp, 3); // stack pointer register is cpu_gpr[3] + TCGv regToStore = tcg_temp_new_i32(tcg_ctx); + + for(int i=0; iopcode, 1, 5) << 2)); + + gen_set_gpr(tcg_ctx, 3, temp); + + switch(ff){ + + case 0x0: + gen_set_gpr(tcg_ctx, 30, temp); //moving sp to ep (element pointer is at cpu_gpr[30]) + break; + + case 0x1: + imm = cpu_lduw_code(env, ctx->base.pc_next); // fetching additional 16bits from memory + tcg_gen_movi_i32(tcg_ctx, temp, imm); + tcg_gen_ext16s_i32(tcg_ctx, temp, temp); + gen_set_gpr(tcg_ctx, 30, temp); + ctx->base.pc_next+=2; // increasing PC due to additional fetch + break; + + case 0x2: + imm = cpu_lduw_code(env, ctx->base.pc_next); // fetching additional 16bits from memory + tcg_gen_movi_i32(tcg_ctx, temp, imm); + tcg_gen_shli_i32(tcg_ctx, temp, temp, 0x10); + gen_set_gpr(tcg_ctx, 30, temp); + ctx->base.pc_next+=2; + break; + + case 0x3: + imm = cpu_lduw_code(env, ctx->base.pc_next) | + (cpu_lduw_code(env, ctx->base.pc_next + 2) << 0x10); + // fetching additional 32bits from memory + + tcg_gen_movi_i32(tcg_ctx, temp, imm); + gen_set_gpr(tcg_ctx, 30, temp); + ctx->base.pc_next = ctx->base.pc_next + 4; + break; + } + + tcg_temp_free(tcg_ctx, temp); + tcg_temp_free(tcg_ctx, adr); + } break; + + case OPC_RH850_PUSHSP_rh_rt: { + TCGv temp = tcg_temp_new_i32(tcg_ctx); + TCGv adr = tcg_temp_new_i32(tcg_ctx); + + uint32_t rs3 = extract32(ctx->opcode, 27, 5); + + int numOfRegs = (rs3-rs1)+1; + + gen_get_gpr(tcg_ctx, temp, 3); // stack pointer register is cpu_gpr[3] + TCGv regToStore = tcg_temp_new_i32(tcg_ctx); + if(rs1<=rs3){ + + for(int i=0; ibase.is_jmp = DISAS_EXIT_TB; + + } break; + + case OPC_RH850_SNOOZE: + break; + + //case OPC_RH850_STCW: + // break; + + case OPC_RH850_STSR_regID_reg2_selID: + regID=rs1; + selID = extract32(ctx->opcode, 27, 5); + if(selID == BANK_ID_BASIC_0 && regID == PSW_IDX){ + TCGv tmp = tcg_temp_new_i32(tcg_ctx); + tcg_gen_movi_tl(tcg_ctx, tmp, 0); + flags_to_tcgv(tcg_ctx, tmp); + gen_set_gpr(tcg_ctx, rs2, tmp); + tcg_temp_free(tcg_ctx, tmp); + } else { + if (cpu_sysRegs[selID][regID] != NULL) { + gen_set_gpr(tcg_ctx, rs2, cpu_sysRegs[selID][regID]); + } else { + TCGv dat = tcg_temp_local_new(tcg_ctx); + tcg_gen_movi_i32(tcg_ctx, dat, 0); + gen_set_gpr(tcg_ctx, rs2, 0); // if sys reg does not exist, write 0 + tcg_temp_free(tcg_ctx, dat); + } + } + break; + + case OPC_RH850_SWITCH_reg1: { + TCGv temp = tcg_temp_new_i32(tcg_ctx); + TCGv adr = tcg_temp_new_i32(tcg_ctx); + + gen_get_gpr(tcg_ctx, adr, rs1); + tcg_gen_shli_i32(tcg_ctx, adr, adr, 0x1); + tcg_gen_add_i32(tcg_ctx, adr, adr, cpu_pc); + tcg_gen_addi_i32(tcg_ctx, adr, adr, 0x2); + + tcg_gen_addi_i32(tcg_ctx, cpu_pc, cpu_pc, 0x2); + tcg_gen_qemu_ld16s(tcg_ctx, temp, adr, MEM_IDX); + tcg_gen_ext16s_i32(tcg_ctx, temp, temp); + tcg_gen_shli_i32(tcg_ctx, temp, temp, 0x1); + tcg_gen_add_i32(tcg_ctx, cpu_pc, cpu_pc, temp); + ctx->base.is_jmp = DISAS_EXIT_TB; + } break; + + // SYNC instructions will not be implemented + case OPC_RH850_SYNCE: + case OPC_RH850_SYNCI: + case OPC_RH850_SYNCM: + case OPC_RH850_SYNCP: + break; + + case OPC_RH850_TRAP: { + + cont = gen_new_label(tcg_ctx); + excFromEbase = gen_new_label(tcg_ctx); + + uint32_t offset; + int vector5 = rs1; + tcg_gen_addi_i32(tcg_ctx, cpu_sysRegs[BANK_ID_BASIC_0][EIPC_IDX], cpu_pc, 0x4); + flags_to_tcgv(tcg_ctx, cpu_sysRegs[BANK_ID_BASIC_0][EIPSW_IDX]); + tcg_gen_movi_i32(tcg_ctx, cpu_sysRegs[BANK_ID_BASIC_0][EIIC_IDX], (0x40 + vector5)); + tcg_gen_movi_i32(tcg_ctx, cpu_UM, 0x0); + tcg_gen_movi_i32(tcg_ctx, cpu_EP, 0x1); + tcg_gen_movi_i32(tcg_ctx, cpu_ID, 0x1); // This bit is under control of winIDEA in single-stepping. + // Additionally EIPSW.ID is set in interrupts in single-stepping, because winIDEA + // sets this bit before executing TRAP instruction. + + if( vector5 > 0xf ){ + offset = 0x50; + } else { + offset = 0x40; + } + + tcg_gen_brcondi_i32(tcg_ctx, TCG_COND_EQ, cpu_EBV, 0x1, excFromEbase); + tcg_gen_addi_i32(tcg_ctx, cpu_pc, cpu_sysRegs[BANK_ID_BASIC_1][RBASE_IDX1], offset); //RBASE + offset + tcg_gen_br(tcg_ctx, cont); + + gen_set_label(tcg_ctx, excFromEbase); + tcg_gen_addi_i32(tcg_ctx, cpu_pc, cpu_sysRegs[BANK_ID_BASIC_1][EBASE_IDX1], offset); //EBASE + offset + + gen_set_label(tcg_ctx, cont); + ctx->base.is_jmp = DISAS_EXIT_TB; + } break; + + case OPC_RH850_SYSCALL: + { + TCGv t0 = tcg_temp_local_new(tcg_ctx); + TCGv t1 = tcg_temp_local_new(tcg_ctx); + + cont = gen_new_label(tcg_ctx); + add_scbp = gen_new_label(tcg_ctx); + + int vector = extract32(ctx->opcode, 0, 5) | ( (extract32(ctx->opcode,27, 3)) << 5); + + tcg_gen_addi_i32(tcg_ctx, cpu_sysRegs[BANK_ID_BASIC_0][EIPC_IDX], cpu_pc, 0x4); + flags_to_tcgv(tcg_ctx, cpu_sysRegs[BANK_ID_BASIC_0][EIPSW_IDX]); + int exception_code = vector + 0x8000; + + tcg_gen_movi_i32(tcg_ctx, cpu_sysRegs[BANK_ID_BASIC_0][EIIC_IDX], exception_code); + tcg_gen_movi_i32(tcg_ctx, cpu_UM, 0x0); + tcg_gen_movi_i32(tcg_ctx, cpu_EP, 0x1); + tcg_gen_movi_i32(tcg_ctx, cpu_ID, 0x1); + + TCGv local_vector = tcg_temp_local_new_i32(tcg_ctx); + tcg_gen_movi_i32(tcg_ctx, local_vector, vector); + + TCGv local_SCCFG_SIZE = tcg_temp_local_new_i32(tcg_ctx); + tcg_gen_mov_i32(tcg_ctx, local_SCCFG_SIZE, cpu_sysRegs[BANK_ID_BASIC_1][SCCFG_IDX1]); + + // if vector <= SCCFG + // gen_set_gpr(17, local_vector); // debug! + // gen_set_gpr(18, local_SCCFG_SIZE); // debug! + tcg_gen_brcond_i32(tcg_ctx, TCG_COND_LEU, local_vector, local_SCCFG_SIZE, add_scbp); + // { + tcg_gen_mov_i32(tcg_ctx, t0, cpu_sysRegs[BANK_ID_BASIC_1][SCBP_IDX1]); + tcg_gen_br(tcg_ctx, cont); + // } else { + gen_set_label(tcg_ctx, add_scbp); + tcg_gen_shli_tl(tcg_ctx, local_vector, local_vector, 0x2); + tcg_gen_add_i32(tcg_ctx, t0, local_vector, cpu_sysRegs[BANK_ID_BASIC_1][SCBP_IDX1]); // t0 = adr + // } + gen_set_label(tcg_ctx, cont); + + //currently loading unsigned word + tcg_gen_qemu_ld_tl(tcg_ctx, t1, t0, MEM_IDX, MO_TEUL); + tcg_gen_add_i32(tcg_ctx, t1,t1,cpu_sysRegs[BANK_ID_BASIC_1][SCBP_IDX1]); + + tcg_gen_mov_i32(tcg_ctx, cpu_pc, t1); + + tcg_temp_free(tcg_ctx, local_vector); + tcg_temp_free(tcg_ctx, local_SCCFG_SIZE); + + ctx->base.is_jmp = DISAS_EXIT_TB; + tcg_temp_free(tcg_ctx, t0); + tcg_temp_free(tcg_ctx, t1); + break; + } + } +} + + +static void gen_cache(DisasContext *ctx, int rs1, int rs2, int operation){ + int cache_op = (extract32(ctx->opcode,11, 2) << 5 ) | (extract32(ctx->opcode, 27, 5)); + switch(cache_op){ + case CHBII: + // printf("CHBII\n"); + break; + case CIBII: + // printf("CIBII\n"); + break; + case CFALI: + // printf("CFALI\n"); + break; + case CISTI: + // printf("CISTI\n"); + break; + case CILDI: + // printf("CILDI\n"); + break; + case CLL: + // printf("CLL\n"); + // this operation is not implemented on single core + break; + } +} + +static void decode_RH850_48(CPURH850State *env, DisasContext *ctx) +{ + int rs1, rs3; + uint64_t opcode48; + + rs1 = GET_RS1(ctx->opcode); + rs3 = extract32(ctx->opcode, 27, 5); + + opcode48 = (ctx->opcode1); + opcode48 = (ctx->opcode) | (opcode48 << 0x20); + uint32_t opcode20 = extract32(opcode48,0,20) & 0xfffe0; + + uint32_t disp23 = (ctx->opcode1 << 7) | (extract32(ctx->opcode, 21, 6) << 1); + uint32_t disp32 = (opcode48 >> 16); + + switch(opcode20) { + + + case OPC_RH850_LDB2: + gen_load(ctx, MO_SB, rs3, rs1, disp23, 1); + break; + case OPC_RH850_LDH2: + gen_load(ctx, MO_TESW, rs3, rs1, disp23, 1); + break; + case OPC_RH850_LDW2: + gen_load(ctx, MO_TESL, rs3, rs1, disp23, 1); + break; + case OPC_RH850_LDDW: + gen_load(ctx, MO_TEQ, rs3, rs1, disp23, 1); + break; + case OPC_RH850_LDBU2: + gen_load(ctx, MO_UB, rs3, rs1, disp23, 1); + break; + case OPC_RH850_LDHU2: + gen_load(ctx, MO_TEUW, rs3, rs1, disp23, 1); + break; + + case OPC_RH850_STB2: + gen_store(ctx, MO_SB, rs1, rs3, disp23, 1); + break; + case OPC_RH850_STH2: + gen_store(ctx, MO_TESW, rs1, rs3, disp23, 1); + break; + case OPC_RH850_STW2: + gen_store(ctx, MO_TESL, rs1, rs3, disp23, 1); + break; + case OPC_RH850_STDW: + gen_store(ctx, MO_TEQ, rs1, rs3, disp23, 1); + break; + } + + if (extract32(ctx->opcode, 5, 11) == 0x31) { + gen_arithmetic(ctx, 0, rs1, OPC_RH850_MOV_imm32_reg1); + } else if (extract32(ctx->opcode, 5, 12) == 0x37) { + gen_jmp(ctx, rs1, disp32, OPC_RH850_JMP_disp32_reg1); + } else if (extract32(ctx->opcode, 5, 11) == 0x17) { + if (rs1 == 0x0){ + gen_jmp(ctx, 0, disp32, OPC_RH850_JR_imm32); + + } else { + gen_jmp(ctx, rs1, disp32, OPC_RH850_JARL_disp32_reg1); + } + } +} + +static void decode_RH850_32(CPURH850State *env, DisasContext *ctx) +{ + TCGContext *tcg_ctx = ctx->uc->tcg_ctx; + + int rs1; + int rs2; + int cond; + uint32_t op; + uint32_t formXop; + uint32_t checkXII; + uint32_t check32bitZERO; + target_long imm_32; + target_long ld_imm; + + op = MASK_OP_MAJOR(ctx->opcode); + rs1 = GET_RS1(ctx->opcode); // rs1 is at b0-b4; + rs2 = GET_RS2(ctx->opcode); // rs2 is at b11-b15; + TCGv r1 = tcg_temp_local_new(tcg_ctx); + TCGv r2 = tcg_temp_local_new(tcg_ctx); + imm_32 = GET_IMM_32(ctx->opcode); + ld_imm = extract32(ctx->opcode, 16, 16); + + gen_get_gpr(tcg_ctx, r1, rs1); + gen_get_gpr(tcg_ctx, r2, rs2); + + switch(op){ + + case OPC_RH850_LDB: + gen_load(ctx, MO_SB, rs2, rs1, ld_imm, 0); + break; + + case OPC_RH850_LDH_LDW: + if ( extract32(ctx->opcode, 16, 1) == 0 ){ + gen_load(ctx, MO_TESW, rs2, rs1, ld_imm, 0); // LD.H + } + else{ + gen_load(ctx, MO_TESL, rs2, rs1, ld_imm & 0xfffe, 0); // LD.W + } + break; + + case OPC_RH850_STB: + gen_store(ctx, MO_SB, rs1, rs2, (extract32(ctx->opcode, 16, 16)), 0); + break; + + case OPC_RH850_STH_STW: + if ( extract32(ctx->opcode, 16, 1)==1 ) { + gen_store(ctx, MO_TESL, rs1, rs2, ((extract32(ctx->opcode, 17, 15))) << 1, 0); + //this is STORE WORD + break; + } + gen_store(ctx, MO_TESW, rs1, rs2, ((extract32(ctx->opcode, 17, 15))) << 1, 0); + //this is STORE HALFWORD + break; + + case OPC_RH850_ADDI_imm16_reg1_reg2: + gen_arithmetic(ctx, rs1,rs2, OPC_RH850_ADDI_imm16_reg1_reg2); + break; + + case OPC_RH850_ANDI_imm16_reg1_reg2: + gen_logical(ctx, rs1, rs2, OPC_RH850_ANDI_imm16_reg1_reg2); + break; + + case OPC_RH850_MOVEA: + if ( extract32(ctx->opcode, 11, 5) == 0 ){ + // This is 48bit MOV + // This instruction should be reached first in decode_RH850_48 + } else { + gen_arithmetic(ctx, rs1, rs2, OPC_RH850_MOVEA_imm16_reg1_reg2); + } + break; + + case OPC_RH850_MOVHI_imm16_reg1_reg2: + if(extract32(ctx->opcode, 11, 5)!=0x0){ + gen_arithmetic(ctx, rs1, rs2, OPC_RH850_MOVHI_imm16_reg1_reg2); + } else { + if(extract32(ctx->opcode, 16, 5)==0x0){ + gen_special(ctx, env, rs1, rs2, OPC_RH850_DISPOSE_imm5_list12); + } else { + gen_special(ctx, env, rs1, rs2, OPC_RH850_DISPOSE_imm5_list12_reg1); + } + } + break; + + case OPC_RH850_ORI_imm16_reg1_reg2: + gen_logical(ctx, rs1, rs2, OPC_RH850_ORI_imm16_reg1_reg2); + break; + + case OPC_RH850_SATSUBI_imm16_reg1_reg2: + if(extract32(ctx->opcode, 11, 5)!=0x0){ + gen_sat_op(ctx, rs1, rs2, OPC_RH850_SATSUBI_imm16_reg1_reg2); + } else { + if(extract32(ctx->opcode, 16, 5)==0x0){ + gen_special(ctx, env, rs1, rs2, OPC_RH850_DISPOSE_imm5_list12); + } else { + gen_special(ctx, env, rs1, rs2, OPC_RH850_DISPOSE_imm5_list12_reg1); + } + } + + break; + case OPC_RH850_XORI_imm16_reg1_reg2: + gen_logical(ctx, rs1, rs2, OPC_RH850_XORI_imm16_reg1_reg2); + break; + + case OPC_RH850_LOOP: + if (extract32(ctx->opcode, 11, 5) == 0x0) + gen_loop(ctx, rs1, ld_imm & 0xfffe); // LOOP + else + gen_multiply(ctx, rs1, rs2, OPC_RH850_MULHI_imm16_reg1_reg2); + break; + case OPC_RH850_BIT_MANIPULATION_2: + + switch(extract32(ctx->opcode, 14, 2)){ + case 0: + gen_bit_manipulation(ctx, rs1, rs2, OPC_RH850_SET1_bit3_disp16_reg1); + break; + case 1: + gen_bit_manipulation(ctx, rs1, rs2, OPC_RH850_NOT1_bit3_disp16_reg1); + break; + case 2: + gen_bit_manipulation(ctx, rs1, rs2, OPC_RH850_CLR1_bit3_disp16_reg1); + break; + case 3: + gen_bit_manipulation(ctx, rs1, rs2, OPC_RH850_TST1_bit3_disp16_reg1); + break; + } + break; + case OPC_RH850_32bit_1: /* case for opcode = 111111 ; formats IX, X, XI, XII */ + if (extract32(ctx->opcode, 16, 1) == 0x1 ) { + if (rs2 == 0x0) { + //this is BCOND2 + cond = extract32(ctx->opcode, 0, 4); + imm_32 = (extract32(ctx->opcode, 4, 1) || + (extract32(ctx->opcode, 17, 15) << 1)) << 1; + if((imm_32 & 0x10000) == 0x10000){ // checking 17th bit if signed + imm_32 |= (0x7fff << 17); + } + gen_branch(env, ctx, cond, rs1, rs2, imm_32); + + break; + } else { + //this is LD.HU + gen_load(ctx, MO_TEUW, rs2, rs1, ld_imm & 0xfffe, 0); + break; + } + } + formXop = MASK_OP_32BIT_SUB(ctx->opcode); //sub groups based on bits b23-b26 + switch(formXop){ + case OPC_RH850_LDSR_RIE_SETF_STSR: + check32bitZERO = extract32(ctx->opcode, 21, 2); + switch(check32bitZERO){ + case 0: + if(extract32(ctx->opcode, 4, 1)==1){ + gen_special(ctx, env, rs1, rs2, OPC_RH850_RIE); + } else { + gen_data_manipulation(ctx, rs1, rs2, OPC_RH850_SETF_cccc_reg2); + } + break; + case OPC_RH850_LDSR_reg2_regID_selID: + gen_special(ctx, env, rs1, rs2, OPC_RH850_LDSR_reg2_regID_selID); + break; + case OPC_RH850_STSR_regID_reg2_selID: + gen_special(ctx, env, rs1, rs2, OPC_RH850_STSR_regID_reg2_selID); + break; + } + break; + case OPC_RH850_FORMAT_IX: //format IX instructions + formXop = MASK_OP_FORMAT_IX(ctx->opcode); //mask on bits 21, 22 + switch(formXop){ + case OPC_RH850_BINS_0: + if (extract32(ctx->opcode, 20, 1) == 1){ + //BINS0 + gen_data_manipulation(ctx, rs1, rs2, OPC_RH850_BINS); + } + else{ + if (extract32(ctx->opcode, 17, 1) == 0){ + gen_data_manipulation(ctx, rs1, rs2, OPC_RH850_SHR_reg1_reg2); + }else{ + gen_data_manipulation(ctx, rs1, rs2, OPC_RH850_SHR_reg1_reg2_reg3); + } + } + break; + case OPC_RH850_BINS_1: + if (extract32(ctx->opcode, 20, 1) == 1){ + //BINS1 + gen_data_manipulation(ctx, rs1, rs2, OPC_RH850_BINS); + } + else{ + if (extract32(ctx->opcode, 17, 1) == 0){ + gen_data_manipulation(ctx, rs1, rs2, OPC_RH850_SAR_reg1_reg2); + }else{ + gen_data_manipulation(ctx, rs1, rs2, OPC_RH850_SAR_reg1_reg2_reg3); + } + } + break; + case OPC_RH850_BINS_2: + if (extract32(ctx->opcode, 20, 1) == 1){ + //BINS2 + gen_data_manipulation(ctx, rs1, rs2, OPC_RH850_BINS); + } + else{ + if (extract32(ctx->opcode, 17, 1) == 0){ + if (extract32(ctx->opcode, 18, 1) == 1){ + gen_data_manipulation(ctx, rs1, rs2, + OPC_RH850_ROTL_imm5_reg2_reg3); + } + else{ + gen_data_manipulation(ctx, rs1, rs2, + OPC_RH850_SHL_reg1_reg2); + } + }else{ + if (extract32(ctx->opcode, 18, 1) == 1){ + gen_data_manipulation(ctx, rs1, rs2, + OPC_RH850_ROTL_reg1_reg2_reg3); + } + else{ + gen_data_manipulation(ctx, rs1, rs2, + OPC_RH850_SHL_reg1_reg2_reg3); + } + } + } + break; + case OPC_RH850_BIT_MANIPULATION: // in format IX + check32bitZERO = extract32(ctx->opcode, 16, 3); + switch(check32bitZERO){ + case OPC_RH850_SET1_reg2_reg1: + gen_bit_manipulation(ctx, rs1, rs2, OPC_RH850_SET1_reg2_reg1); + break; + case OPC_RH850_NOT1_reg2_reg1: + gen_bit_manipulation(ctx, rs1, rs2, OPC_RH850_NOT1_reg2_reg1); + break; + case OPC_RH850_CLR1_reg2_reg1: + gen_bit_manipulation(ctx, rs1, rs2, OPC_RH850_CLR1_reg2_reg1); + break; + case OPC_RH850_TST1_reg2_reg1: + if (extract32(ctx->opcode, 19, 1) == 0){ + gen_bit_manipulation(ctx, rs1, rs2, OPC_RH850_TST1_reg2_reg1); + } else { + gen_special(ctx, env, rs1, rs2, OPC_RH850_CAXI_reg1_reg2_reg3); + } + } + break; + } + break; + + + case OPC_RH850_FORMAT_X: //format X instructions + //(+JARL3 - added due to MASK_OP_FORMAT_X matching) + formXop = MASK_OP_FORMAT_X(ctx->opcode); + + switch(formXop){ + + case OPC_RH850_CTRET: + gen_special(ctx, env, rs1, rs2, OPC_RH850_CTRET); + break; + case OPC_RH850_DI: + gen_special(ctx, env, rs1, rs2, OPC_RH850_DI); + break; + case OPC_RH850_EI: + gen_special(ctx, env, rs1, rs2, OPC_RH850_EI); + break; + case OPC_RH850_EIRET: + gen_special(ctx, env, rs1, rs2, OPC_RH850_EIRET); + break; + case OPC_RH850_FERET: + gen_special(ctx, env, rs1, rs2, OPC_RH850_FERET); + break; + case OPC_RH850_HALT: + gen_special(ctx, env, rs1, rs2, OPC_RH850_HALT); + break; + case OPC_RH850_JARL3: + gen_jmp(ctx, rs1, 0, OPC_RH850_JARL_reg1_reg3); + + break; + case OPC_RH850_SNOOZE: + gen_special(ctx, env, rs1, rs2, OPC_RH850_SNOOZE); + break; + case OPC_RH850_SYSCALL: + gen_special(ctx, env, rs1, rs2, OPC_RH850_SYSCALL); + break; + case OPC_RH850_TRAP: + gen_special(ctx, env, rs1, rs2, OPC_RH850_TRAP); + break; + case OPC_RH850_PREF: + //printf("PREF \n"); + break; + case OPC_RH850_POPSP_rh_rt: + gen_special(ctx, env, rs1, rs2, OPC_RH850_POPSP_rh_rt); + break; + case OPC_RH850_PUSHSP_rh_rt: + gen_special(ctx, env, rs1, rs2, OPC_RH850_PUSHSP_rh_rt); + break; + default: + if ((extract32(ctx->opcode, 13, 12) == 0xB07)) + { + if ((extract32(ctx->opcode, 27, 5) == 0x1E) && + (extract32(ctx->opcode, 0, 5) == 0x1F)) + { + if ((extract32(ctx->opcode, 23, 4) == 0x2)) // CLL + gen_mutual_exclusion(ctx, extract32(ctx->opcode, 27, 5), rs1, operation_CLL); + } else { + //CACHE; if cacheop bits are 1111110, opcode matches CLL ins, + //then they are THE SAME instruction, so this should be correct + gen_cache(ctx,rs1,rs2, 1); + } + } else + printf("ERROR! \n"); + break; + } + break; + case OPC_RH850_MUL_INSTS: + if (extract32(ctx->opcode, 22, 1) == 0){ + if (extract32(ctx->opcode, 21, 1) == 0){ + gen_data_manipulation(ctx, rs1, rs2, OPC_RH850_SASF_cccc_reg2); + } else { + if (extract32(ctx->opcode, 17, 1) == 1){ + gen_multiply(ctx, rs1, rs2, OPC_RH850_MULU_reg1_reg2_reg3); + } else { + gen_multiply(ctx, rs1, rs2, OPC_RH850_MUL_reg1_reg2_reg3); + } + } + break; + } else if (extract32(ctx->opcode, 22, 1) == 1){ + if (extract32(ctx->opcode, 17, 1) == 1){ + gen_multiply(ctx, rs1, rs2, OPC_RH850_MULU_imm9_reg2_reg3); + } else { + gen_multiply(ctx, rs1, rs2, OPC_RH850_MUL_imm9_reg2_reg3); + } + break; + } + break; + + case OPC_RH850_FORMAT_XI: // DIV instructions in format XI + formXop = extract32(ctx->opcode, 16, 7); + switch(formXop){ + + case OPC_RH850_DIV_reg1_reg2_reg3: + gen_divide(ctx, rs1, rs2, OPC_RH850_DIV_reg1_reg2_reg3); + //DIV + break; + case OPC_RH850_DIVH_reg1_reg2_reg3: + gen_divide(ctx, rs1, rs2, OPC_RH850_DIVH_reg1_reg2_reg3); + //DIVH 2 + break; + case OPC_RH850_DIVHU_reg1_reg2_reg3: + gen_divide(ctx, rs1, rs2, OPC_RH850_DIVHU_reg1_reg2_reg3); + //DIVHU + break; + + case OPC_RH850_DIVQ: + gen_divide(ctx, rs1, rs2, OPC_RH850_DIV_reg1_reg2_reg3); + //DIVQ => using DIV implementation, will be changed if needed + break; + case OPC_RH850_DIVQU: + gen_divide(ctx, rs1, rs2, OPC_RH850_DIVU_reg1_reg2_reg3); + //DIVQU => using DIVU implementation, will be changed if needed + break; + case OPC_RH850_DIVU_reg1_reg2_reg3: + gen_divide(ctx, rs1, rs2, OPC_RH850_DIVU_reg1_reg2_reg3); + //DIVU + break; + } + break; + + case OPC_RH850_FORMAT_XII: // for opcode = 0110 ; format XII instructions + //excluding MUL and including CMOV + // also LDL.W and STC.W (Format VII) + checkXII = extract32(ctx->opcode, 21, 2); + + switch(checkXII){ + case 0: + gen_data_manipulation(ctx, rs1, rs2, OPC_RH850_CMOV_cccc_imm5_reg2_reg3); + break; + case 1: + gen_data_manipulation(ctx, rs1, rs2, OPC_RH850_CMOV_cccc_reg1_reg2_reg3); + break; + case 2: + formXop = extract32(ctx->opcode, 17, 2); + + switch(formXop){ + case OPC_RH850_BSW_reg2_reg3: + gen_data_manipulation(ctx, rs1, rs2, OPC_RH850_BSW_reg2_reg3); + break; + case OPC_RH850_BSH_reg2_reg3: + gen_data_manipulation(ctx, rs1, rs2, OPC_RH850_BSH_reg2_reg3); + break; + case OPC_RH850_HSW_reg2_reg3: + //HSW + gen_data_manipulation(ctx, rs1, rs2, OPC_RH850_HSW_reg2_reg3); + break; + case OPC_RH850_HSH_reg2_reg3: + //HSH + gen_data_manipulation(ctx, rs1, rs2, OPC_RH850_HSH_reg2_reg3); + break; + } + break; + case 3: //these are SCHOL, SCHOR, SCH1L, SCH1R. Also LDL.W + formXop = extract32(ctx->opcode, 17, 2); + switch(formXop){ + case OPC_RH850_SCH0R_reg2_reg3: + if (extract32(ctx->opcode, 5, 11) == 0x3F && + extract32(ctx->opcode, 16, 5) == 0x18) + gen_mutual_exclusion(ctx, extract32(ctx->opcode, 27, 5), + rs1, operation_LDL_W); + else + gen_bit_search(ctx, rs2, OPC_RH850_SCH0R_reg2_reg3); + break; + case OPC_RH850_SCH1R_reg2_reg3: + if (extract32(ctx->opcode, 19, 2) == 0x0){ + gen_bit_search(ctx, rs2, OPC_RH850_SCH1R_reg2_reg3); + } else if (extract32(ctx->opcode, 5, 11) == 0x3F && + extract32(ctx->opcode, 16, 5) == 0x1a) + gen_mutual_exclusion(ctx, extract32(ctx->opcode, 27, 5), + rs1, operation_STC_W); + break; + case OPC_RH850_SCH0L_reg2_reg3: + gen_bit_search(ctx, rs2, OPC_RH850_SCH0L_reg2_reg3); + break; + case OPC_RH850_SCH1L_reg2_reg3: + gen_bit_search(ctx, rs2, OPC_RH850_SCH1L_reg2_reg3); + break; + } + + } + break; + + case OPC_RH850_ADDIT_ARITH: + formXop = extract32(ctx->opcode, 21, 2); + switch(formXop){ + + case OPC_RH850_ADF_SATADD3: + if (extract32(ctx->opcode, 16, 5) == 0x1A){ + gen_sat_op(ctx, rs1, rs2, OPC_RH850_SATADD_reg1_reg2_reg3); + } else { + gen_cond_arith(ctx, rs1, rs2, OPC_RH850_ADF_cccc_reg1_reg2_reg3); + } + break; + case OPC_RH850_SBF_SATSUB: + if (extract32(ctx->opcode, 16, 5) == 0x1A){ + gen_sat_op(ctx, rs1, rs2, OPC_RH850_SATSUB_reg1_reg2_reg3); + } else { + gen_cond_arith(ctx, rs1, rs2, OPC_RH850_SBF_cccc_reg1_reg2_reg3); + } + break; + break; + case OPC_RH850_MAC_reg1_reg2_reg3_reg4: + gen_mul_accumulate(ctx, rs1, rs2, OPC_RH850_MAC_reg1_reg2_reg3_reg4); + break; + case OPC_RH850_MACU_reg1_reg2_reg3_reg4: + gen_mul_accumulate(ctx, rs1, rs2, OPC_RH850_MACU_reg1_reg2_reg3_reg4); + break; + } + } + } + + if (MASK_OP_FORMAT_V_FORMAT_XIII(ctx->opcode) == OPC_RH850_FORMAT_V_XIII){ + if(extract32(ctx->opcode, 16, 1) == 0){ + uint32_t disp22 = extract32(ctx->opcode, 16, 16) | + (extract32(ctx->opcode, 0, 6) << 16 ); + if( (disp22 & 0x200000) == 0x200000){ + disp22 = disp22 | (0x3ff << 22); + } + + if (extract32(ctx->opcode, 11, 5) == 0){ + gen_jmp(ctx, 0, disp22, OPC_RH850_JR_imm22); //JR disp22 + } else { + gen_jmp(ctx, 0, disp22, OPC_RH850_JARL_disp22_reg2); + + + } + }else{ + if (extract32(ctx->opcode, 11, 5) != 0){ + //LD.BU + gen_load(ctx, MO_UB, rs2, rs1, (ld_imm & 0xfffe) | extract32(ctx->opcode, 5, 1), 0); + + }else{ + if (extract32(ctx->opcode, 16, 3) == 0x3){ + gen_special(ctx, env, rs1, rs2, OPC_RH850_PREPARE_list12_imm5_sp); + //PREPARE2 + } + else if (extract32(ctx->opcode, 16, 3) == 0x1){ + gen_special(ctx, env, rs1, rs2, OPC_RH850_PREPARE_list12_imm5); + //PREPARE1 + } + } + } + } + + tcg_temp_free(tcg_ctx, r1); + tcg_temp_free(tcg_ctx, r2); +} + +static void decode_RH850_16(CPURH850State *env, DisasContext *ctx) +{ + int rs1; + int rs2; + int cond; + uint32_t op; + uint32_t subOpCheck; + uint32_t imm; + uint32_t disp32 = 0; + + op = MASK_OP_MAJOR(ctx->opcode); + rs1 = GET_RS1(ctx->opcode); // rs1 at bits b0-b4; + rs2 = GET_RS2(ctx->opcode); // rs2 at bits b11-b15; + imm = rs1; + + if((op & 0xf << 7) == OPC_RH850_BCOND ){ // checking for 4 bit opcode for BCOND + cond = extract32(ctx->opcode, 0, 4); + imm = ( extract32(ctx->opcode, 4, 3) | (extract32(ctx->opcode, 11, 5) << 3)) << 1 ; + + if ( (imm & 0x100) == 0x100){ + imm |= (0x7fffff << 9); + } + gen_branch(env, ctx, cond, rs1, rs2, imm); + + return; + } + + switch(op){ + case OPC_RH850_16bit_0: + if (rs2 != 0) { + gen_arithmetic(ctx, rs1, rs2, OPC_RH850_MOV_reg1_reg2); + break; + } else { + subOpCheck = MASK_OP_FORMAT_I_0(op); + switch(subOpCheck){ + case OPC_RH850_NOP: + break; + case OPC_RH850_SYNCI: + break; + case OPC_RH850_SYNCE: + break; + case OPC_RH850_SYNCM: + break; + case OPC_RH850_SYNCP: + break; + } + } + break; + + case OPC_RH850_16bit_2: + if (rs2 == 0){ + if (rs1 == 0){ + gen_special(ctx, env, rs1, rs2, OPC_RH850_RIE); + break; + } else { + gen_special(ctx, env, rs1, rs2, OPC_RH850_SWITCH_reg1); + break; + } + } else { + if (rs1 == 0){ + gen_special(ctx, env, rs1, rs2, OPC_RH850_FETRAP_vector4); + break; + } else { + gen_divide(ctx, rs1, rs2, OPC_RH850_DIVH_reg1_reg2); + break; + } + } + break; + + case OPC_RH850_16bit_4: + if (rs2 == 0){ + gen_data_manipulation(ctx, rs1, rs2, OPC_RH850_ZXB_reg1); + break; + } else { + gen_sat_op(ctx, rs1, rs2, OPC_RH850_SATSUBR_reg1_reg2); + break; + } + break; + case OPC_RH850_16bit_5: + if (rs2 == 0){ + gen_data_manipulation(ctx, rs1, rs2, OPC_RH850_SXB_reg1); + break; + } else { + gen_sat_op(ctx, rs1, rs2, OPC_RH850_SATSUB_reg1_reg2); + break; + } + break; + case OPC_RH850_16bit_6: + if (rs2 == 0){ + gen_data_manipulation(ctx, rs1, rs2, OPC_RH850_ZXH_reg1); + break; + } else { + gen_sat_op(ctx, rs1, rs2, OPC_RH850_SATADD_reg1_reg2); + break; + } + break; + case OPC_RH850_16bit_7: + if (rs2 == 0){ + gen_data_manipulation(ctx, rs1, rs2, OPC_RH850_SXH_reg1); + break; + } else { + gen_multiply(ctx, rs1, rs2, OPC_RH850_MULH_reg1_reg2); + break; + } + break; + case OPC_RH850_NOT_reg1_reg2: + gen_logical(ctx, rs1, rs2, OPC_RH850_NOT_reg1_reg2); + break; + // decode properly (handle also case when rs2 != 0), then uncomment +// case OPC_RH850_JMP_DISP: + // JMP opcode: DDDD DDDD DDDD DDDD dddd dddd dddd ddd0 0000 0110 111R RRRR +// disp32 = ctx->opcode >> 16; + + + // this case is already handled in decode_RH850_48() + + case OPC_RH850_16bit_3: + if (rs2 == 0) { // JMP + gen_jmp(ctx, rs1, disp32, OPC_RH850_JMP_reg1); + break; + } else { + if(extract32(rs1,4,1)==1){ + //SLD.HU + gen_load(ctx, MO_TEUW, rs2, 30, extract32(ctx->opcode, 0, 4) << 1, 0); + }else{ + //SLD.BU + gen_load(ctx, MO_UB, rs2, 30, extract32(ctx->opcode, 0, 4), 0); + } + break; + } + break; + case OPC_RH850_OR_reg1_reg2: + gen_logical(ctx, rs1, rs2, OPC_RH850_OR_reg1_reg2); + break; + case OPC_RH850_XOR_reg1_reg2: + gen_logical(ctx, rs1, rs2, OPC_RH850_XOR_reg1_reg2); + break; + case OPC_RH850_AND_reg1_reg2: + gen_logical(ctx, rs1, rs2, OPC_RH850_AND_reg1_reg2); + break; + case OPC_RH850_TST_reg1_reg2: + gen_logical(ctx, rs1, rs2, OPC_RH850_TST_reg1_reg2); + break; + case OPC_RH850_SUBR_reg1_reg2: + gen_arithmetic(ctx, rs1, rs2, OPC_RH850_SUBR_reg1_reg2); + break; + case OPC_RH850_SUB_reg1_reg2: + gen_arithmetic(ctx, rs1, rs2, OPC_RH850_SUB_reg1_reg2); + break; + case OPC_RH850_ADD_reg1_reg2: + gen_arithmetic(ctx, rs1, rs2, OPC_RH850_ADD_reg1_reg2); + break; + case OPC_RH850_CMP_reg1_reg2: + gen_arithmetic(ctx, rs1, rs2, OPC_RH850_CMP_reg1_reg2); + break; + case OPC_RH850_16bit_16: + if (rs2 == 0){ + gen_special(ctx, env, rs1, rs2, OPC_RH850_CALLT_imm6); + break; + } else { + gen_arithmetic(ctx, imm, rs2, OPC_RH850_MOV_imm5_reg2); + break; + } + break; + case OPC_RH850_16bit_17: + if (rs2 == 0){ + gen_special(ctx, env, rs1, rs2, OPC_RH850_CALLT_imm6); + break; + } else { + gen_sat_op(ctx, rs1, rs2, OPC_RH850_SATADD_imm5_reg2); + break; + } + break; + case OPC_RH850_ADD_imm5_reg2: + gen_arithmetic(ctx, rs1, rs2, OPC_RH850_ADD_imm5_reg2); + break; + case OPC_RH850_CMP_imm5_reg2: + gen_arithmetic(ctx, rs1, rs2, OPC_RH850_CMP_imm5_reg2); + break; + case OPC_RH850_SHR_imm5_reg2: + gen_data_manipulation(ctx, rs1, rs2, OPC_RH850_SHR_imm5_reg2); + break; + case OPC_RH850_SAR_imm5_reg2: + gen_data_manipulation(ctx, rs1, rs2, OPC_RH850_SAR_imm5_reg2); + break; + case OPC_RH850_SHL_imm5_reg2: + gen_data_manipulation(ctx, rs1, rs2, OPC_RH850_SHL_imm5_reg2); + break; + case OPC_RH850_MULH_imm5_reg2: + gen_multiply(ctx, rs1, rs2, OPC_RH850_MULH_imm5_reg2); + break; + } + + //Format IV ; dividing on code bits b7-b10 + uint32_t opIV = (op >> 7); + opIV = opIV << 5; + + switch(opIV){ + case OPC_RH850_16bit_SLDB: + gen_load(ctx, MO_SB, rs2, 30, extract32(ctx->opcode, 0, 7), 0); + break; + case OPC_RH850_16bit_SLDH: + gen_load(ctx, MO_TESW, rs2, 30, extract32(ctx->opcode, 0, 7) << 1, 0); + break; + case OPC_RH850_16bit_IV10: + if ( extract32(rs1,0,1) == 1 ) { + //SST.W + gen_store(ctx, MO_TEUL, 30, rs2, (extract32(ctx->opcode, 1, 6)) << 2, 0); + /// Note An MAE or MDP exception might occur + /// depending on the result of address calculation. + } + else{ + //SLD.W + gen_load(ctx, MO_TESL, rs2, 30, extract32(ctx->opcode, 1, 6) << 2, 0); + } + break; + case OPC_RH850_16bit_SSTB: + gen_store(ctx, MO_UB, 30, rs2, (extract32(ctx->opcode, 0, 7)), 0); + /// Note An MDP exception might occur depending on the result of address calculation. + break; + case OPC_RH850_16bit_SSTH: + gen_store(ctx, MO_TEUW, 30, rs2, (extract32(ctx->opcode, 0, 7)) << 1, 0); + /// Note An MAE or MDP exception might occur + ///depending on the result of address calculation. + break; + } +} + + +// ################################################################################### +// ################################################################################### +// ################################################################################### + +static void rh850_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + struct uc_struct *uc = cpu->uc; + dc->uc = uc; + + CPURH850State *env = cpu->env_ptr; + dc->env = env; + dc->pc = dc->base.pc_first; +} + +static void rh850_tr_tb_start(DisasContextBase *dcbase, CPUState *cpu) +{ +} + +static void rh850_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + TCGContext *tcg_ctx = dc->uc->tcg_ctx; + + tcg_gen_insn_start(tcg_ctx, dc->pc); +} + +/* + * This f. is called when breakpoint is hit. It should implement + * handling of breakpoint - for example HW breakpoints may be + * handled differently from SW breakpoints (see arm/translate.c). + * However, in RH850 we currently implement only SW breakpoints. + * + * Comment from translator.c: + * The breakpoint_check hook may use DISAS_TOO_MANY to indicate + * that only one more instruction is to be executed. Otherwise + * it should use DISAS_NORETURN when generating an exception, + * but may use a DISAS_TARGET_* value for Something Else. + */ +static bool rh850_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cpu, + const CPUBreakpoint *bp) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + + gen_exception_debug(dc); + /* The address covered by the breakpoint must be included in + [tb->pc, tb->pc + tb->size) in order to for it to be + properly cleared -- thus we increment the PC here so that + the logic setting tb->size below does the right thing. */ + dc->base.pc_next += 2; + dc->base.is_jmp = DISAS_NORETURN; + return true; +} + + +static void rh850_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + CPURH850State *env = dc->env; + + dc->opcode = cpu_lduw_code(env, dc->pc); // get opcode from memory + + if ((extract32(dc->opcode, 9, 2) != 0x3) && (extract32(dc->opcode, 5, 11) != 0x17)) { + dc->base.pc_next = dc->pc + 2; + decode_RH850_16(env, dc); //this function includes 32-bit JR and JARL + } else { + dc->opcode = (dc->opcode) | (cpu_lduw_code(env, dc->pc + 2) << 0x10); + if (((extract32(dc->opcode, 6, 11) == 0x41e) && ((extract32(dc->opcode, 17, 2) > 0x1) || + (extract32(dc->opcode, 17, 3) == 0x4))) || + (extract32(dc->opcode, 5, 11) == 0x31) || //48-bit MOV + (extract32(dc->opcode, 5, 12) == 0x37) || //48-bit JMP + (extract32(dc->opcode, 5, 11) == 0x17) ) { //48-bit JARL and JR + dc->opcode1 = cpu_lduw_code(env, dc->pc + 4); + dc->base.pc_next = dc->pc + 6; + decode_RH850_48(env, dc); + } else { + dc->base.pc_next = dc->pc + 4; + decode_RH850_32(env, dc); + } + } + + dc->pc = dc->base.pc_next; + + //printf("addr: %x\n", dc->pc); + //copyFlagsToPSW(); + +#ifdef RH850_HAS_MMU + if (dc->base.is_jmp == DISAS_NEXT) { + /* Stop translation when the next insn might touch a new page. + * This ensures that prefetch aborts at the right place. + * + * We cannot determine the size of the next insn without + * completely decoding it. However, the maximum insn size + * is 32 bytes, so end if we do not have that much remaining. + * This may produce several small TBs at the end of each page, + * but they will all be linked with goto_tb. + * + * ??? ColdFire maximum is 4 bytes; MC68000's maximum is also + * smaller than MC68020's. + */ + target_ulong start_page_offset + = dc->pc - (dc->base.pc_first & TARGET_PAGE_MASK); + + if (start_page_offset >= TARGET_PAGE_SIZE - 32) { + dc->base.is_jmp = DISAS_TOO_MANY; + } + } +#endif +} + +// Emit exit TB code according to base.is_jmp +static void rh850_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + TCGContext *tcg_ctx = dc->uc->tcg_ctx; + + if (dc->base.is_jmp == DISAS_NORETURN) { + return; + } + if (dc->base.singlestep_enabled) { + if (dc->base.is_jmp == DISAS_NEXT || dc->base.is_jmp == DISAS_TOO_MANY) { + // PC is not loaded inside TB, so we have to do it here in case of + // single stepping + tcg_gen_movi_tl(tcg_ctx, cpu_pc, dc->pc); + } + gen_exception_debug(dc); + } + + switch (dc->base.is_jmp) { + case DISAS_TOO_MANY: + gen_goto_tb_imm(dc, 0, dc->pc); + break; + case DISAS_INDIRECT_JUMP: + /* PC in CPURH850State must have been updated! */ + tcg_gen_lookup_and_goto_ptr(tcg_ctx); + break; + case DISAS_EXIT_TB: + tcg_gen_exit_tb(tcg_ctx, NULL, 0); + break; + case DISAS_NORETURN: + case DISAS_TB_EXIT_ALREADY_GENERATED: + break; + default: + g_assert_not_reached(); + } +} + +static const TranslatorOps rh850_tr_ops = { + .init_disas_context = rh850_tr_init_disas_context, + .tb_start = rh850_tr_tb_start, + .insn_start = rh850_tr_insn_start, + .breakpoint_check = rh850_tr_breakpoint_check, + .translate_insn = rh850_tr_translate_insn, + .tb_stop = rh850_tr_tb_stop, +}; + +/** + * This function translates one translation block (translation block + * is a sequence of instructions without jumps). Translation block + * is the longest translated sequence of instructions. The sequence + * may be shorter, if we are in singlestep mode (1 instruction), if + * breakpoint is detected, ... - see if statements, which break + * while loop below. + */ +#define NEW_GEN_INSN +#ifdef NEW_GEN_INSN +void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int max_insns) +{ + DisasContext dc; + translator_loop(&rh850_tr_ops, &dc.base, cpu, tb, max_insns); +} + +#else // NEW_GEN_INSN + +void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns) +{ + CPURH850State *env = cs->env_ptr; + DisasContext ctx; + target_ulong pc_start = tb->pc; + ctx.pc = pc_start; + + if (false) translator_loop(&rh850_tr_ops, &ctx.base, cs, tb); + /* once we have GDB, the rest of the translate.c implementation should be + ready for singlestep */ + ctx.base.singlestep_enabled = cs->singlestep_enabled; + ctx.base.singlestep_enabled = 1;/// this is only for gdb exceptions + + ctx.base.tb = tb; + ctx.base.is_jmp = DISAS_NEXT; + + ctx.base.num_insns = 0; + ctx.base.max_insns = tb->cflags & CF_COUNT_MASK; + if (ctx.base.max_insns == 0) { + ctx.base.max_insns = CF_COUNT_MASK; + } + if (ctx.base.max_insns > TCG_MAX_INSNS) { + ctx.base.max_insns = TCG_MAX_INSNS; + } + gen_tb_start(tb); + + while (ctx.base.is_jmp == DISAS_NEXT) { + tcg_gen_insn_start(ctx.pc); + ctx.base.num_insns++; + + if (unlikely(cpu_breakpoint_test(cs, ctx.pc, BP_ANY))) { + tcg_gen_movi_tl(cpu_pc, ctx.pc); + gen_exception_debug(&ctx); + /* The address covered by the breakpoint must be included in + [tb->pc, tb->pc + tb->size) in order to for it to be + properly cleared -- thus we increment the PC here so that + the logic setting tb->size below does the right thing. */ + ctx.pc += 4; + goto done_generating; + } + + if (ctx.base.num_insns == ctx.base.max_insns && (tb->cflags & CF_LAST_IO)) { + gen_io_start(); + } + + ctx.opcode = cpu_lduw_code(env, ctx.pc); // get opcode from memory + + if ((extract32(ctx.opcode, 9, 2) != 0x3) && (extract32(ctx.opcode, 5, 11) != 0x17)) { + ctx.base.pc_next = ctx.pc + 2; + decode_RH850_16(env, &ctx); //this function includes 32-bit JR and JARL + } else { + ctx.opcode = (ctx.opcode) | (cpu_lduw_code(env, ctx.pc+2) << 0x10); + if (((extract32(ctx.opcode, 6, 11) == 0x41e) && ((extract32(ctx.opcode, 17, 2) > 0x1) || + (extract32(ctx.opcode, 17, 3) == 0x4))) || + (extract32(ctx.opcode, 5, 11) == 0x31) || //48-bit MOV + (extract32(ctx.opcode, 5, 12) == 0x37) || //48-bit JMP + (extract32(ctx.opcode, 5, 11) == 0x17) ) { //48-bit JARL and JR + ctx.opcode1 = cpu_lduw_code(env, ctx.pc+4); + ctx.base.pc_next = ctx.pc + 6; + decode_RH850_48(env, &ctx); + } else { + ctx.base.pc_next = ctx.pc + 4; + decode_RH850_32(env, &ctx); + } + } + + ctx.pc = ctx.base.pc_next; + + copyFlagsToPSW(); + + if (cs->singlestep_enabled) { + break; + } + if (tcg_op_buf_full()) { + break; + } + if (ctx.base.num_insns >= ctx.base.max_insns) { + break; + } + if (singlestep) { + break; + } + + } + + if (tb->cflags & CF_LAST_IO) { + gen_io_end(); + } + switch (ctx.base.is_jmp) { + case DISAS_TOO_MANY: + gen_goto_tb_imm(&ctx, 0, ctx.pc); + break; + case DISAS_INDIRECT_JUMP: + tcg_gen_lookup_and_goto_ptr(); + break; +// case BS_NONE: /* handle end of page - DO NOT CHAIN. See gen_goto_tb. */ +// tcg_gen_movi_tl(cpu_pc, ctx.pc); +// if (cs->singlestep_enabled) { +// gen_exception_debug(&ctx); +// } else { +// tcg_gen_exit_tb(NULL, 0); +// } +// break; + case DISAS_NORETURN: + case DISAS_TB_EXIT_ALREADY_GENERATED: // ops using BS_BRANCH generate own exit seq + break; + default: + break; + } +done_generating: + gen_tb_end(tb, ctx.base.num_insns); + tb->size = ctx.pc - pc_start; + tb->icount = ctx.base.num_insns; + +#ifdef DEBUG_DISAS + if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) && qemu_log_in_addr_range(pc_start)) { + qemu_log("\nIN: %s\n", lookup_symbol(pc_start)); + log_target_disas(cs, pc_start, ctx.pc - pc_start); + qemu_log("\n"); + } +#endif +} +#endif // OLD_GEN_INSN +void rh850_translate_init(struct uc_struct *uc) +{ + TCGContext *tcg_ctx = uc->tcg_ctx; + int i; + + /* cpu_gpr[0] is a placeholder for the zero register. Do not use it. */ + /* Use the gen_set_gpr and gen_get_gpr helper functions when accessing */ + /* registers, unless you specifically block writes to reg 0 */ + + for (i = 0; i < NUM_GP_REGS; i++) { + cpu_gpr[i] = tcg_global_mem_new(tcg_ctx, tcg_ctx->cpu_env, + offsetof(CPURH850State, gpRegs[i]), rh850_gp_regnames[i]); + } + + for (int bankIdx = 0; bankIdx < NUM_SYS_REG_BANKS; bankIdx++) { + for (int regIdx = 0; regIdx < MAX_SYS_REGS_IN_BANK; regIdx++) { + const char *regName = rh850_sys_regnames[bankIdx][regIdx]; + if (regName != NULL) { + cpu_sysRegs[bankIdx][regIdx] = tcg_global_mem_new(tcg_ctx, tcg_ctx->cpu_env, + offsetof(CPURH850State, systemRegs[bankIdx][regIdx]), + regName); + } else { + cpu_sysRegs[bankIdx][regIdx] = NULL; // mark register as not present + } + } + } + + for (i = 0; i < 1; i++) { + cpu_sysDatabuffRegs[i] = tcg_global_mem_new(tcg_ctx, tcg_ctx->cpu_env, + offsetof(CPURH850State, sysDatabuffRegs[i]), rh850_sys_databuff_regnames[i]); + } + + // PSW register flags + cpu_ZF = tcg_global_mem_new_i32(tcg_ctx, tcg_ctx->cpu_env, offsetof(CPURH850State, Z_flag), "ZF"); + cpu_SF = tcg_global_mem_new_i32(tcg_ctx, tcg_ctx->cpu_env, offsetof(CPURH850State, S_flag), "SF"); + cpu_OVF = tcg_global_mem_new_i32(tcg_ctx, tcg_ctx->cpu_env, offsetof(CPURH850State, OV_flag), "OVF"); + cpu_CYF = tcg_global_mem_new_i32(tcg_ctx, tcg_ctx->cpu_env, offsetof(CPURH850State, CY_flag), "CYF"); + cpu_SATF = tcg_global_mem_new_i32(tcg_ctx, tcg_ctx->cpu_env, offsetof(CPURH850State, SAT_flag), "SAT"); + cpu_ID = tcg_global_mem_new_i32(tcg_ctx, tcg_ctx->cpu_env, offsetof(CPURH850State, ID_flag), "ID"); + cpu_EP = tcg_global_mem_new_i32(tcg_ctx, tcg_ctx->cpu_env, offsetof(CPURH850State, EP_flag), "EP"); + cpu_NP = tcg_global_mem_new_i32(tcg_ctx, tcg_ctx->cpu_env, offsetof(CPURH850State, NP_flag), "NP"); + cpu_EBV = tcg_global_mem_new_i32(tcg_ctx, tcg_ctx->cpu_env, offsetof(CPURH850State, EBV_flag), "EBV"); + cpu_CU0 = tcg_global_mem_new_i32(tcg_ctx, tcg_ctx->cpu_env, offsetof(CPURH850State, CU0_flag), "CU0"); + cpu_CU1 = tcg_global_mem_new_i32(tcg_ctx, tcg_ctx->cpu_env, offsetof(CPURH850State, CU1_flag), "CU1"); + cpu_CU2 = tcg_global_mem_new_i32(tcg_ctx, tcg_ctx->cpu_env, offsetof(CPURH850State, CU2_flag), "CU2"); + cpu_UM = tcg_global_mem_new_i32(tcg_ctx, tcg_ctx->cpu_env, offsetof(CPURH850State, UM_flag), "UM"); + + cpu_pc = tcg_global_mem_new(tcg_ctx, tcg_ctx->cpu_env, offsetof(CPURH850State, pc), "pc"); + load_res = tcg_global_mem_new(tcg_ctx, tcg_ctx->cpu_env, offsetof(CPURH850State, load_res), "load_res"); + load_val = tcg_global_mem_new(tcg_ctx, tcg_ctx->cpu_env, offsetof(CPURH850State, load_val), "load_val"); + + cpu_LLbit = tcg_global_mem_new(tcg_ctx, tcg_ctx->cpu_env, offsetof(CPURH850State, cpu_LLbit), "cpu_LLbit"); + cpu_LLAddress = tcg_global_mem_new(tcg_ctx, tcg_ctx->cpu_env, offsetof(CPURH850State, cpu_LLAddress), "cpu_LLAddress"); + +} diff --git a/qemu/target/rh850/unicorn.c b/qemu/target/rh850/unicorn.c new file mode 100644 index 0000000000..1c1db9eecf --- /dev/null +++ b/qemu/target/rh850/unicorn.c @@ -0,0 +1,195 @@ +/* Unicorn Emulator Engine */ +/* By Nguyen Anh Quynh , 2015-2021 */ + +#include "sysemu/cpus.h" +#include "cpu.h" +#include "unicorn_common.h" +#include "uc_priv.h" +#include "unicorn.h" + +RH850CPU *cpu_rh850_init(struct uc_struct *uc, const char *cpu_model); + +static void rh850_set_pc(struct uc_struct *uc, uint64_t address) +{ + rh850_cpu_set_pc(uc->cpu, address); +} + +static uint64_t rh850_get_pc(struct uc_struct *uc) +{ + return rh850_cpu_get_pc(uc->cpu); +} + +static void rh850_release(void *ctx) +{ + + int i; + TCGContext *tcg_ctx = (TCGContext *)ctx; + RH850CPU *cpu = (RH850CPU *)tcg_ctx->uc->cpu; + CPUTLBDesc *d = cpu->neg.tlb.d; + CPUTLBDescFast *f = cpu->neg.tlb.f; + CPUTLBDesc *desc; + CPUTLBDescFast *fast; + + for (i = 0; i < NB_MMU_MODES; i++) { + desc = &(d[i]); + fast = &(f[i]); + g_free(desc->iotlb); + g_free(fast->table); + } + + release_common(ctx); +} + +void rh850_reg_reset(struct uc_struct *uc) +{ + CPUArchState *env = uc->cpu->env_ptr; + + memset(env->gpRegs, 0, sizeof(env->gpRegs)); + env->pc = 0; +} + +static void reg_read(CPURH850State *env, unsigned int regid, void *value) +{ + int sel_id; + + /* PC */ + if (regid == UC_RH850_REG_PC) + { + *(uint64_t *)value = env->pc; + return; + } + + /* General purpose register. */ + if ((regid >= UC_RH850_REG_R0) && (regid <= UC_RH850_REG_R31)) + { + *(uint64_t *)value = env->gpRegs[regid]; + return; + } + + /* System registers. */ + if ((regid >= UC_RH850_SYSREG_SELID0) && (regid <= (UC_RH850_SYSREG_SELID7 + 32))) + { + sel_id = (regid - 32)/32; + *(uint64_t *)value = env->systemRegs[sel_id][regid % 32]; + return; + } +} + +static void reg_write(CPURH850State *env, unsigned int regid, const void *value) +{ + /* TODO */ + + int sel_id; + + /* PC */ + if (regid == UC_RH850_REG_PC) + { + env->pc = *(uint64_t *)value; + return; + } + + /* General purpose register. */ + if ((regid >= UC_RH850_REG_R0) && (regid <= UC_RH850_REG_R31)) + { + env->gpRegs[regid] = *(uint64_t *)value; + return; + } + + /* System registers. */ + if ((regid >= UC_RH850_SYSREG_SELID0) && (regid <= (UC_RH850_SYSREG_SELID7 + 32))) + { + sel_id = (regid - 32)/32; + env->systemRegs[sel_id][regid % 32] = *(uint64_t *)value; + return; + } +} + +static int rh850_reg_read(struct uc_struct *uc, unsigned int *regs, void **vals, + int count) +{ + CPURH850State *env = &(RH850_CPU(uc->cpu)->env); + int i; + + for (i = 0; i < count; i++) { + unsigned int regid = regs[i]; + void *value = vals[i]; + reg_read(env, regid, value); + } + return 0; +} + +static int rh850_reg_write(struct uc_struct *uc, unsigned int *regs, + void *const *vals, int count) +{ + CPURH850State *env = &(RH850_CPU(uc->cpu)->env); + int i; + + for (i = 0; i < count; i++) { + unsigned int regid = regs[i]; + const void *value = vals[i]; + reg_write(env, regid, value); + if (regid == UC_RH850_REG_PC) { + // force to quit execution and flush TB + uc->quit_request = true; + uc_emu_stop(uc); + } + } + return 0; +} + +DEFAULT_VISIBILITY +int rh850_context_reg_read(struct uc_context *ctx, unsigned int *regs, + void **vals, int count) +{ + CPURH850State *env = (CPURH850State *)ctx->data; + int i; + + for (i = 0; i < count; i++) { + unsigned int regid = regs[i]; + void *value = vals[i]; + reg_read(env, regid, value); + } + + return 0; +} + +DEFAULT_VISIBILITY +int rh850_context_reg_write(struct uc_context *ctx, unsigned int *regs, + void *const *vals, int count) +{ + CPURH850State *env = (CPURH850State *)ctx->data; + int i; + + for (i = 0; i < count; i++) { + unsigned int regid = regs[i]; + const void *value = vals[i]; + reg_write(env, regid, value); + } + + return 0; +} + +static int rh850_cpus_init(struct uc_struct *uc, const char *cpu_model) +{ + RH850CPU *cpu; + + cpu = cpu_rh850_init(uc, cpu_model); + if (cpu == NULL) { + return -1; + } + return 0; +} + +DEFAULT_VISIBILITY +void rh850_uc_init(struct uc_struct *uc) +{ + uc->release = rh850_release; + uc->reg_read = rh850_reg_read; + uc->reg_write = rh850_reg_write; + uc->reg_reset = rh850_reg_reset; + uc->set_pc = rh850_set_pc; + uc->get_pc = rh850_get_pc; + uc->cpus_init = rh850_cpus_init; + uc->cpu_context_size = offsetof(CPURH850State, uc); + uc_common_init(uc); +} diff --git a/qemu/target/rh850/unicorn.h b/qemu/target/rh850/unicorn.h new file mode 100644 index 0000000000..84d0ebd27c --- /dev/null +++ b/qemu/target/rh850/unicorn.h @@ -0,0 +1,19 @@ +/* Unicorn Emulator Engine */ +/* By Damien Cauquil , 2023 */ + +#ifndef UC_QEMU_TARGET_RH850_H +#define UC_QEMU_TARGET_RH850_H + +// functions to read & write registers +// int s390_reg_read(struct uc_struct *uc, unsigned int *regs, void **vals, int +// count); int s390_reg_write(struct uc_struct *uc, unsigned int *regs, void +// *const *vals, int count); +int rh850_context_reg_read(struct uc_context *ctx, unsigned int *regs, + void **vals, int count); +int rh850_context_reg_write(struct uc_context *ctx, unsigned int *regs, + void *const *vals, int count); + +void rh850_reg_reset(struct uc_struct *uc); + +void rh850_uc_init(struct uc_struct *uc); +#endif diff --git a/symbols.sh b/symbols.sh index f2d0a114d8..bf835ad9a7 100755 --- a/symbols.sh +++ b/symbols.sh @@ -6295,7 +6295,7 @@ restore_state_to_opc \ helper_uc_tricore_exit \ " -ARCHS="x86_64 arm aarch64 riscv32 riscv64 mips mipsel mips64 mips64el sparc sparc64 m68k ppc ppc64 s390x tricore" +ARCHS="x86_64 arm aarch64 rh850 riscv32 riscv64 mips mipsel mips64 mips64el sparc sparc64 m68k ppc ppc64 s390x tricore" for arch in $ARCHS; do diff --git a/uc.c b/uc.c index 667f40ca4f..62916dab80 100644 --- a/uc.c +++ b/uc.c @@ -171,6 +171,10 @@ bool uc_arch_supported(uc_arch arch) case UC_ARCH_X86: return true; #endif +#ifdef UNICORN_HAS_RH850 + case UC_ARCH_RH850: + return true; +#endif #ifdef UNICORN_HAS_RISCV case UC_ARCH_RISCV: return true; From b53e0c01888cb7b8c6d49d04a050870a226693fe Mon Sep 17 00:00:00 2001 From: Damien Cauquil Date: Wed, 12 Apr 2023 17:16:04 +0200 Subject: [PATCH 002/263] Improved RH850 integration - Fixed some bugs in unicorn building system - Added RH850 unit test file - Modified instruction decoding routine to handle emulation end address --- CMakeLists.txt | 7 ++ include/unicorn/rh850.h | 4 +- include/unicorn/unicorn.h | 5 +- qemu/rh850.h | 4 + qemu/target/rh850/cpu.c | 1 + qemu/target/rh850/helper.h | 1 + qemu/target/rh850/op_helper.c | 9 +++ qemu/target/rh850/translate.c | 134 ++++++++++++++++++++++------------ tests/unit/test_rh850.c | 40 ++++++++++ uc.c | 22 ++++++ 10 files changed, 179 insertions(+), 48 deletions(-) create mode 100644 tests/unit/test_rh850.c diff --git a/CMakeLists.txt b/CMakeLists.txt index dcc27afcd3..855c9d08ef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1281,6 +1281,13 @@ if(UNICORN_HAS_PPC) target_link_libraries(ppc64-softmmu PRIVATE unicorn-common) set(UNICORN_TEST_FILE ${UNICORN_TEST_FILE} test_ppc) endif() +if(UNICORN_HAS_RH850) + set(UNICORN_COMPILE_OPTIONS ${UNICORN_COMPILE_OPTIONS} -DUNICORN_HAS_RH850) + set(UNICORN_LINK_LIBRARIES ${UNICORN_LINK_LIBRARIES} rh850-softmmu rh850-softmmu) + #set(UNICORN_SAMPLE_FILE ${UNICORN_SAMPLE_FILE} sample_rh850) + target_link_libraries(rh850-softmmu PRIVATE unicorn-common) + set(UNICORN_TEST_FILE ${UNICORN_TEST_FILE} test_rh850) +endif() if(UNICORN_HAS_RISCV) set(UNICORN_COMPILE_OPTIONS ${UNICORN_COMPILE_OPTIONS} -DUNICORN_HAS_RISCV) set(UNICORN_LINK_LIBRARIES ${UNICORN_LINK_LIBRARIES} riscv32-softmmu riscv64-softmmu) diff --git a/include/unicorn/rh850.h b/include/unicorn/rh850.h index 64b03e6f44..676d295ab7 100644 --- a/include/unicorn/rh850.h +++ b/include/unicorn/rh850.h @@ -8,8 +8,8 @@ extern "C" { #endif -#ifdef _MSRH850 -#pragma warRH850disable : 4201) +#ifdef _MSC_VER +#pragma warning(disable : 4201) #endif #define UC_RH850_SYSREG_SELID0 32 diff --git a/include/unicorn/unicorn.h b/include/unicorn/unicorn.h index ca7535645b..ade720409c 100644 --- a/include/unicorn/unicorn.h +++ b/include/unicorn/unicorn.h @@ -107,8 +107,8 @@ typedef enum uc_arch { UC_ARCH_RISCV, // RISCV architecture UC_ARCH_S390X, // S390X architecture UC_ARCH_TRICORE, // TriCore architecture - UC_ARCH_MAX, UC_ARCH_RH850, // Renesas RH850 architecture (V850e3v2) + UC_ARCH_MAX, } uc_arch; // Mode type @@ -154,6 +154,9 @@ typedef enum uc_mode { UC_MODE_SPARC64 = 1 << 3, // 64-bit mode UC_MODE_V9 = 1 << 4, // SparcV9 mode (currently unsupported) + // rh850 + UC_MODE_RH850 = 1 << 2, // 32-bit mode + // riscv UC_MODE_RISCV32 = 1 << 2, // 32-bit mode UC_MODE_RISCV64 = 1 << 3, // 64-bit mode diff --git a/qemu/rh850.h b/qemu/rh850.h index 02909f809b..d4254cceb7 100644 --- a/qemu/rh850.h +++ b/qemu/rh850.h @@ -1277,4 +1277,8 @@ #define gen_helper_vfp_set_fpscr gen_helper_vfp_set_fpscr_rh850 #define gen_helper_cpsr_read gen_helper_cpsr_read_rh850 #define gen_helper_cpsr_write gen_helper_cpsr_write_rh850 +#define gen_intermediate_code gen_intermediate_code_rh850 +#define restore_state_to_opc restore_state_to_opc_rh850 +#define helper_tlb_flush helper_tlb_flush_rh850 +#define helper_uc_rh850_exit helper_uc_rh850_exit_rh850 #endif diff --git a/qemu/target/rh850/cpu.c b/qemu/target/rh850/cpu.c index 172fcd51f2..ec724e6d17 100644 --- a/qemu/target/rh850/cpu.c +++ b/qemu/target/rh850/cpu.c @@ -566,6 +566,7 @@ static void rh850_cpu_init(struct uc_struct *uc, CPUState *obj) cpu_set_cpustate_pointers(cpu); cs->env_ptr = &cpu->env; + cpu->env.uc = uc; } static void rh850_cpu_class_init(struct uc_struct *uc, CPUClass *c) diff --git a/qemu/target/rh850/helper.h b/qemu/target/rh850/helper.h index cbdf08ffec..05907fc983 100644 --- a/qemu/target/rh850/helper.h +++ b/qemu/target/rh850/helper.h @@ -1,6 +1,7 @@ DEF_HELPER_4(uc_tracecode, void, i32, i32, ptr, i64) DEF_HELPER_6(uc_traceopcode, void, ptr, i64, i64, i32, ptr, i64) +DEF_HELPER_1(uc_rh850_exit, void, env) /* Exceptions */ DEF_HELPER_2(raise_exception, noreturn, env, i32) diff --git a/qemu/target/rh850/op_helper.c b/qemu/target/rh850/op_helper.c index 0b39585cae..8566b43eb4 100644 --- a/qemu/target/rh850/op_helper.c +++ b/qemu/target/rh850/op_helper.c @@ -666,4 +666,13 @@ void helper_tlb_flush(CPURH850State *env) tlb_flush(cs); } +void helper_uc_rh850_exit(CPURH850State *env) +{ + CPUState *cs = CPU(env); + + cs->exception_index = EXCP_HLT; + cs->halted = 1; + cpu_loop_exit(cs); +} + #endif /* !CONFIG_USER_ONLY */ diff --git a/qemu/target/rh850/translate.c b/qemu/target/rh850/translate.c index 6f23597f3c..4970821606 100644 --- a/qemu/target/rh850/translate.c +++ b/qemu/target/rh850/translate.c @@ -31,6 +31,14 @@ #include "instmap.h" +#include "unicorn/platform.h" +#include "uc_priv.h" + +/* + * Unicorn: Special disas state for exiting in the middle of tb. + */ +#define DISAS_UC_EXIT DISAS_TARGET_6 + /* global register indices */ static TCGv cpu_gpr[NUM_GP_REGS]; static TCGv cpu_pc; @@ -103,6 +111,7 @@ typedef struct DisasContext { DisasContextBase base; CPURH850State *env; target_ulong pc; // pointer to instruction being translated + target_ulong pc_succ_insn; uint32_t opcode; uint32_t opcode1; // used for 48 bit instructions @@ -189,6 +198,18 @@ static void gen_exception_debug(DisasContext *dc) dc->base.is_jmp = DISAS_TB_EXIT_ALREADY_GENERATED; } +static void gen_exception_halt(DisasContext *dc) +{ + TCGContext *tcg_ctx = dc->uc->tcg_ctx; + + TCGv_i32 helper_tmp = tcg_const_i32(tcg_ctx, EXCP_HLT); + gen_helper_raise_exception(tcg_ctx, tcg_ctx->cpu_env, helper_tmp); + tcg_temp_free_i32(tcg_ctx, helper_tmp); + + //gen_helper_raise_exception(tcg_ctx, tcg_ctx->cpu_env, EXCP_DEBUG); + dc->base.is_jmp = DISAS_TB_EXIT_ALREADY_GENERATED; +} + static void gen_goto_tb_imm(DisasContext *ctx, int n, target_ulong dest) { @@ -4889,7 +4910,7 @@ static void rh850_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu) DisasContext *dc = container_of(dcbase, DisasContext, base); TCGContext *tcg_ctx = dc->uc->tcg_ctx; - tcg_gen_insn_start(tcg_ctx, dc->pc); + tcg_gen_insn_start(tcg_ctx, dc->base.pc_next); } /* @@ -4923,56 +4944,75 @@ static bool rh850_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cpu, static void rh850_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *dc = container_of(dcbase, DisasContext, base); + struct uc_struct *uc = dc->uc; + TCGContext *tcg_ctx = uc->tcg_ctx; + TCGOp *tcg_op, *prev_op = NULL; CPURH850State *env = dc->env; + bool insn_hook = false; - dc->opcode = cpu_lduw_code(env, dc->pc); // get opcode from memory - - if ((extract32(dc->opcode, 9, 2) != 0x3) && (extract32(dc->opcode, 5, 11) != 0x17)) { - dc->base.pc_next = dc->pc + 2; - decode_RH850_16(env, dc); //this function includes 32-bit JR and JARL - } else { - dc->opcode = (dc->opcode) | (cpu_lduw_code(env, dc->pc + 2) << 0x10); - if (((extract32(dc->opcode, 6, 11) == 0x41e) && ((extract32(dc->opcode, 17, 2) > 0x1) || - (extract32(dc->opcode, 17, 3) == 0x4))) || - (extract32(dc->opcode, 5, 11) == 0x31) || //48-bit MOV - (extract32(dc->opcode, 5, 12) == 0x37) || //48-bit JMP - (extract32(dc->opcode, 5, 11) == 0x17) ) { //48-bit JARL and JR - dc->opcode1 = cpu_lduw_code(env, dc->pc + 4); - dc->base.pc_next = dc->pc + 6; - decode_RH850_48(env, dc); - } else { - dc->base.pc_next = dc->pc + 4; - decode_RH850_32(env, dc); - } + if (uc_addr_is_exit(dc->uc, dc->base.pc_next)) { + dcbase->is_jmp = DISAS_UC_EXIT; } + else + { + // Unicorn: trace this instruction on request + if (HOOK_EXISTS_BOUNDED(uc, UC_HOOK_CODE, dc->pc)) { + + // Sync PC in advance + tcg_gen_movi_tl(tcg_ctx, tcg_ctx->cpu_pc, dc->pc); + + // save the last operand + prev_op = tcg_last_op(tcg_ctx); + insn_hook = true; + gen_uc_tracecode(tcg_ctx, 4, UC_HOOK_CODE_IDX, uc, dc->pc); + // the callback might want to stop emulation immediately + check_exit_request(tcg_ctx); + } + + dc->opcode = cpu_lduw_code(env, dc->pc); // get opcode from memory + + if ((extract32(dc->opcode, 9, 2) != 0x3) && (extract32(dc->opcode, 5, 11) != 0x17)) { + dc->base.pc_next = dc->pc + 2; + decode_RH850_16(env, dc); //this function includes 32-bit JR and JARL + } else { + dc->opcode = (dc->opcode) | (cpu_lduw_code(env, dc->pc + 2) << 0x10); + if (((extract32(dc->opcode, 6, 11) == 0x41e) && ((extract32(dc->opcode, 17, 2) > 0x1) || + (extract32(dc->opcode, 17, 3) == 0x4))) || + (extract32(dc->opcode, 5, 11) == 0x31) || //48-bit MOV + (extract32(dc->opcode, 5, 12) == 0x37) || //48-bit JMP + (extract32(dc->opcode, 5, 11) == 0x17) ) { //48-bit JARL and JR + dc->opcode1 = cpu_lduw_code(env, dc->pc + 4); + dc->base.pc_next = dc->pc + 6; + decode_RH850_48(env, dc); + } else { + dc->base.pc_next = dc->pc + 4; + decode_RH850_32(env, dc); + } + } + + if (insn_hook) { + // Unicorn: patch the callback to have the proper instruction size. + if (prev_op) { + // As explained further up in the function where prev_op is + // assigned, we move forward in the tail queue, so we're modifying the + // move instruction generated by gen_uc_tracecode() that contains + // the instruction size to assign the proper size (replacing 0xF1F1F1F1). + tcg_op = QTAILQ_NEXT(prev_op, link); + } else { + // this instruction is the first emulated code ever, + // so the instruction operand is the first operand + tcg_op = QTAILQ_FIRST(&tcg_ctx->ops); + } - dc->pc = dc->base.pc_next; - - //printf("addr: %x\n", dc->pc); - //copyFlagsToPSW(); - -#ifdef RH850_HAS_MMU - if (dc->base.is_jmp == DISAS_NEXT) { - /* Stop translation when the next insn might touch a new page. - * This ensures that prefetch aborts at the right place. - * - * We cannot determine the size of the next insn without - * completely decoding it. However, the maximum insn size - * is 32 bytes, so end if we do not have that much remaining. - * This may produce several small TBs at the end of each page, - * but they will all be linked with goto_tb. - * - * ??? ColdFire maximum is 4 bytes; MC68000's maximum is also - * smaller than MC68020's. - */ - target_ulong start_page_offset - = dc->pc - (dc->base.pc_first & TARGET_PAGE_MASK); - - if (start_page_offset >= TARGET_PAGE_SIZE - 32) { - dc->base.is_jmp = DISAS_TOO_MANY; + /* + TODO: implement pc_succ_insn in instruction decoding sub-routines + to track instruction size. + */ + //tcg_op->args[1] = dc->pc_succ_insn - dc->base.pc_next; } + + dc->pc = dc->base.pc_next; } -#endif } // Emit exit TB code according to base.is_jmp @@ -5007,6 +5047,10 @@ static void rh850_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) case DISAS_NORETURN: case DISAS_TB_EXIT_ALREADY_GENERATED: break; + case DISAS_UC_EXIT: + tcg_gen_movi_tl(tcg_ctx, cpu_pc, dc->pc); + gen_exception_halt(dc); + break; default: g_assert_not_reached(); } diff --git a/tests/unit/test_rh850.c b/tests/unit/test_rh850.c new file mode 100644 index 0000000000..35a3bf9666 --- /dev/null +++ b/tests/unit/test_rh850.c @@ -0,0 +1,40 @@ +#include "unicorn_test.h" + +const uint64_t code_start = 0x1000; +const uint64_t code_len = 0x4000; + +static void uc_common_setup(uc_engine **uc, uc_arch arch, uc_mode mode, + const char *code, uint64_t size) +{ + OK(uc_open(arch, mode, uc)); + OK(uc_mem_map(*uc, code_start, code_len, UC_PROT_ALL)); + OK(uc_mem_write(*uc, code_start, code, size)); +} + +static void test_rh850_add(void) +{ + char code[] = "\x01\x0e\x06\x00\xc1\x11"; + uint64_t r1 = 0x1234; + uint64_t r2 = 0x7777; + uint64_t pc; + uc_engine *uc; + + uc_common_setup(&uc, UC_ARCH_RH850, UC_MODE_LITTLE_ENDIAN, code, + sizeof(code) - 1); + OK(uc_reg_write(uc, UC_RH850_REG_R1, &r1)); + OK(uc_reg_write(uc, UC_RH850_REG_R2, &r2)); + + OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0)); + + OK(uc_reg_read(uc, UC_RH850_REG_R1, &r1)); + OK(uc_reg_read(uc, UC_RH850_REG_R2, &r2)); + OK(uc_reg_read(uc, UC_RH850_REG_PC, &pc)); + + TEST_CHECK(r1 == 0x1234 + 6); + TEST_CHECK(r2 == 0x89b1); + TEST_CHECK(pc == code_start + sizeof(code) - 1); + + OK(uc_close(uc)); +} + +TEST_LIST = {{"test_rh850_add", test_rh850_add}, {NULL, NULL}}; \ No newline at end of file diff --git a/uc.c b/uc.c index 62916dab80..fbce7034d5 100644 --- a/uc.c +++ b/uc.c @@ -23,6 +23,7 @@ #include "qemu/target/mips/unicorn.h" #include "qemu/target/sparc/unicorn.h" #include "qemu/target/ppc/unicorn.h" +#include "qemu/target/rh850/unicorn.h" #include "qemu/target/riscv/unicorn.h" #include "qemu/target/s390x/unicorn.h" #include "qemu/target/tricore/unicorn.h" @@ -378,6 +379,15 @@ uc_err uc_open(uc_arch arch, uc_mode mode, uc_engine **result) } break; #endif +#ifdef UNICORN_HAS_RH850 + case UC_ARCH_RH850: + if (mode != UC_MODE_LITTLE_ENDIAN) { + free(uc); + return UC_ERR_MODE; + } + uc->init_arch = rh850_uc_init; + break; +#endif #ifdef UNICORN_HAS_RISCV case UC_ARCH_RISCV: if ((mode & ~UC_MODE_RISCV_MASK) || @@ -822,6 +832,11 @@ uc_err uc_emu_start(uc_engine *uc, uint64_t begin, uint64_t until, } break; #endif +#ifdef UNICORN_HAS_RH850 + case UC_ARCH_RH850: + uc_reg_write(uc, UC_RH850_REG_PC, &begin); + break; +#endif #ifdef UNICORN_HAS_RISCV case UC_ARCH_RISCV: if (uc->mode & UC_MODE_RISCV64) { @@ -897,6 +912,7 @@ uc_err uc_emu_start(uc_engine *uc, uint64_t begin, uint64_t until, if (timeout) { // wait for the timer to finish + printf("Wait VM to finish ...\n"); qemu_thread_join(&uc->timer); } @@ -2019,6 +2035,12 @@ static void find_context_reg_rw_function(uc_arch arch, uc_mode mode, } break; #endif +#ifdef UNICORN_HAS_RH850 + case UC_ARCH_RH850: + rw->context_reg_read = rh850_context_reg_read; + rw->context_reg_write = rh850_context_reg_write; + break; +#endif #ifdef UNICORN_HAS_RISCV case UC_ARCH_RISCV: if (mode & UC_MODE_RISCV32) { From f43ffbe5108c87bc8cdee141d2f1f70771d8f2fc Mon Sep 17 00:00:00 2001 From: Damien Cauquil Date: Thu, 13 Apr 2023 09:00:38 +0200 Subject: [PATCH 003/263] Removed hook-related code (causes some issues for now). --- .../dotnet/UnicornManaged/Const/Common.fs | 4 +- bindings/dotnet/UnicornManaged/Const/Rh850.fs | 99 + bindings/go/unicorn/rh850_const.go | 94 + bindings/go/unicorn/unicorn_const.go | 4 +- bindings/java/unicorn/Rh850Const.java | 97 + bindings/java/unicorn/UnicornConst.java | 4 +- bindings/pascal/unicorn/Rh850Const.pas | 99 + bindings/pascal/unicorn/UnicornConst.pas | 4 +- .../python3.9/site-packages/easy-install.pth | 1 + .../python3.9/site-packages/easy_install.py | 5 + .../pip-20.3.4.dist-info/INSTALLER | 1 + .../pip-20.3.4.dist-info/LICENSE.txt | 20 + .../pip-20.3.4.dist-info/METADATA | 94 + .../site-packages/pip-20.3.4.dist-info/RECORD | 284 + .../pip-20.3.4.dist-info/REQUESTED | 0 .../site-packages/pip-20.3.4.dist-info/WHEEL | 6 + .../pip-20.3.4.dist-info/entry_points.txt | 5 + .../pip-20.3.4.dist-info/top_level.txt | 1 + .../python3.9/site-packages/pip/__init__.py | 18 + .../python3.9/site-packages/pip/__main__.py | 26 + .../site-packages/pip/_internal/__init__.py | 17 + .../site-packages/pip/_internal/cache.py | 346 + .../pip/_internal/cli/__init__.py | 4 + .../pip/_internal/cli/autocompletion.py | 164 + .../pip/_internal/cli/base_command.py | 260 + .../pip/_internal/cli/cmdoptions.py | 971 +++ .../pip/_internal/cli/command_context.py | 36 + .../site-packages/pip/_internal/cli/main.py | 75 + .../pip/_internal/cli/main_parser.py | 96 + .../site-packages/pip/_internal/cli/parser.py | 285 + .../pip/_internal/cli/progress_bars.py | 280 + .../pip/_internal/cli/req_command.py | 436 ++ .../pip/_internal/cli/spinners.py | 173 + .../pip/_internal/cli/status_codes.py | 8 + .../pip/_internal/commands/__init__.py | 123 + .../pip/_internal/commands/cache.py | 234 + .../pip/_internal/commands/check.py | 51 + .../pip/_internal/commands/completion.py | 98 + .../pip/_internal/commands/configuration.py | 280 + .../pip/_internal/commands/debug.py | 251 + .../pip/_internal/commands/download.py | 143 + .../pip/_internal/commands/freeze.py | 116 + .../pip/_internal/commands/hash.py | 63 + .../pip/_internal/commands/help.py | 46 + .../pip/_internal/commands/install.py | 763 +++ .../pip/_internal/commands/list.py | 328 + .../pip/_internal/commands/search.py | 169 + .../pip/_internal/commands/show.py | 186 + .../pip/_internal/commands/uninstall.py | 95 + .../pip/_internal/commands/wheel.py | 198 + .../pip/_internal/configuration.py | 407 ++ .../pip/_internal/distributions/__init__.py | 24 + .../pip/_internal/distributions/base.py | 46 + .../pip/_internal/distributions/installed.py | 25 + .../pip/_internal/distributions/sdist.py | 105 + .../pip/_internal/distributions/wheel.py | 37 + .../site-packages/pip/_internal/exceptions.py | 391 ++ .../pip/_internal/index/__init__.py | 2 + .../pip/_internal/index/collector.py | 667 ++ .../pip/_internal/index/package_finder.py | 1015 +++ .../site-packages/pip/_internal/locations.py | 199 + .../site-packages/pip/_internal/main.py | 16 + .../pip/_internal/models/__init__.py | 2 + .../pip/_internal/models/candidate.py | 39 + .../pip/_internal/models/direct_url.py | 243 + .../pip/_internal/models/format_control.py | 92 + .../pip/_internal/models/index.py | 34 + .../pip/_internal/models/link.py | 246 + .../pip/_internal/models/scheme.py | 31 + .../pip/_internal/models/search_scope.py | 135 + .../pip/_internal/models/selection_prefs.py | 50 + .../pip/_internal/models/target_python.py | 117 + .../pip/_internal/models/wheel.py | 78 + .../pip/_internal/network/__init__.py | 2 + .../pip/_internal/network/auth.py | 310 + .../pip/_internal/network/cache.py | 79 + .../pip/_internal/network/download.py | 202 + .../pip/_internal/network/lazy_wheel.py | 231 + .../pip/_internal/network/session.py | 428 ++ .../pip/_internal/network/utils.py | 97 + .../pip/_internal/network/xmlrpc.py | 53 + .../pip/_internal/operations/__init__.py | 0 .../pip/_internal/operations/check.py | 155 + .../pip/_internal/operations/freeze.py | 277 + .../_internal/operations/install/__init__.py | 2 + .../operations/install/editable_legacy.py | 52 + .../_internal/operations/install/legacy.py | 130 + .../pip/_internal/operations/install/wheel.py | 846 +++ .../pip/_internal/operations/prepare.py | 608 ++ .../site-packages/pip/_internal/pyproject.py | 196 + .../pip/_internal/req/__init__.py | 103 + .../pip/_internal/req/constructors.py | 476 ++ .../pip/_internal/req/req_file.py | 574 ++ .../pip/_internal/req/req_install.py | 915 +++ .../pip/_internal/req/req_set.py | 204 + .../pip/_internal/req/req_tracker.py | 151 + .../pip/_internal/req/req_uninstall.py | 657 ++ .../pip/_internal/resolution/__init__.py | 0 .../pip/_internal/resolution/base.py | 21 + .../_internal/resolution/legacy/__init__.py | 0 .../_internal/resolution/legacy/resolver.py | 473 ++ .../resolution/resolvelib/__init__.py | 0 .../_internal/resolution/resolvelib/base.py | 156 + .../resolution/resolvelib/candidates.py | 604 ++ .../resolution/resolvelib/factory.py | 504 ++ .../resolution/resolvelib/found_candidates.py | 101 + .../resolution/resolvelib/provider.py | 174 + .../resolution/resolvelib/reporter.py | 84 + .../resolution/resolvelib/requirements.py | 201 + .../resolution/resolvelib/resolver.py | 297 + .../pip/_internal/self_outdated_check.py | 197 + .../pip/_internal/utils/__init__.py | 0 .../pip/_internal/utils/appdirs.py | 44 + .../pip/_internal/utils/compat.py | 293 + .../pip/_internal/utils/compatibility_tags.py | 178 + .../pip/_internal/utils/datetime.py | 14 + .../pip/_internal/utils/deprecation.py | 104 + .../pip/_internal/utils/direct_url_helpers.py | 126 + .../pip/_internal/utils/distutils_args.py | 48 + .../pip/_internal/utils/encoding.py | 41 + .../pip/_internal/utils/entrypoints.py | 31 + .../pip/_internal/utils/filesystem.py | 224 + .../pip/_internal/utils/filetypes.py | 26 + .../pip/_internal/utils/glibc.py | 98 + .../pip/_internal/utils/hashes.py | 169 + .../_internal/utils/inject_securetransport.py | 36 + .../pip/_internal/utils/logging.py | 399 ++ .../site-packages/pip/_internal/utils/misc.py | 977 +++ .../pip/_internal/utils/models.py | 44 + .../pip/_internal/utils/packaging.py | 95 + .../pip/_internal/utils/parallel.py | 107 + .../pip/_internal/utils/pkg_resources.py | 44 + .../pip/_internal/utils/setuptools_build.py | 181 + .../pip/_internal/utils/subprocess.py | 299 + .../pip/_internal/utils/temp_dir.py | 284 + .../pip/_internal/utils/typing.py | 38 + .../pip/_internal/utils/unpacking.py | 281 + .../site-packages/pip/_internal/utils/urls.py | 55 + .../pip/_internal/utils/virtualenv.py | 119 + .../pip/_internal/utils/wheel.py | 225 + .../pip/_internal/vcs/__init__.py | 15 + .../site-packages/pip/_internal/vcs/bazaar.py | 123 + .../site-packages/pip/_internal/vcs/git.py | 460 ++ .../pip/_internal/vcs/mercurial.py | 172 + .../pip/_internal/vcs/subversion.py | 340 + .../pip/_internal/vcs/versioncontrol.py | 735 +++ .../pip/_internal/wheel_builder.py | 363 ++ .../site-packages/pip/_vendor/__init__.py | 123 + .../site-packages/pip/_vendor/vendor.txt | 24 + .../pkg_resources-0.0.0.dist-info/AUTHORS.txt | 590 ++ .../pkg_resources-0.0.0.dist-info/INSTALLER | 1 + .../pkg_resources-0.0.0.dist-info/LICENSE.txt | 20 + .../pkg_resources-0.0.0.dist-info/METADATA | 13 + .../pkg_resources-0.0.0.dist-info/RECORD | 39 + .../pkg_resources-0.0.0.dist-info/REQUESTED | 0 .../pkg_resources-0.0.0.dist-info/WHEEL | 6 + .../site-packages/pkg_resources/__init__.py | 3296 ++++++++++ .../pkg_resources/_vendor/__init__.py | 0 .../pkg_resources/_vendor/appdirs.py | 608 ++ .../_vendor/packaging/__about__.py | 21 + .../_vendor/packaging/__init__.py | 14 + .../_vendor/packaging/_compat.py | 30 + .../_vendor/packaging/_structures.py | 68 + .../_vendor/packaging/markers.py | 301 + .../_vendor/packaging/requirements.py | 127 + .../_vendor/packaging/specifiers.py | 774 +++ .../pkg_resources/_vendor/packaging/utils.py | 14 + .../_vendor/packaging/version.py | 393 ++ .../pkg_resources/_vendor/pyparsing.py | 5742 +++++++++++++++++ .../pkg_resources/_vendor/six.py | 868 +++ .../pkg_resources/extern/__init__.py | 73 + .../site-packages/pkg_resources/py31compat.py | 23 + .../setuptools-44.1.1.dist-info/AUTHORS.txt | 590 ++ .../setuptools-44.1.1.dist-info/INSTALLER | 1 + .../setuptools-44.1.1.dist-info/LICENSE.txt | 20 + .../setuptools-44.1.1.dist-info/METADATA | 82 + .../setuptools-44.1.1.dist-info/RECORD | 164 + .../setuptools-44.1.1.dist-info/REQUESTED | 0 .../setuptools-44.1.1.dist-info/WHEEL | 6 + .../dependency_links.txt | 2 + .../entry_points.txt | 68 + .../setuptools-44.1.1.dist-info/top_level.txt | 3 + .../setuptools-44.1.1.dist-info/zip-safe | 1 + .../site-packages/setuptools/__init__.py | 245 + .../setuptools/_deprecation_warning.py | 7 + .../site-packages/setuptools/_imp.py | 73 + .../setuptools/_vendor/__init__.py | 0 .../setuptools/_vendor/ordered_set.py | 488 ++ .../setuptools/_vendor/packaging/__about__.py | 27 + .../setuptools/_vendor/packaging/__init__.py | 26 + .../setuptools/_vendor/packaging/_compat.py | 31 + .../_vendor/packaging/_structures.py | 68 + .../setuptools/_vendor/packaging/markers.py | 296 + .../_vendor/packaging/requirements.py | 138 + .../_vendor/packaging/specifiers.py | 749 +++ .../setuptools/_vendor/packaging/tags.py | 404 ++ .../setuptools/_vendor/packaging/utils.py | 57 + .../setuptools/_vendor/packaging/version.py | 420 ++ .../setuptools/_vendor/pyparsing.py | 5742 +++++++++++++++++ .../site-packages/setuptools/_vendor/six.py | 868 +++ .../site-packages/setuptools/archive_util.py | 173 + .../setuptools/command/__init__.py | 17 + .../site-packages/setuptools/command/alias.py | 80 + .../setuptools/command/bdist_egg.py | 502 ++ .../setuptools/command/bdist_rpm.py | 43 + .../setuptools/command/bdist_wininst.py | 21 + .../setuptools/command/develop.py | 221 + .../setuptools/command/dist_info.py | 36 + .../setuptools/command/easy_install.py | 2402 +++++++ .../setuptools/command/egg_info.py | 717 ++ .../setuptools/command/install.py | 125 + .../setuptools/command/install_egg_info.py | 82 + .../setuptools/command/install_lib.py | 147 + .../setuptools/command/install_scripts.py | 65 + .../setuptools/command/launcher manifest.xml | 15 + .../setuptools/command/py36compat.py | 136 + .../setuptools/command/register.py | 18 + .../setuptools/command/rotate.py | 66 + .../setuptools/command/saveopts.py | 22 + .../site-packages/setuptools/command/sdist.py | 252 + .../setuptools/command/setopt.py | 149 + .../site-packages/setuptools/command/test.py | 279 + .../setuptools/command/upload.py | 17 + .../setuptools/command/upload_docs.py | 206 + .../site-packages/setuptools/config.py | 659 ++ .../site-packages/setuptools/dep_util.py | 23 + .../site-packages/setuptools/depends.py | 176 + .../site-packages/setuptools/dist.py | 1274 ++++ .../site-packages/setuptools/errors.py | 16 + .../site-packages/setuptools/extension.py | 57 + .../setuptools/extern/__init__.py | 73 + .../site-packages/setuptools/glob.py | 174 + .../site-packages/setuptools/installer.py | 150 + .../site-packages/setuptools/launch.py | 35 + .../site-packages/setuptools/lib2to3_ex.py | 62 + .../site-packages/setuptools/monkey.py | 179 + .../site-packages/setuptools/msvc.py | 1679 +++++ .../site-packages/setuptools/namespaces.py | 107 + .../site-packages/setuptools/package_index.py | 1136 ++++ .../site-packages/setuptools/py27compat.py | 60 + .../site-packages/setuptools/py31compat.py | 32 + .../site-packages/setuptools/py33compat.py | 59 + .../site-packages/setuptools/py34compat.py | 13 + .../site-packages/setuptools/sandbox.py | 491 ++ .../setuptools/script (dev).tmpl | 6 + .../site-packages/setuptools/script.tmpl | 3 + .../site-packages/setuptools/site-patch.py | 74 + .../site-packages/setuptools/ssl_support.py | 260 + .../site-packages/setuptools/unicode_utils.py | 44 + .../site-packages/setuptools/version.py | 6 + .../site-packages/setuptools/wheel.py | 220 + .../setuptools/windows_support.py | 29 + .../EGG-INFO/PKG-INFO | 32 + .../EGG-INFO/SOURCES.txt | 24 + .../EGG-INFO/dependency_links.txt | 1 + .../EGG-INFO/not-zip-safe | 1 + .../EGG-INFO/top_level.txt | 1 + .../unicorn/__init__.py | 4 + .../unicorn/arm64_const.py | 333 + .../unicorn/arm_const.py | 192 + .../unicorn/include/unicorn/arm.h | 232 + .../unicorn/include/unicorn/arm64.h | 393 ++ .../unicorn/include/unicorn/m68k.h | 65 + .../unicorn/include/unicorn/mips.h | 275 + .../unicorn/include/unicorn/platform.h | 263 + .../unicorn/include/unicorn/ppc.h | 434 ++ .../unicorn/include/unicorn/rh850.h | 111 + .../unicorn/include/unicorn/riscv.h | 314 + .../unicorn/include/unicorn/s390x.h | 144 + .../unicorn/include/unicorn/sparc.h | 172 + .../unicorn/include/unicorn/tricore.h | 174 + .../unicorn/include/unicorn/unicorn.h | 1176 ++++ .../unicorn/include/unicorn/x86.h | 1681 +++++ .../unicorn/m68k_const.py | 37 + .../unicorn/mips_const.py | 235 + .../unicorn/ppc_const.py | 404 ++ .../unicorn/rh850_const.py | 91 + .../unicorn/riscv_const.py | 285 + .../unicorn/s390x_const.py | 122 + .../unicorn/sparc_const.py | 134 + .../unicorn/tricore_const.py | 124 + .../unicorn/unicorn.py | 1051 +++ .../unicorn/unicorn_const.py | 142 + .../unicorn/x86_const.py | 1628 +++++ bindings/python/bindings-test/lib64 | 1 + bindings/python/bindings-test/pyvenv.cfg | 3 + .../CacheControl-0.12.6-py2.py3-none-any.whl | Bin 0 -> 23441 bytes .../appdirs-1.4.4-py2.py3-none-any.whl | Bin 0 -> 14285 bytes .../certifi-2020.6.20-py2.py3-none-any.whl | Bin 0 -> 161344 bytes .../chardet-4.0.0-py2.py3-none-any.whl | Bin 0 -> 174749 bytes .../colorama-0.4.4-py2.py3-none-any.whl | Bin 0 -> 20722 bytes ...ntextlib2-0.6.0.post1-py2.py3-none-any.whl | Bin 0 -> 12692 bytes .../distlib-0.3.1-py2.py3-none-any.whl | Bin 0 -> 147633 bytes .../distro-1.5.0-py2.py3-none-any.whl | Bin 0 -> 19426 bytes .../html5lib-1.1-py2.py3-none-any.whl | Bin 0 -> 116071 bytes .../idna-2.10-py2.py3-none-any.whl | Bin 0 -> 63344 bytes .../ipaddr-2.2.0-py2.py3-none-any.whl | Bin 0 -> 19706 bytes .../msgpack-1.0.0-py2.py3-none-any.whl | Bin 0 -> 75866 bytes .../packaging-20.9-py2.py3-none-any.whl | Bin 0 -> 41435 bytes .../pep517-0.9.1-py2.py3-none-any.whl | Bin 0 -> 22249 bytes .../pip-20.3.4-py2.py3-none-any.whl | Bin 0 -> 311123 bytes .../pkg_resources-0.0.0-py2.py3-none-any.whl | Bin 0 -> 122731 bytes .../progress-1.5-py2.py3-none-any.whl | Bin 0 -> 12965 bytes .../pyparsing-2.4.7-py2.py3-none-any.whl | Bin 0 -> 72626 bytes .../requests-2.25.1-py2.py3-none-any.whl | Bin 0 -> 62975 bytes .../resolvelib-0.5.4-py2.py3-none-any.whl | Bin 0 -> 17707 bytes .../retrying-1.3.3-py2.py3-none-any.whl | Bin 0 -> 11776 bytes .../setuptools-44.1.1-py2.py3-none-any.whl | Bin 0 -> 473123 bytes .../six-1.16.0-py2.py3-none-any.whl | Bin 0 -> 15791 bytes .../toml-0.10.1-py2.py3-none-any.whl | Bin 0 -> 21108 bytes .../urllib3-1.26.5-py2.py3-none-any.whl | Bin 0 -> 134200 bytes .../webencodings-0.5.1-py2.py3-none-any.whl | Bin 0 -> 15904 bytes .../wheel-0.34.2-py2.py3-none-any.whl | Bin 0 -> 31030 bytes bindings/python/sample_rh850.py | 70 + bindings/python/unicorn/unicorn_const.py | 5 +- .../lib/unicorn_engine/rh850_const.rb | 94 + .../lib/unicorn_engine/unicorn_const.rb | 4 +- qemu/target/rh850/translate.c | 4 + 318 files changed, 74591 insertions(+), 7 deletions(-) create mode 100644 bindings/dotnet/UnicornManaged/Const/Rh850.fs create mode 100644 bindings/go/unicorn/rh850_const.go create mode 100644 bindings/java/unicorn/Rh850Const.java create mode 100644 bindings/pascal/unicorn/Rh850Const.pas create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/easy-install.pth create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/easy_install.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip-20.3.4.dist-info/INSTALLER create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip-20.3.4.dist-info/LICENSE.txt create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip-20.3.4.dist-info/METADATA create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip-20.3.4.dist-info/RECORD create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip-20.3.4.dist-info/REQUESTED create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip-20.3.4.dist-info/WHEEL create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip-20.3.4.dist-info/entry_points.txt create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip-20.3.4.dist-info/top_level.txt create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/__init__.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/__main__.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/__init__.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cache.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/__init__.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/autocompletion.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/base_command.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/cmdoptions.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/command_context.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/main.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/main_parser.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/parser.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/progress_bars.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/req_command.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/spinners.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/status_codes.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/__init__.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/cache.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/check.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/completion.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/configuration.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/debug.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/download.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/freeze.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/hash.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/help.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/install.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/list.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/search.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/show.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/uninstall.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/wheel.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/configuration.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/distributions/__init__.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/distributions/base.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/distributions/installed.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/distributions/sdist.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/distributions/wheel.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/exceptions.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/index/__init__.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/index/collector.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/index/package_finder.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/locations.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/main.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/models/__init__.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/models/candidate.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/models/direct_url.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/models/format_control.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/models/index.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/models/link.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/models/scheme.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/models/search_scope.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/models/selection_prefs.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/models/target_python.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/models/wheel.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/network/__init__.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/network/auth.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/network/cache.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/network/download.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/network/lazy_wheel.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/network/session.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/network/utils.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/network/xmlrpc.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/operations/__init__.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/operations/check.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/operations/freeze.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/operations/install/__init__.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/operations/install/editable_legacy.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/operations/install/legacy.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/operations/install/wheel.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/operations/prepare.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/pyproject.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/req/__init__.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/req/constructors.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/req/req_file.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/req/req_install.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/req/req_set.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/req/req_tracker.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/req/req_uninstall.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/__init__.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/base.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/legacy/__init__.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/legacy/resolver.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__init__.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/base.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/candidates.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/factory.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/found_candidates.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/provider.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/reporter.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/requirements.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/resolver.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/self_outdated_check.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/__init__.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/appdirs.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/compat.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/compatibility_tags.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/datetime.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/deprecation.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/direct_url_helpers.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/distutils_args.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/encoding.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/entrypoints.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/filesystem.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/filetypes.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/glibc.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/hashes.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/inject_securetransport.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/logging.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/misc.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/models.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/packaging.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/parallel.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/pkg_resources.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/setuptools_build.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/subprocess.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/temp_dir.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/typing.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/unpacking.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/urls.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/virtualenv.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/wheel.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/vcs/__init__.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/vcs/bazaar.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/vcs/git.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/vcs/mercurial.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/vcs/subversion.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/vcs/versioncontrol.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/wheel_builder.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_vendor/__init__.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pip/_vendor/vendor.txt create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/AUTHORS.txt create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/INSTALLER create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/LICENSE.txt create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/METADATA create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/RECORD create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/REQUESTED create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/WHEEL create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/__init__.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/__init__.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/appdirs.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__about__.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__init__.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/_compat.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/_structures.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/markers.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/requirements.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/specifiers.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/utils.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/version.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/pyparsing.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/six.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/extern/__init__.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/py31compat.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/AUTHORS.txt create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/INSTALLER create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/LICENSE.txt create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/METADATA create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/RECORD create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/REQUESTED create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/WHEEL create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/dependency_links.txt create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/entry_points.txt create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/top_level.txt create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/zip-safe create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/__init__.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_deprecation_warning.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_imp.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/__init__.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/ordered_set.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/packaging/__about__.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/packaging/__init__.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/packaging/_compat.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/packaging/_structures.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/packaging/markers.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/packaging/requirements.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/packaging/specifiers.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/packaging/tags.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/packaging/utils.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/packaging/version.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/pyparsing.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/six.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/archive_util.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/__init__.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/alias.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/bdist_egg.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/bdist_rpm.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/bdist_wininst.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/develop.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/dist_info.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/easy_install.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/egg_info.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/install.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/install_egg_info.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/install_lib.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/install_scripts.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/launcher manifest.xml create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/py36compat.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/register.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/rotate.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/saveopts.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/sdist.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/setopt.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/test.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/upload.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/upload_docs.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/config.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/dep_util.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/depends.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/dist.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/errors.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/extension.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/extern/__init__.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/glob.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/installer.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/launch.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/lib2to3_ex.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/monkey.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/msvc.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/namespaces.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/package_index.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/py27compat.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/py31compat.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/py33compat.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/py34compat.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/sandbox.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/script (dev).tmpl create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/script.tmpl create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/site-patch.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/ssl_support.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/unicode_utils.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/version.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/wheel.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/windows_support.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/EGG-INFO/PKG-INFO create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/EGG-INFO/SOURCES.txt create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/EGG-INFO/dependency_links.txt create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/EGG-INFO/not-zip-safe create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/EGG-INFO/top_level.txt create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/__init__.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/arm64_const.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/arm_const.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/arm.h create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/arm64.h create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/m68k.h create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/mips.h create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/platform.h create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/ppc.h create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/rh850.h create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/riscv.h create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/s390x.h create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/sparc.h create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/tricore.h create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/unicorn.h create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/x86.h create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/m68k_const.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/mips_const.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/ppc_const.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/rh850_const.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/riscv_const.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/s390x_const.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/sparc_const.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/tricore_const.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/unicorn.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/unicorn_const.py create mode 100644 bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/x86_const.py create mode 120000 bindings/python/bindings-test/lib64 create mode 100644 bindings/python/bindings-test/pyvenv.cfg create mode 100644 bindings/python/bindings-test/share/python-wheels/CacheControl-0.12.6-py2.py3-none-any.whl create mode 100644 bindings/python/bindings-test/share/python-wheels/appdirs-1.4.4-py2.py3-none-any.whl create mode 100644 bindings/python/bindings-test/share/python-wheels/certifi-2020.6.20-py2.py3-none-any.whl create mode 100644 bindings/python/bindings-test/share/python-wheels/chardet-4.0.0-py2.py3-none-any.whl create mode 100644 bindings/python/bindings-test/share/python-wheels/colorama-0.4.4-py2.py3-none-any.whl create mode 100644 bindings/python/bindings-test/share/python-wheels/contextlib2-0.6.0.post1-py2.py3-none-any.whl create mode 100644 bindings/python/bindings-test/share/python-wheels/distlib-0.3.1-py2.py3-none-any.whl create mode 100644 bindings/python/bindings-test/share/python-wheels/distro-1.5.0-py2.py3-none-any.whl create mode 100644 bindings/python/bindings-test/share/python-wheels/html5lib-1.1-py2.py3-none-any.whl create mode 100644 bindings/python/bindings-test/share/python-wheels/idna-2.10-py2.py3-none-any.whl create mode 100644 bindings/python/bindings-test/share/python-wheels/ipaddr-2.2.0-py2.py3-none-any.whl create mode 100644 bindings/python/bindings-test/share/python-wheels/msgpack-1.0.0-py2.py3-none-any.whl create mode 100644 bindings/python/bindings-test/share/python-wheels/packaging-20.9-py2.py3-none-any.whl create mode 100644 bindings/python/bindings-test/share/python-wheels/pep517-0.9.1-py2.py3-none-any.whl create mode 100644 bindings/python/bindings-test/share/python-wheels/pip-20.3.4-py2.py3-none-any.whl create mode 100644 bindings/python/bindings-test/share/python-wheels/pkg_resources-0.0.0-py2.py3-none-any.whl create mode 100644 bindings/python/bindings-test/share/python-wheels/progress-1.5-py2.py3-none-any.whl create mode 100644 bindings/python/bindings-test/share/python-wheels/pyparsing-2.4.7-py2.py3-none-any.whl create mode 100644 bindings/python/bindings-test/share/python-wheels/requests-2.25.1-py2.py3-none-any.whl create mode 100644 bindings/python/bindings-test/share/python-wheels/resolvelib-0.5.4-py2.py3-none-any.whl create mode 100644 bindings/python/bindings-test/share/python-wheels/retrying-1.3.3-py2.py3-none-any.whl create mode 100644 bindings/python/bindings-test/share/python-wheels/setuptools-44.1.1-py2.py3-none-any.whl create mode 100644 bindings/python/bindings-test/share/python-wheels/six-1.16.0-py2.py3-none-any.whl create mode 100644 bindings/python/bindings-test/share/python-wheels/toml-0.10.1-py2.py3-none-any.whl create mode 100644 bindings/python/bindings-test/share/python-wheels/urllib3-1.26.5-py2.py3-none-any.whl create mode 100644 bindings/python/bindings-test/share/python-wheels/webencodings-0.5.1-py2.py3-none-any.whl create mode 100644 bindings/python/bindings-test/share/python-wheels/wheel-0.34.2-py2.py3-none-any.whl create mode 100644 bindings/python/sample_rh850.py create mode 100644 bindings/ruby/unicorn_gem/lib/unicorn_engine/rh850_const.rb diff --git a/bindings/dotnet/UnicornManaged/Const/Common.fs b/bindings/dotnet/UnicornManaged/Const/Common.fs index 72b5cb3a2c..4e36345163 100644 --- a/bindings/dotnet/UnicornManaged/Const/Common.fs +++ b/bindings/dotnet/UnicornManaged/Const/Common.fs @@ -28,7 +28,8 @@ module Common = let UC_ARCH_RISCV = 8 let UC_ARCH_S390X = 9 let UC_ARCH_TRICORE = 10 - let UC_ARCH_MAX = 11 + let UC_ARCH_RH850 = 11 + let UC_ARCH_MAX = 12 let UC_MODE_LITTLE_ENDIAN = 0 let UC_MODE_BIG_ENDIAN = 1073741824 @@ -55,6 +56,7 @@ module Common = let UC_MODE_SPARC32 = 4 let UC_MODE_SPARC64 = 8 let UC_MODE_V9 = 16 + let UC_MODE_RH850 = 4 let UC_MODE_RISCV32 = 4 let UC_MODE_RISCV64 = 8 diff --git a/bindings/dotnet/UnicornManaged/Const/Rh850.fs b/bindings/dotnet/UnicornManaged/Const/Rh850.fs new file mode 100644 index 0000000000..6d05417eab --- /dev/null +++ b/bindings/dotnet/UnicornManaged/Const/Rh850.fs @@ -0,0 +1,99 @@ +// For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT + +namespace UnicornManaged.Const + +open System + +[] +module Rh850 = + let UC_RH850_SYSREG_SELID0 = 32 + let UC_RH850_SYSREG_SELID1 = 64 + let UC_RH850_SYSREG_SELID2 = 96 + let UC_RH850_SYSREG_SELID3 = 128 + let UC_RH850_SYSREG_SELID4 = 160 + let UC_RH850_SYSREG_SELID5 = 192 + let UC_RH850_SYSREG_SELID6 = 224 + let UC_RH850_SYSREG_SELID7 = 256 + + // RH850 global purpose registers + + let UC_RH850_REG_R0 = 0 + let UC_RH850_REG_R1 = 1 + let UC_RH850_REG_R2 = 2 + let UC_RH850_REG_R3 = 3 + let UC_RH850_REG_R4 = 4 + let UC_RH850_REG_R5 = 5 + let UC_RH850_REG_R6 = 6 + let UC_RH850_REG_R7 = 7 + let UC_RH850_REG_R8 = 8 + let UC_RH850_REG_R9 = 9 + let UC_RH850_REG_R10 = 10 + let UC_RH850_REG_R11 = 11 + let UC_RH850_REG_R12 = 12 + let UC_RH850_REG_R13 = 13 + let UC_RH850_REG_R14 = 14 + let UC_RH850_REG_R15 = 15 + let UC_RH850_REG_R16 = 16 + let UC_RH850_REG_R17 = 17 + let UC_RH850_REG_R18 = 18 + let UC_RH850_REG_R19 = 19 + let UC_RH850_REG_R20 = 20 + let UC_RH850_REG_R21 = 21 + let UC_RH850_REG_R22 = 22 + let UC_RH850_REG_R23 = 23 + let UC_RH850_REG_R24 = 24 + let UC_RH850_REG_R25 = 25 + let UC_RH850_REG_R26 = 26 + let UC_RH850_REG_R27 = 27 + let UC_RH850_REG_R28 = 28 + let UC_RH850_REG_R29 = 29 + let UC_RH850_REG_R30 = 30 + let UC_RH850_REG_R31 = 31 + + // RH850 system registers, selection ID 0 + let UC_RH850_REG_EIPC = 32 + let UC_RH850_REG_EIPSW = 33 + let UC_RH850_REG_FEPC = 34 + let UC_RH850_REG_FEPSW = 35 + let UC_RH850_REG_ECR = 36 + let UC_RH850_REG_PSW = 37 + let UC_RH850_REG_FPSR = 38 + let UC_RH850_REG_FPEPC = 39 + let UC_RH850_REG_FPST = 40 + let UC_RH850_REG_FPCC = 41 + let UC_RH850_REG_FPCFG = 42 + let UC_RH850_REG_FPEC = 43 + let UC_RH850_REG_EIIC = 45 + let UC_RH850_REG_FEIC = 46 + let UC_RH850_REG_CTPC = 48 + let UC_RH850_REG_CTPSW = 49 + let UC_RH850_REG_CTBP = 52 + let UC_RH850_REG_EIWR = 60 + let UC_RH850_REG_FEWR = 61 + let UC_RH850_REG_BSEL = 63 + + // RH850 system regusters, selection ID 1 + let UC_RH850_REG_MCFG0 = 64 + let UC_RH850_REG_RBASE = 65 + let UC_RH850_REG_EBASE = 66 + let UC_RH850_REG_INTBP = 67 + let UC_RH850_REG_MCTL = 68 + let UC_RH850_REG_PID = 69 + let UC_RH850_REG_SCCFG = 75 + let UC_RH850_REG_SCBP = 76 + + // RH850 system registers, selection ID 2 + let UC_RH850_REG_HTCFG0 = 96 + let UC_RH850_REG_MEA = 102 + let UC_RH850_REG_ASID = 103 + let UC_RH850_REG_MEI = 104 + let UC_RH850_REG_PC = 288 + let UC_RH850_REG_ENDING = 289 + + // RH8509 Registers aliases. + + let UC_RH850_REG_ZERO = 0 + let UC_RH850_REG_SP = 2 + let UC_RH850_REG_EP = 30 + let UC_RH850_REG_LP = 31 + diff --git a/bindings/go/unicorn/rh850_const.go b/bindings/go/unicorn/rh850_const.go new file mode 100644 index 0000000000..72a892f8a8 --- /dev/null +++ b/bindings/go/unicorn/rh850_const.go @@ -0,0 +1,94 @@ +package unicorn +// For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT [rh850_const.go] +const ( + RH850_SYSREG_SELID0 = 32 + RH850_SYSREG_SELID1 = 64 + RH850_SYSREG_SELID2 = 96 + RH850_SYSREG_SELID3 = 128 + RH850_SYSREG_SELID4 = 160 + RH850_SYSREG_SELID5 = 192 + RH850_SYSREG_SELID6 = 224 + RH850_SYSREG_SELID7 = 256 + +// RH850 global purpose registers + + RH850_REG_R0 = 0 + RH850_REG_R1 = 1 + RH850_REG_R2 = 2 + RH850_REG_R3 = 3 + RH850_REG_R4 = 4 + RH850_REG_R5 = 5 + RH850_REG_R6 = 6 + RH850_REG_R7 = 7 + RH850_REG_R8 = 8 + RH850_REG_R9 = 9 + RH850_REG_R10 = 10 + RH850_REG_R11 = 11 + RH850_REG_R12 = 12 + RH850_REG_R13 = 13 + RH850_REG_R14 = 14 + RH850_REG_R15 = 15 + RH850_REG_R16 = 16 + RH850_REG_R17 = 17 + RH850_REG_R18 = 18 + RH850_REG_R19 = 19 + RH850_REG_R20 = 20 + RH850_REG_R21 = 21 + RH850_REG_R22 = 22 + RH850_REG_R23 = 23 + RH850_REG_R24 = 24 + RH850_REG_R25 = 25 + RH850_REG_R26 = 26 + RH850_REG_R27 = 27 + RH850_REG_R28 = 28 + RH850_REG_R29 = 29 + RH850_REG_R30 = 30 + RH850_REG_R31 = 31 + +// RH850 system registers, selection ID 0 + RH850_REG_EIPC = 32 + RH850_REG_EIPSW = 33 + RH850_REG_FEPC = 34 + RH850_REG_FEPSW = 35 + RH850_REG_ECR = 36 + RH850_REG_PSW = 37 + RH850_REG_FPSR = 38 + RH850_REG_FPEPC = 39 + RH850_REG_FPST = 40 + RH850_REG_FPCC = 41 + RH850_REG_FPCFG = 42 + RH850_REG_FPEC = 43 + RH850_REG_EIIC = 45 + RH850_REG_FEIC = 46 + RH850_REG_CTPC = 48 + RH850_REG_CTPSW = 49 + RH850_REG_CTBP = 52 + RH850_REG_EIWR = 60 + RH850_REG_FEWR = 61 + RH850_REG_BSEL = 63 + +// RH850 system regusters, selection ID 1 + RH850_REG_MCFG0 = 64 + RH850_REG_RBASE = 65 + RH850_REG_EBASE = 66 + RH850_REG_INTBP = 67 + RH850_REG_MCTL = 68 + RH850_REG_PID = 69 + RH850_REG_SCCFG = 75 + RH850_REG_SCBP = 76 + +// RH850 system registers, selection ID 2 + RH850_REG_HTCFG0 = 96 + RH850_REG_MEA = 102 + RH850_REG_ASID = 103 + RH850_REG_MEI = 104 + RH850_REG_PC = 288 + RH850_REG_ENDING = 289 + +// RH8509 Registers aliases. + + RH850_REG_ZERO = 0 + RH850_REG_SP = 2 + RH850_REG_EP = 30 + RH850_REG_LP = 31 +) \ No newline at end of file diff --git a/bindings/go/unicorn/unicorn_const.go b/bindings/go/unicorn/unicorn_const.go index 19268ce565..f342162264 100644 --- a/bindings/go/unicorn/unicorn_const.go +++ b/bindings/go/unicorn/unicorn_const.go @@ -23,7 +23,8 @@ const ( ARCH_RISCV = 8 ARCH_S390X = 9 ARCH_TRICORE = 10 - ARCH_MAX = 11 + ARCH_RH850 = 11 + ARCH_MAX = 12 MODE_LITTLE_ENDIAN = 0 MODE_BIG_ENDIAN = 1073741824 @@ -50,6 +51,7 @@ const ( MODE_SPARC32 = 4 MODE_SPARC64 = 8 MODE_V9 = 16 + MODE_RH850 = 4 MODE_RISCV32 = 4 MODE_RISCV64 = 8 diff --git a/bindings/java/unicorn/Rh850Const.java b/bindings/java/unicorn/Rh850Const.java new file mode 100644 index 0000000000..3474253d05 --- /dev/null +++ b/bindings/java/unicorn/Rh850Const.java @@ -0,0 +1,97 @@ +// For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT + +package unicorn; + +public interface Rh850Const { + public static final int UC_RH850_SYSREG_SELID0 = 32; + public static final int UC_RH850_SYSREG_SELID1 = 64; + public static final int UC_RH850_SYSREG_SELID2 = 96; + public static final int UC_RH850_SYSREG_SELID3 = 128; + public static final int UC_RH850_SYSREG_SELID4 = 160; + public static final int UC_RH850_SYSREG_SELID5 = 192; + public static final int UC_RH850_SYSREG_SELID6 = 224; + public static final int UC_RH850_SYSREG_SELID7 = 256; + +// RH850 global purpose registers + + public static final int UC_RH850_REG_R0 = 0; + public static final int UC_RH850_REG_R1 = 1; + public static final int UC_RH850_REG_R2 = 2; + public static final int UC_RH850_REG_R3 = 3; + public static final int UC_RH850_REG_R4 = 4; + public static final int UC_RH850_REG_R5 = 5; + public static final int UC_RH850_REG_R6 = 6; + public static final int UC_RH850_REG_R7 = 7; + public static final int UC_RH850_REG_R8 = 8; + public static final int UC_RH850_REG_R9 = 9; + public static final int UC_RH850_REG_R10 = 10; + public static final int UC_RH850_REG_R11 = 11; + public static final int UC_RH850_REG_R12 = 12; + public static final int UC_RH850_REG_R13 = 13; + public static final int UC_RH850_REG_R14 = 14; + public static final int UC_RH850_REG_R15 = 15; + public static final int UC_RH850_REG_R16 = 16; + public static final int UC_RH850_REG_R17 = 17; + public static final int UC_RH850_REG_R18 = 18; + public static final int UC_RH850_REG_R19 = 19; + public static final int UC_RH850_REG_R20 = 20; + public static final int UC_RH850_REG_R21 = 21; + public static final int UC_RH850_REG_R22 = 22; + public static final int UC_RH850_REG_R23 = 23; + public static final int UC_RH850_REG_R24 = 24; + public static final int UC_RH850_REG_R25 = 25; + public static final int UC_RH850_REG_R26 = 26; + public static final int UC_RH850_REG_R27 = 27; + public static final int UC_RH850_REG_R28 = 28; + public static final int UC_RH850_REG_R29 = 29; + public static final int UC_RH850_REG_R30 = 30; + public static final int UC_RH850_REG_R31 = 31; + +// RH850 system registers, selection ID 0 + public static final int UC_RH850_REG_EIPC = 32; + public static final int UC_RH850_REG_EIPSW = 33; + public static final int UC_RH850_REG_FEPC = 34; + public static final int UC_RH850_REG_FEPSW = 35; + public static final int UC_RH850_REG_ECR = 36; + public static final int UC_RH850_REG_PSW = 37; + public static final int UC_RH850_REG_FPSR = 38; + public static final int UC_RH850_REG_FPEPC = 39; + public static final int UC_RH850_REG_FPST = 40; + public static final int UC_RH850_REG_FPCC = 41; + public static final int UC_RH850_REG_FPCFG = 42; + public static final int UC_RH850_REG_FPEC = 43; + public static final int UC_RH850_REG_EIIC = 45; + public static final int UC_RH850_REG_FEIC = 46; + public static final int UC_RH850_REG_CTPC = 48; + public static final int UC_RH850_REG_CTPSW = 49; + public static final int UC_RH850_REG_CTBP = 52; + public static final int UC_RH850_REG_EIWR = 60; + public static final int UC_RH850_REG_FEWR = 61; + public static final int UC_RH850_REG_BSEL = 63; + +// RH850 system regusters, selection ID 1 + public static final int UC_RH850_REG_MCFG0 = 64; + public static final int UC_RH850_REG_RBASE = 65; + public static final int UC_RH850_REG_EBASE = 66; + public static final int UC_RH850_REG_INTBP = 67; + public static final int UC_RH850_REG_MCTL = 68; + public static final int UC_RH850_REG_PID = 69; + public static final int UC_RH850_REG_SCCFG = 75; + public static final int UC_RH850_REG_SCBP = 76; + +// RH850 system registers, selection ID 2 + public static final int UC_RH850_REG_HTCFG0 = 96; + public static final int UC_RH850_REG_MEA = 102; + public static final int UC_RH850_REG_ASID = 103; + public static final int UC_RH850_REG_MEI = 104; + public static final int UC_RH850_REG_PC = 288; + public static final int UC_RH850_REG_ENDING = 289; + +// RH8509 Registers aliases. + + public static final int UC_RH850_REG_ZERO = 0; + public static final int UC_RH850_REG_SP = 2; + public static final int UC_RH850_REG_EP = 30; + public static final int UC_RH850_REG_LP = 31; + +} diff --git a/bindings/java/unicorn/UnicornConst.java b/bindings/java/unicorn/UnicornConst.java index 62759ba6fc..6957b06a6c 100644 --- a/bindings/java/unicorn/UnicornConst.java +++ b/bindings/java/unicorn/UnicornConst.java @@ -25,7 +25,8 @@ public interface UnicornConst { public static final int UC_ARCH_RISCV = 8; public static final int UC_ARCH_S390X = 9; public static final int UC_ARCH_TRICORE = 10; - public static final int UC_ARCH_MAX = 11; + public static final int UC_ARCH_RH850 = 11; + public static final int UC_ARCH_MAX = 12; public static final int UC_MODE_LITTLE_ENDIAN = 0; public static final int UC_MODE_BIG_ENDIAN = 1073741824; @@ -52,6 +53,7 @@ public interface UnicornConst { public static final int UC_MODE_SPARC32 = 4; public static final int UC_MODE_SPARC64 = 8; public static final int UC_MODE_V9 = 16; + public static final int UC_MODE_RH850 = 4; public static final int UC_MODE_RISCV32 = 4; public static final int UC_MODE_RISCV64 = 8; diff --git a/bindings/pascal/unicorn/Rh850Const.pas b/bindings/pascal/unicorn/Rh850Const.pas new file mode 100644 index 0000000000..31632ba8b0 --- /dev/null +++ b/bindings/pascal/unicorn/Rh850Const.pas @@ -0,0 +1,99 @@ +// For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT + +unit Rh850Const; + +interface + +const UC_RH850_SYSREG_SELID0 = 32; + UC_RH850_SYSREG_SELID1 = 64; + UC_RH850_SYSREG_SELID2 = 96; + UC_RH850_SYSREG_SELID3 = 128; + UC_RH850_SYSREG_SELID4 = 160; + UC_RH850_SYSREG_SELID5 = 192; + UC_RH850_SYSREG_SELID6 = 224; + UC_RH850_SYSREG_SELID7 = 256; + +// RH850 global purpose registers + + UC_RH850_REG_R0 = 0; + UC_RH850_REG_R1 = 1; + UC_RH850_REG_R2 = 2; + UC_RH850_REG_R3 = 3; + UC_RH850_REG_R4 = 4; + UC_RH850_REG_R5 = 5; + UC_RH850_REG_R6 = 6; + UC_RH850_REG_R7 = 7; + UC_RH850_REG_R8 = 8; + UC_RH850_REG_R9 = 9; + UC_RH850_REG_R10 = 10; + UC_RH850_REG_R11 = 11; + UC_RH850_REG_R12 = 12; + UC_RH850_REG_R13 = 13; + UC_RH850_REG_R14 = 14; + UC_RH850_REG_R15 = 15; + UC_RH850_REG_R16 = 16; + UC_RH850_REG_R17 = 17; + UC_RH850_REG_R18 = 18; + UC_RH850_REG_R19 = 19; + UC_RH850_REG_R20 = 20; + UC_RH850_REG_R21 = 21; + UC_RH850_REG_R22 = 22; + UC_RH850_REG_R23 = 23; + UC_RH850_REG_R24 = 24; + UC_RH850_REG_R25 = 25; + UC_RH850_REG_R26 = 26; + UC_RH850_REG_R27 = 27; + UC_RH850_REG_R28 = 28; + UC_RH850_REG_R29 = 29; + UC_RH850_REG_R30 = 30; + UC_RH850_REG_R31 = 31; + +// RH850 system registers, selection ID 0 + UC_RH850_REG_EIPC = 32; + UC_RH850_REG_EIPSW = 33; + UC_RH850_REG_FEPC = 34; + UC_RH850_REG_FEPSW = 35; + UC_RH850_REG_ECR = 36; + UC_RH850_REG_PSW = 37; + UC_RH850_REG_FPSR = 38; + UC_RH850_REG_FPEPC = 39; + UC_RH850_REG_FPST = 40; + UC_RH850_REG_FPCC = 41; + UC_RH850_REG_FPCFG = 42; + UC_RH850_REG_FPEC = 43; + UC_RH850_REG_EIIC = 45; + UC_RH850_REG_FEIC = 46; + UC_RH850_REG_CTPC = 48; + UC_RH850_REG_CTPSW = 49; + UC_RH850_REG_CTBP = 52; + UC_RH850_REG_EIWR = 60; + UC_RH850_REG_FEWR = 61; + UC_RH850_REG_BSEL = 63; + +// RH850 system regusters, selection ID 1 + UC_RH850_REG_MCFG0 = 64; + UC_RH850_REG_RBASE = 65; + UC_RH850_REG_EBASE = 66; + UC_RH850_REG_INTBP = 67; + UC_RH850_REG_MCTL = 68; + UC_RH850_REG_PID = 69; + UC_RH850_REG_SCCFG = 75; + UC_RH850_REG_SCBP = 76; + +// RH850 system registers, selection ID 2 + UC_RH850_REG_HTCFG0 = 96; + UC_RH850_REG_MEA = 102; + UC_RH850_REG_ASID = 103; + UC_RH850_REG_MEI = 104; + UC_RH850_REG_PC = 288; + UC_RH850_REG_ENDING = 289; + +// RH8509 Registers aliases. + + UC_RH850_REG_ZERO = 0; + UC_RH850_REG_SP = 2; + UC_RH850_REG_EP = 30; + UC_RH850_REG_LP = 31; + +implementation +end. \ No newline at end of file diff --git a/bindings/pascal/unicorn/UnicornConst.pas b/bindings/pascal/unicorn/UnicornConst.pas index 26896d130d..9f36d13714 100644 --- a/bindings/pascal/unicorn/UnicornConst.pas +++ b/bindings/pascal/unicorn/UnicornConst.pas @@ -26,7 +26,8 @@ interface UC_ARCH_RISCV = 8; UC_ARCH_S390X = 9; UC_ARCH_TRICORE = 10; - UC_ARCH_MAX = 11; + UC_ARCH_RH850 = 11; + UC_ARCH_MAX = 12; UC_MODE_LITTLE_ENDIAN = 0; UC_MODE_BIG_ENDIAN = 1073741824; @@ -53,6 +54,7 @@ interface UC_MODE_SPARC32 = 4; UC_MODE_SPARC64 = 8; UC_MODE_V9 = 16; + UC_MODE_RH850 = 4; UC_MODE_RISCV32 = 4; UC_MODE_RISCV64 = 8; diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/easy-install.pth b/bindings/python/bindings-test/lib/python3.9/site-packages/easy-install.pth new file mode 100644 index 0000000000..1841fa5fc2 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/easy-install.pth @@ -0,0 +1 @@ +./unicorn-2.0.1.post1-py3.9.egg diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/easy_install.py b/bindings/python/bindings-test/lib/python3.9/site-packages/easy_install.py new file mode 100644 index 0000000000..d87e984034 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/easy_install.py @@ -0,0 +1,5 @@ +"""Run the EasyInstall command""" + +if __name__ == '__main__': + from setuptools.command.easy_install import main + main() diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip-20.3.4.dist-info/INSTALLER b/bindings/python/bindings-test/lib/python3.9/site-packages/pip-20.3.4.dist-info/INSTALLER new file mode 100644 index 0000000000..a1b589e38a --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip-20.3.4.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip-20.3.4.dist-info/LICENSE.txt b/bindings/python/bindings-test/lib/python3.9/site-packages/pip-20.3.4.dist-info/LICENSE.txt new file mode 100644 index 0000000000..75eb0fd80b --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip-20.3.4.dist-info/LICENSE.txt @@ -0,0 +1,20 @@ +Copyright (c) 2008-2020 The pip developers (see AUTHORS.txt file) + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip-20.3.4.dist-info/METADATA b/bindings/python/bindings-test/lib/python3.9/site-packages/pip-20.3.4.dist-info/METADATA new file mode 100644 index 0000000000..f7e5e8117f --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip-20.3.4.dist-info/METADATA @@ -0,0 +1,94 @@ +Metadata-Version: 2.1 +Name: pip +Version: 20.3.4 +Summary: The PyPA recommended tool for installing Python packages. +Home-page: https://pip.pypa.io/ +Author: The pip developers +Author-email: distutils-sig@python.org +License: MIT +Project-URL: Documentation, https://pip.pypa.io +Project-URL: Source, https://github.com/pypa/pip +Project-URL: Changelog, https://pip.pypa.io/en/stable/news/ +Keywords: distutils easy_install egg setuptools wheel virtualenv +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Topic :: Software Development :: Build Tools +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Requires-Python: >=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.* + +pip - The Python Package Installer +================================== + +.. image:: https://img.shields.io/pypi/v/pip.svg + :target: https://pypi.org/project/pip/ + +.. image:: https://readthedocs.org/projects/pip/badge/?version=latest + :target: https://pip.pypa.io/en/latest + +pip is the `package installer`_ for Python. You can use pip to install packages from the `Python Package Index`_ and other indexes. + +Please take a look at our documentation for how to install and use pip: + +* `Installation`_ +* `Usage`_ + +We release updates regularly, with a new version every 3 months. Find more details in our documentation: + +* `Release notes`_ +* `Release process`_ + +In pip 20.3, we've `made a big improvement to the heart of pip`_; `learn more`_. We want your input, so `sign up for our user experience research studies`_ to help us do it right. + +**Note**: pip 21.0, in January 2021, will remove Python 2 support, per pip's `Python 2 support policy`_. Please migrate to Python 3. + +If you find bugs, need help, or want to talk to the developers, please use our mailing lists or chat rooms: + +* `Issue tracking`_ +* `Discourse channel`_ +* `User IRC`_ + +If you want to get involved head over to GitHub to get the source code, look at our development documentation and feel free to jump on the developer mailing lists and chat rooms: + +* `GitHub page`_ +* `Development documentation`_ +* `Development mailing list`_ +* `Development IRC`_ + +Code of Conduct +--------------- + +Everyone interacting in the pip project's codebases, issue trackers, chat +rooms, and mailing lists is expected to follow the `PSF Code of Conduct`_. + +.. _package installer: https://packaging.python.org/guides/tool-recommendations/ +.. _Python Package Index: https://pypi.org +.. _Installation: https://pip.pypa.io/en/stable/installing.html +.. _Usage: https://pip.pypa.io/en/stable/ +.. _Release notes: https://pip.pypa.io/en/stable/news.html +.. _Release process: https://pip.pypa.io/en/latest/development/release-process/ +.. _GitHub page: https://github.com/pypa/pip +.. _Development documentation: https://pip.pypa.io/en/latest/development +.. _made a big improvement to the heart of pip: https://pyfound.blogspot.com/2020/11/pip-20-3-new-resolver.html +.. _learn more: https://pip.pypa.io/en/latest/user_guide/#changes-to-the-pip-dependency-resolver-in-20-3-2020 +.. _sign up for our user experience research studies: https://pyfound.blogspot.com/2020/03/new-pip-resolver-to-roll-out-this-year.html +.. _Python 2 support policy: https://pip.pypa.io/en/latest/development/release-process/#python-2-support +.. _Issue tracking: https://github.com/pypa/pip/issues +.. _Discourse channel: https://discuss.python.org/c/packaging +.. _Development mailing list: https://mail.python.org/mailman3/lists/distutils-sig.python.org/ +.. _User IRC: https://webchat.freenode.net/?channels=%23pypa +.. _Development IRC: https://webchat.freenode.net/?channels=%23pypa-dev +.. _PSF Code of Conduct: https://github.com/pypa/.github/blob/main/CODE_OF_CONDUCT.md + + diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip-20.3.4.dist-info/RECORD b/bindings/python/bindings-test/lib/python3.9/site-packages/pip-20.3.4.dist-info/RECORD new file mode 100644 index 0000000000..eb387a65e0 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip-20.3.4.dist-info/RECORD @@ -0,0 +1,284 @@ +../../../bin/pip,sha256=cNTqymKR8XF4AYw7niXTMhd2so49N4nl2yV1-SmjBsQ,287 +../../../bin/pip3,sha256=cNTqymKR8XF4AYw7niXTMhd2so49N4nl2yV1-SmjBsQ,287 +../../../bin/pip3.9,sha256=cNTqymKR8XF4AYw7niXTMhd2so49N4nl2yV1-SmjBsQ,287 +pip-20.3.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +pip-20.3.4.dist-info/LICENSE.txt,sha256=gdAS_gPyTUkBTvvgoNNlG9Mv1KFDTig6W1JdeMD2Efg,1090 +pip-20.3.4.dist-info/METADATA,sha256=NrQymkcD8Kl04ckwQXiv2W-ZCeORlee9lZ0RlurDR-o,4304 +pip-20.3.4.dist-info/RECORD,, +pip-20.3.4.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pip-20.3.4.dist-info/WHEEL,sha256=kGT74LWyRUZrL4VgLh6_g12IeVl_9u9ZVhadrgXZUEY,110 +pip-20.3.4.dist-info/entry_points.txt,sha256=5ExSa1s54zSPNA_1epJn5SX06786S8k5YHwskMvVYzw,125 +pip-20.3.4.dist-info/top_level.txt,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +pip/__init__.py,sha256=dUUDS2wGpUgaxc_qko944J7JopSJIz53d0FM7Eml96k,455 +pip/__main__.py,sha256=bqCAM1cj1HwHCDx3WJa-LJxOBXimGxE8OjBqAvnhVg0,911 +pip/__pycache__/__init__.cpython-39.pyc,, +pip/__pycache__/__main__.cpython-39.pyc,, +pip/_internal/__init__.py,sha256=TeXyNeKLd7EETjf3lJAGSY1Db-dYA6a_xCLHWUkEmXA,495 +pip/_internal/__pycache__/__init__.cpython-39.pyc,, +pip/_internal/__pycache__/build_env.cpython-39.pyc,, +pip/_internal/__pycache__/cache.cpython-39.pyc,, +pip/_internal/__pycache__/configuration.cpython-39.pyc,, +pip/_internal/__pycache__/exceptions.cpython-39.pyc,, +pip/_internal/__pycache__/locations.cpython-39.pyc,, +pip/_internal/__pycache__/main.cpython-39.pyc,, +pip/_internal/__pycache__/pyproject.cpython-39.pyc,, +pip/_internal/__pycache__/self_outdated_check.cpython-39.pyc,, +pip/_internal/__pycache__/wheel_builder.cpython-39.pyc,, +pip/_internal/build_env.py,sha256=5PdJVlRvDe-fmGfc_wqOWtQ9Ad9gm2Elwfy2V5aVuio,8089 +pip/_internal/cache.py,sha256=HDTjGrm57Fl-vuojIcL17744KRCl66uuNXaAmwA8HLQ,12249 +pip/_internal/cli/__init__.py,sha256=FkHBgpxxb-_gd6r1FjnNhfMOzAUYyXoXKJ6abijfcFU,132 +pip/_internal/cli/__pycache__/__init__.cpython-39.pyc,, +pip/_internal/cli/__pycache__/autocompletion.cpython-39.pyc,, +pip/_internal/cli/__pycache__/base_command.cpython-39.pyc,, +pip/_internal/cli/__pycache__/cmdoptions.cpython-39.pyc,, +pip/_internal/cli/__pycache__/command_context.cpython-39.pyc,, +pip/_internal/cli/__pycache__/main.cpython-39.pyc,, +pip/_internal/cli/__pycache__/main_parser.cpython-39.pyc,, +pip/_internal/cli/__pycache__/parser.cpython-39.pyc,, +pip/_internal/cli/__pycache__/progress_bars.cpython-39.pyc,, +pip/_internal/cli/__pycache__/req_command.cpython-39.pyc,, +pip/_internal/cli/__pycache__/spinners.cpython-39.pyc,, +pip/_internal/cli/__pycache__/status_codes.cpython-39.pyc,, +pip/_internal/cli/autocompletion.py,sha256=ekGNtcDI0p7rFVc-7s4T9Tbss4Jgb7vsB649XJIblRg,6547 +pip/_internal/cli/base_command.py,sha256=duI7mshtryhdmzI9GeHGqssTZM4UkZW0IT5pX3SYqtA,9337 +pip/_internal/cli/cmdoptions.py,sha256=biNkTbqoY13QHo0BxnjndJFrQmzFSrEiR-5PKX30rWY,28617 +pip/_internal/cli/command_context.py,sha256=k1VHqTCeYjQ0b3tyqiUToA3An5FxpQmo5rb-9AHJ6VY,975 +pip/_internal/cli/main.py,sha256=Hxc9dZyW3xiDsYZX-_J2cGXT5DWNLNn_Y7o9oUme-Ec,2616 +pip/_internal/cli/main_parser.py,sha256=QSUbu5dPZ3pxsmChno8eH16kZxAcUkGy8YcCG_eeGrc,2830 +pip/_internal/cli/parser.py,sha256=ne2OH7B3xSeGPUelZkRQ38Tv9hqyl__sgyNiP3P55-U,10388 +pip/_internal/cli/progress_bars.py,sha256=J1zykt2LI4gbBeXorfYRmYV5FgXhcW4x3r6xE_a7Z7c,9121 +pip/_internal/cli/req_command.py,sha256=_WNGkkvnuP210DcZXWRUzJ8wMYNNQQ2Nw9mGOnHCHS4,16455 +pip/_internal/cli/spinners.py,sha256=GUQWNPnBD1CTRHxxumvUwodHovIvofMBu-bkaSaUnQY,5509 +pip/_internal/cli/status_codes.py,sha256=F6uDG6Gj7RNKQJUDnd87QKqI16Us-t-B0wPF_4QMpWc,156 +pip/_internal/commands/__init__.py,sha256=30max1NT-jWYrzAKwioPuUgD75EKubqLkBhHYmeZQH8,4101 +pip/_internal/commands/__pycache__/__init__.cpython-39.pyc,, +pip/_internal/commands/__pycache__/cache.cpython-39.pyc,, +pip/_internal/commands/__pycache__/check.cpython-39.pyc,, +pip/_internal/commands/__pycache__/completion.cpython-39.pyc,, +pip/_internal/commands/__pycache__/configuration.cpython-39.pyc,, +pip/_internal/commands/__pycache__/debug.cpython-39.pyc,, +pip/_internal/commands/__pycache__/download.cpython-39.pyc,, +pip/_internal/commands/__pycache__/freeze.cpython-39.pyc,, +pip/_internal/commands/__pycache__/hash.cpython-39.pyc,, +pip/_internal/commands/__pycache__/help.cpython-39.pyc,, +pip/_internal/commands/__pycache__/install.cpython-39.pyc,, +pip/_internal/commands/__pycache__/list.cpython-39.pyc,, +pip/_internal/commands/__pycache__/search.cpython-39.pyc,, +pip/_internal/commands/__pycache__/show.cpython-39.pyc,, +pip/_internal/commands/__pycache__/uninstall.cpython-39.pyc,, +pip/_internal/commands/__pycache__/wheel.cpython-39.pyc,, +pip/_internal/commands/cache.py,sha256=m7T9C6jB7fmNozyG24J1LkeTsoyfyIUYg_5otImUq64,7555 +pip/_internal/commands/check.py,sha256=NijmAIKljW3kY-V0QLMT7VttuEDtDroQa5qrfy4B-1I,1677 +pip/_internal/commands/completion.py,sha256=SFurXIoVZgXMhD-rPwyftjD2dtaOosIgBbHbCJ4Bnmo,3081 +pip/_internal/commands/configuration.py,sha256=i4uMbWcK-PW1VLY7f6eKklh7qO1Jnsvqvqe4cY6Uj4Y,9327 +pip/_internal/commands/debug.py,sha256=A54tXwZIEefYoOYlHvu-QbVyfNDqN00cHWkCe_6DdCU,8193 +pip/_internal/commands/download.py,sha256=NGk_sEGui-Id-1jki2FzbcTA4HZKEVbnImENnHGw8is,4919 +pip/_internal/commands/freeze.py,sha256=BcB1CYWMK95dE2SAkuk7aAhenv-pMVRfQQZ0_W8oKNc,3888 +pip/_internal/commands/hash.py,sha256=v2nYCiEsEI9nEam1p6GwdG8xyj5gFv-4WrqvNexKmeY,1843 +pip/_internal/commands/help.py,sha256=ofk4ez1AaR16kha-w4DLuWOi_B82wxU_2aT2VnHM8cg,1294 +pip/_internal/commands/install.py,sha256=L5depJz54VvwxCFV0_b9f1F_o4sKP9QR0dKIFH1ocL4,28449 +pip/_internal/commands/list.py,sha256=uM5Dvi9FIzT_QzLEIheQ-7C3vgOF3rh2B9CjCwPIHeY,11547 +pip/_internal/commands/search.py,sha256=YtTJydvon5CVZ5OYAvof495HyghFfMQkkUberJjYm1c,6033 +pip/_internal/commands/show.py,sha256=zk9FZqNPZ5Q4dGXnKrKdk3PaLPsWOHOwoFWGbMzhoKA,6996 +pip/_internal/commands/uninstall.py,sha256=Ys8hwFsg0kvvGtLGYG3ibL5BKvURhlSlCX50ZQ-hsHk,3311 +pip/_internal/commands/wheel.py,sha256=xvUMV9v_Qjwtip_4y1CWSTDVUdxa4dd4DY1PwtkXUxI,6802 +pip/_internal/configuration.py,sha256=B57qs7H0cGj8OPHQ8feeAzF8q333Wbdgd63pp1CtScM,13904 +pip/_internal/distributions/__init__.py,sha256=ECBUW5Gtu9TjJwyFLvim-i6kUMYVuikNh9I5asL6tbA,959 +pip/_internal/distributions/__pycache__/__init__.cpython-39.pyc,, +pip/_internal/distributions/__pycache__/base.cpython-39.pyc,, +pip/_internal/distributions/__pycache__/installed.cpython-39.pyc,, +pip/_internal/distributions/__pycache__/sdist.cpython-39.pyc,, +pip/_internal/distributions/__pycache__/wheel.cpython-39.pyc,, +pip/_internal/distributions/base.py,sha256=rGDUfzALQQN-9vkrcbCl7bhGMQbQ-BdHLWW6xWJObQs,1426 +pip/_internal/distributions/installed.py,sha256=aUtTvTcnVQSEts20D0Z0ifHnfT-fwMA-SXoqAq5pR58,761 +pip/_internal/distributions/sdist.py,sha256=UvAp42AhjJwa0x-QM72GptF5k_Y7KXhEjm0owTrskG4,4087 +pip/_internal/distributions/wheel.py,sha256=lePMBDS_ptPq1NI7n-GQYbFdDn8RdCbXoZ1PagrqvW8,1295 +pip/_internal/exceptions.py,sha256=8_7M9CgtGmTHHwgvpT8Mg8iDli7DfIMoIDfIvpdXUSY,13003 +pip/_internal/index/__init__.py,sha256=vpt-JeTZefh8a-FC22ZeBSXFVbuBcXSGiILhQZJaNpQ,30 +pip/_internal/index/__pycache__/__init__.cpython-39.pyc,, +pip/_internal/index/__pycache__/collector.cpython-39.pyc,, +pip/_internal/index/__pycache__/package_finder.cpython-39.pyc,, +pip/_internal/index/collector.py,sha256=gZ_9wP_AmiIS8TVlpzHOKZvQsZAXUwCmC4Tg12Uz7LE,22070 +pip/_internal/index/package_finder.py,sha256=l8bLOqUbTZuqt9js7lzqTTWfKzwErOsXiYE3tfJF0Mk,37454 +pip/_internal/locations.py,sha256=MEkFeloQEtkH2EgMbpAI2wHFDXVFV299pw_b1nCGYIM,6870 +pip/_internal/main.py,sha256=LqoUFbyaZAZ1wZ0xSZ6wIIx9-m1JoSnSDztWnjR_pMo,437 +pip/_internal/models/__init__.py,sha256=3DHUd_qxpPozfzouoqa9g9ts1Czr5qaHfFxbnxriepM,63 +pip/_internal/models/__pycache__/__init__.cpython-39.pyc,, +pip/_internal/models/__pycache__/candidate.cpython-39.pyc,, +pip/_internal/models/__pycache__/direct_url.cpython-39.pyc,, +pip/_internal/models/__pycache__/format_control.cpython-39.pyc,, +pip/_internal/models/__pycache__/index.cpython-39.pyc,, +pip/_internal/models/__pycache__/link.cpython-39.pyc,, +pip/_internal/models/__pycache__/scheme.cpython-39.pyc,, +pip/_internal/models/__pycache__/search_scope.cpython-39.pyc,, +pip/_internal/models/__pycache__/selection_prefs.cpython-39.pyc,, +pip/_internal/models/__pycache__/target_python.cpython-39.pyc,, +pip/_internal/models/__pycache__/wheel.cpython-39.pyc,, +pip/_internal/models/candidate.py,sha256=GmprVP8YD1kXg4VlREolYjC_fqwLl7LfeCN-ZBSNNig,1196 +pip/_internal/models/direct_url.py,sha256=ZE07jfJmU_AlLgYOkuFup7kgsZP5k8BRewB8YXp50mc,6884 +pip/_internal/models/format_control.py,sha256=YFi9CrJrfpEkuS2DOCtrWqYudrho1BHaBSwT8KexxH8,2823 +pip/_internal/models/index.py,sha256=carvxxaT7mJyoEkptaECHUZiNaA6R5NrsGF55zawNn8,1161 +pip/_internal/models/link.py,sha256=BywYuw790dC1zvSFij8-Cm4QZfmUcupe6xSAmk3i8CM,7471 +pip/_internal/models/scheme.py,sha256=EhPkT_6G0Md84JTLSVopYsp5H_K6BREYmFvU8H6wMK8,778 +pip/_internal/models/search_scope.py,sha256=Lum0mY4_pdR9DDBy6HV5xHGIMPp_kU8vMsqYKFHZip4,4751 +pip/_internal/models/selection_prefs.py,sha256=1lS2d6nbrMrjWgRuwdl05tnGChjtDijKjG4XCbnuLmc,2045 +pip/_internal/models/target_python.py,sha256=PK8GMs15pSUGCG18RgTGmvxvYE8-M5WKnudl4CikTYM,4070 +pip/_internal/models/wheel.py,sha256=FTfzVb4WIbfIehxhdlAVvCil_MQ0-W44oyN56cE6NHc,2772 +pip/_internal/network/__init__.py,sha256=jf6Tt5nV_7zkARBrKojIXItgejvoegVJVKUbhAa5Ioc,50 +pip/_internal/network/__pycache__/__init__.cpython-39.pyc,, +pip/_internal/network/__pycache__/auth.cpython-39.pyc,, +pip/_internal/network/__pycache__/cache.cpython-39.pyc,, +pip/_internal/network/__pycache__/download.cpython-39.pyc,, +pip/_internal/network/__pycache__/lazy_wheel.cpython-39.pyc,, +pip/_internal/network/__pycache__/session.cpython-39.pyc,, +pip/_internal/network/__pycache__/utils.cpython-39.pyc,, +pip/_internal/network/__pycache__/xmlrpc.cpython-39.pyc,, +pip/_internal/network/auth.py,sha256=ntH7kjy1f6OI0O8s8RncqhyjwiiNkMChJVFB9PInP08,11652 +pip/_internal/network/cache.py,sha256=6rpBfrrzr9SaBy7_AM1EUH1pSFYq1pXCftMqk-1kkQQ,2329 +pip/_internal/network/download.py,sha256=mcmjWRKFOwdL6niizxm0ACv9tdf06TOYBK_xY4l_3c4,6401 +pip/_internal/network/lazy_wheel.py,sha256=o8DD4VooJvZJ2SfBsZDI4i85eONCITQKLydfklNroh0,8121 +pip/_internal/network/session.py,sha256=doOFU1lep6MjHBS_H1AVmRzcwEs7zcXbJtsfu7Xcgy0,15449 +pip/_internal/network/utils.py,sha256=ZPHg7u6DEcg2EvILIdPECnvPLp21OPHxNVmeXfMy-n0,4172 +pip/_internal/network/xmlrpc.py,sha256=4GnaQBJBKycuyWStRYUi93kmv70XootLfxOymAsP4SM,1883 +pip/_internal/operations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pip/_internal/operations/__pycache__/__init__.cpython-39.pyc,, +pip/_internal/operations/__pycache__/check.cpython-39.pyc,, +pip/_internal/operations/__pycache__/freeze.cpython-39.pyc,, +pip/_internal/operations/__pycache__/prepare.cpython-39.pyc,, +pip/_internal/operations/build/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pip/_internal/operations/build/__pycache__/__init__.cpython-39.pyc,, +pip/_internal/operations/build/__pycache__/metadata.cpython-39.pyc,, +pip/_internal/operations/build/__pycache__/metadata_legacy.cpython-39.pyc,, +pip/_internal/operations/build/__pycache__/wheel.cpython-39.pyc,, +pip/_internal/operations/build/__pycache__/wheel_legacy.cpython-39.pyc,, +pip/_internal/operations/build/metadata.py,sha256=lXcRhnnN2-f49dYBNf1_NLkHZ-s-4OPV7tCOyJJmZ94,1255 +pip/_internal/operations/build/metadata_legacy.py,sha256=VgzBTk8naIO8-8N_ifEYF7ZAxWUDhphWVIaVlZ2FqYM,2011 +pip/_internal/operations/build/wheel.py,sha256=Ya0i8_uzfssdN2vorOVzNJYbAYVTLUnSZimCFdP4F7w,1466 +pip/_internal/operations/build/wheel_legacy.py,sha256=9CnTpc25Agvl9MnMgrVnHUWTlJ3um8aV4m9dbGdGHi0,3347 +pip/_internal/operations/check.py,sha256=EPNWcQyUSc3_pa_6Npv_mI5sXZ5zqRrmk0M67YViDIY,5216 +pip/_internal/operations/freeze.py,sha256=35mnNtUYhwYb_Lioo1RxHEgD7Eqm3KUqOOJ6RQQT_7Y,10411 +pip/_internal/operations/install/__init__.py,sha256=mX7hyD2GNBO2mFGokDQ30r_GXv7Y_PLdtxcUv144e-s,51 +pip/_internal/operations/install/__pycache__/__init__.cpython-39.pyc,, +pip/_internal/operations/install/__pycache__/editable_legacy.cpython-39.pyc,, +pip/_internal/operations/install/__pycache__/legacy.cpython-39.pyc,, +pip/_internal/operations/install/__pycache__/wheel.cpython-39.pyc,, +pip/_internal/operations/install/editable_legacy.py,sha256=rJ_xs2qtDUjpY2-n6eYlVyZiNoKbOtZXZrYrcnIELt4,1488 +pip/_internal/operations/install/legacy.py,sha256=zu3Gw54dgHtluyW5n8j5qKcAScidQXJvqB8fb0oLB-4,4281 +pip/_internal/operations/install/wheel.py,sha256=ENg_QbLbBnwYiPt1lzFIrQGu2QhkECxKm9_dTaaz5TU,31247 +pip/_internal/operations/prepare.py,sha256=-MKVSMKGYpqJ0y6fa1gq3eDvSKhR0ZLXZVlzaC_TVNo,22460 +pip/_internal/pyproject.py,sha256=DoQzvtOh5_wCPpU8E-J3IDCOKHvJw_SIY_gI8ih4I58,7400 +pip/_internal/req/__init__.py,sha256=s-E5Vxxqqpcs7xfY5gY69oHogsWJ4sLbnUiDoWmkHOU,3133 +pip/_internal/req/__pycache__/__init__.cpython-39.pyc,, +pip/_internal/req/__pycache__/constructors.cpython-39.pyc,, +pip/_internal/req/__pycache__/req_file.cpython-39.pyc,, +pip/_internal/req/__pycache__/req_install.cpython-39.pyc,, +pip/_internal/req/__pycache__/req_set.cpython-39.pyc,, +pip/_internal/req/__pycache__/req_tracker.cpython-39.pyc,, +pip/_internal/req/__pycache__/req_uninstall.cpython-39.pyc,, +pip/_internal/req/constructors.py,sha256=0pLw8q5kozJyAUfFNCHGC3Y1acQV7FxuD6f-fVmrOMo,16135 +pip/_internal/req/req_file.py,sha256=f62QFxszUwN1q14Z_YZ3GdYm8mUCe2WoD0r8sDebQoE,18594 +pip/_internal/req/req_install.py,sha256=1d1QqpMV-xgGkNTF4j1guPNDV4_SxPbaFrvAvQthcrk,33803 +pip/_internal/req/req_set.py,sha256=csA7N4VelGpf-ovyFQRaxR9XTeAk2j9kiZHO6SIDxW0,7887 +pip/_internal/req/req_tracker.py,sha256=fVl3Pgl3yl12rFBQICYpy3StxWxD3j5pDWrHo8QmP7g,4691 +pip/_internal/req/req_uninstall.py,sha256=vuT3vX3zab3d8Gh-p1AgoDhpKU1P3OVyuC8a_57Es4U,23771 +pip/_internal/resolution/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pip/_internal/resolution/__pycache__/__init__.cpython-39.pyc,, +pip/_internal/resolution/__pycache__/base.cpython-39.pyc,, +pip/_internal/resolution/base.py,sha256=MemTQyKXiVrtdxsGzuI7QqBd7Ek0wNHvCoe3ZLZO4_A,683 +pip/_internal/resolution/legacy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pip/_internal/resolution/legacy/__pycache__/__init__.cpython-39.pyc,, +pip/_internal/resolution/legacy/__pycache__/resolver.cpython-39.pyc,, +pip/_internal/resolution/legacy/resolver.py,sha256=4aLvLZt0_BPHLaROEl9IjEhza9CZia8PLHlvZfMUMoQ,18234 +pip/_internal/resolution/resolvelib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pip/_internal/resolution/resolvelib/__pycache__/__init__.cpython-39.pyc,, +pip/_internal/resolution/resolvelib/__pycache__/base.cpython-39.pyc,, +pip/_internal/resolution/resolvelib/__pycache__/candidates.cpython-39.pyc,, +pip/_internal/resolution/resolvelib/__pycache__/factory.cpython-39.pyc,, +pip/_internal/resolution/resolvelib/__pycache__/found_candidates.cpython-39.pyc,, +pip/_internal/resolution/resolvelib/__pycache__/provider.cpython-39.pyc,, +pip/_internal/resolution/resolvelib/__pycache__/reporter.cpython-39.pyc,, +pip/_internal/resolution/resolvelib/__pycache__/requirements.cpython-39.pyc,, +pip/_internal/resolution/resolvelib/__pycache__/resolver.cpython-39.pyc,, +pip/_internal/resolution/resolvelib/base.py,sha256=Kw8tB9Q7bYlJPIZmR4bGGRruk4SU9io5dnzshRVRvI4,5061 +pip/_internal/resolution/resolvelib/candidates.py,sha256=WNQUFRfJOokXCdpbTMadg9KcL2PXmhwnoXMwD6-E2Vo,20083 +pip/_internal/resolution/resolvelib/factory.py,sha256=ovOeMdAC2vhsZZLZbMqnisZaoBDlF8d64fJdpVCSMto,18946 +pip/_internal/resolution/resolvelib/found_candidates.py,sha256=_o9lCikecM_61kgl5xCKAByv_haREFlrc1qRoyVB9ts,3773 +pip/_internal/resolution/resolvelib/provider.py,sha256=bFS1-xUV9Pz1DefrDNfFaSlBjM785ftzoJi_fXbzdig,7339 +pip/_internal/resolution/resolvelib/reporter.py,sha256=dw4K2w0m7HEgxFF3r60voTrFDDPyhBLN8rzw4cQXaoo,2857 +pip/_internal/resolution/resolvelib/requirements.py,sha256=sps2y82iZtBdjPHZ16Ej9A4KdI7_8YC9R9nhAo1abyA,5969 +pip/_internal/resolution/resolvelib/resolver.py,sha256=ZHXXMwLkSUXfs0LG7gVK1IuIqNZlORNrwXjBmBVmDN4,11707 +pip/_internal/self_outdated_check.py,sha256=cVPuBaP89nm8Qdf_vVdXZxwtt8ebm4tL8fcStPl3dU8,6745 +pip/_internal/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pip/_internal/utils/__pycache__/__init__.cpython-39.pyc,, +pip/_internal/utils/__pycache__/appdirs.cpython-39.pyc,, +pip/_internal/utils/__pycache__/compat.cpython-39.pyc,, +pip/_internal/utils/__pycache__/compatibility_tags.cpython-39.pyc,, +pip/_internal/utils/__pycache__/datetime.cpython-39.pyc,, +pip/_internal/utils/__pycache__/deprecation.cpython-39.pyc,, +pip/_internal/utils/__pycache__/direct_url_helpers.cpython-39.pyc,, +pip/_internal/utils/__pycache__/distutils_args.cpython-39.pyc,, +pip/_internal/utils/__pycache__/encoding.cpython-39.pyc,, +pip/_internal/utils/__pycache__/entrypoints.cpython-39.pyc,, +pip/_internal/utils/__pycache__/filesystem.cpython-39.pyc,, +pip/_internal/utils/__pycache__/filetypes.cpython-39.pyc,, +pip/_internal/utils/__pycache__/glibc.cpython-39.pyc,, +pip/_internal/utils/__pycache__/hashes.cpython-39.pyc,, +pip/_internal/utils/__pycache__/inject_securetransport.cpython-39.pyc,, +pip/_internal/utils/__pycache__/logging.cpython-39.pyc,, +pip/_internal/utils/__pycache__/misc.cpython-39.pyc,, +pip/_internal/utils/__pycache__/models.cpython-39.pyc,, +pip/_internal/utils/__pycache__/packaging.cpython-39.pyc,, +pip/_internal/utils/__pycache__/parallel.cpython-39.pyc,, +pip/_internal/utils/__pycache__/pkg_resources.cpython-39.pyc,, +pip/_internal/utils/__pycache__/setuptools_build.cpython-39.pyc,, +pip/_internal/utils/__pycache__/subprocess.cpython-39.pyc,, +pip/_internal/utils/__pycache__/temp_dir.cpython-39.pyc,, +pip/_internal/utils/__pycache__/typing.cpython-39.pyc,, +pip/_internal/utils/__pycache__/unpacking.cpython-39.pyc,, +pip/_internal/utils/__pycache__/urls.cpython-39.pyc,, +pip/_internal/utils/__pycache__/virtualenv.cpython-39.pyc,, +pip/_internal/utils/__pycache__/wheel.cpython-39.pyc,, +pip/_internal/utils/appdirs.py,sha256=RZzUG-Bkh2b-miX0DSZ3v703_-bgK-v0PfWCCjwVE9g,1349 +pip/_internal/utils/compat.py,sha256=JoSVxgMmV8ZZTwXrPRGgQk1EyomJZM3gb-nolCxslko,9489 +pip/_internal/utils/compatibility_tags.py,sha256=2frtUos4dHeHKV38noN_rs_u8VTehy4eMxqyEYVtZtY,5690 +pip/_internal/utils/datetime.py,sha256=KL-vIdGU9JIpGB5NYkmwXWkH-G_2mvvABlmRtoSZsao,295 +pip/_internal/utils/deprecation.py,sha256=pBnNogoA4UGTxa_JDnPXBRRYpKMbExAhXpBwAwklOBs,3318 +pip/_internal/utils/direct_url_helpers.py,sha256=Q0c-z0iuQx_D1FeRlu7nZD5h2nt4QSow23B26PQrp0s,4146 +pip/_internal/utils/distutils_args.py,sha256=a56mblNxk9BGifbpEETG61mmBrqhjtjRkJ4HYn-oOEE,1350 +pip/_internal/utils/encoding.py,sha256=53p3H36wc49dyr0EgtBbdHdvH4Dr-Egl0zc_J0sweqc,1284 +pip/_internal/utils/entrypoints.py,sha256=yvizXdrIeK44OI-J2YBIcojfrXxGO9oe8JCxBvMdxIk,1152 +pip/_internal/utils/filesystem.py,sha256=-fU3XteCAIJwf_9FvCZU7vhywvt3nuf_cqkCdwgy1Y8,6943 +pip/_internal/utils/filetypes.py,sha256=QvagL0Vm4tMZ_qyFqopZWpaDHEM3Q6FyF35vfOY-CJg,847 +pip/_internal/utils/glibc.py,sha256=LOeNGgawCKS-4ke9fii78fwXD73dtNav3uxz1Bf-Ab8,3297 +pip/_internal/utils/hashes.py,sha256=ydFGVhDk0Nj2JyaTKzUHRe5iBnbgh4KG-HFtXbr_xmo,5297 +pip/_internal/utils/inject_securetransport.py,sha256=M17ZlFVY66ApgeASVjKKLKNz0LAfk-SyU0HZ4ZB6MmI,810 +pip/_internal/utils/logging.py,sha256=YIfuDUEkmdn9cIRQ_Ec8rgXs1m5nOwDECtZqM4CBH5U,13093 +pip/_internal/utils/misc.py,sha256=BpnBnDkypGrWZHIBFU0g9IYsO6rglX8sQVnUselvY-8,28698 +pip/_internal/utils/models.py,sha256=HqiBVtTbW_b_Umvj2fjhDWOHo2RKhPwSz4iAYkQZ688,1201 +pip/_internal/utils/packaging.py,sha256=KOLx30EXZobHKTaA8khLNqEMb986DeaCcgDhZHaw6RY,3036 +pip/_internal/utils/parallel.py,sha256=d6wJWWHnPOcwO4pyL7pv08DG3l_5YtHzIBdhHhI3epw,3404 +pip/_internal/utils/pkg_resources.py,sha256=ZX-k7V5q_aNWyDse92nN7orN1aCpRLsaxzpkBZ1XKzU,1254 +pip/_internal/utils/setuptools_build.py,sha256=E1KswI7wfNnCDE5R6G8c9ZbByENpu7NqocjY26PCQDw,5058 +pip/_internal/utils/subprocess.py,sha256=nihl4bmnTpU4wQPjJESYNdTrS2-5T1SC00kM2JZx2gI,10866 +pip/_internal/utils/temp_dir.py,sha256=cmFpYI_5VDeXUsGvia9jUNh8XEKXYvpGlIi_iq2MRVU,8845 +pip/_internal/utils/typing.py,sha256=xkYwOeHlf4zsHXBDC4310HtEqwhQcYXFPq2h35Tcrl0,1401 +pip/_internal/utils/unpacking.py,sha256=YFAckhqqvmehA8Kan5vd3b1kN_9TafqmOk4b-yz4fho,9488 +pip/_internal/utils/urls.py,sha256=q2rw1kMiiig_XZcoyJSsWMJQqYw-2wUmrMoST4mCW_I,1527 +pip/_internal/utils/virtualenv.py,sha256=fNGrRp-8QmNL5OzXajBd-z7PbwOsx1XY6G-AVMAhfso,3706 +pip/_internal/utils/wheel.py,sha256=wFzn3h8GqYvgsyWPZtUyn0Rb3MJzmtyP3owMOhKnmL0,7303 +pip/_internal/vcs/__init__.py,sha256=viJxJRqRE_mVScum85bgQIXAd6o0ozFt18VpC-qIJrM,617 +pip/_internal/vcs/__pycache__/__init__.cpython-39.pyc,, +pip/_internal/vcs/__pycache__/bazaar.cpython-39.pyc,, +pip/_internal/vcs/__pycache__/git.cpython-39.pyc,, +pip/_internal/vcs/__pycache__/mercurial.cpython-39.pyc,, +pip/_internal/vcs/__pycache__/subversion.cpython-39.pyc,, +pip/_internal/vcs/__pycache__/versioncontrol.cpython-39.pyc,, +pip/_internal/vcs/bazaar.py,sha256=ivvSGrrYbryp7TU9Vn8hkJddcYCRfvKzqPx05o_G71k,4016 +pip/_internal/vcs/git.py,sha256=_RePK_kSTbjzsQCn5Dv-alIxoLPJu2GkhFjehd20bpY,15893 +pip/_internal/vcs/mercurial.py,sha256=EcevyquppjAS-y1CrDxcFPwAid0d9gEGhxVp_DUJSZ0,5564 +pip/_internal/vcs/subversion.py,sha256=OajehInnYm8T9BVD-yeaIjN5hG_F9ZSJqFlt5OZqEuk,12558 +pip/_internal/vcs/versioncontrol.py,sha256=_FmtOI-jxJl5vthUgedDgNJG-OHQK_6SLoEIvTNIES4,23767 +pip/_internal/wheel_builder.py,sha256=krzl8rpbRqlXDb3W---hqTPn09msEprUmpqpG2Bcys4,11889 +pip/_vendor/__init__.py,sha256=9W5OMec7OR5iGiLkewOfrMJ9Wt-FjLAezVSYzwHc2ds,5156 +pip/_vendor/__pycache__/__init__.cpython-39.pyc,, +pip/_vendor/vendor.txt,sha256=B9Th9JlPs1TDBKZkMFiB54aghp1RFZHuJ5djqKyl6a0,437 diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip-20.3.4.dist-info/REQUESTED b/bindings/python/bindings-test/lib/python3.9/site-packages/pip-20.3.4.dist-info/REQUESTED new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip-20.3.4.dist-info/WHEEL b/bindings/python/bindings-test/lib/python3.9/site-packages/pip-20.3.4.dist-info/WHEEL new file mode 100644 index 0000000000..ef99c6cf32 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip-20.3.4.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.34.2) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip-20.3.4.dist-info/entry_points.txt b/bindings/python/bindings-test/lib/python3.9/site-packages/pip-20.3.4.dist-info/entry_points.txt new file mode 100644 index 0000000000..9609f72c54 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip-20.3.4.dist-info/entry_points.txt @@ -0,0 +1,5 @@ +[console_scripts] +pip = pip._internal.cli.main:main +pip3 = pip._internal.cli.main:main +pip3.9 = pip._internal.cli.main:main + diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip-20.3.4.dist-info/top_level.txt b/bindings/python/bindings-test/lib/python3.9/site-packages/pip-20.3.4.dist-info/top_level.txt new file mode 100644 index 0000000000..a1b589e38a --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip-20.3.4.dist-info/top_level.txt @@ -0,0 +1 @@ +pip diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/__init__.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/__init__.py new file mode 100644 index 0000000000..f9bd0632fe --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/__init__.py @@ -0,0 +1,18 @@ +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List, Optional + + +__version__ = "20.3.4" + + +def main(args=None): + # type: (Optional[List[str]]) -> int + """This is an internal API only meant for use by pip's own console scripts. + + For additional details, see https://github.com/pypa/pip/issues/7498. + """ + from pip._internal.utils.entrypoints import _wrapper + + return _wrapper(args) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/__main__.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/__main__.py new file mode 100644 index 0000000000..7c2505fa5b --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/__main__.py @@ -0,0 +1,26 @@ +from __future__ import absolute_import + +import os +import sys + +# Remove '' and current working directory from the first entry +# of sys.path, if present to avoid using current directory +# in pip commands check, freeze, install, list and show, +# when invoked as python -m pip +if sys.path[0] in ('', os.getcwd()): + sys.path.pop(0) + +# If we are running from a wheel, add the wheel to sys.path +# This allows the usage python pip-*.whl/pip install pip-*.whl +if __package__ == '': + # __file__ is pip-*.whl/pip/__main__.py + # first dirname call strips of '/__main__.py', second strips off '/pip' + # Resulting path is the name of the wheel itself + # Add that to sys.path so we can import pip + path = os.path.dirname(os.path.dirname(__file__)) + sys.path.insert(0, path) + +from pip._internal.cli.main import main as _main # isort:skip # noqa + +if __name__ == '__main__': + sys.exit(_main()) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/__init__.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/__init__.py new file mode 100644 index 0000000000..a778e99488 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/__init__.py @@ -0,0 +1,17 @@ +import pip._internal.utils.inject_securetransport # noqa +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List, Optional + + +def main(args=None): + # type: (Optional[List[str]]) -> int + """This is preserved for old console scripts that may still be referencing + it. + + For additional details, see https://github.com/pypa/pip/issues/7498. + """ + from pip._internal.utils.entrypoints import _wrapper + + return _wrapper(args) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cache.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cache.py new file mode 100644 index 0000000000..def8dd64a1 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cache.py @@ -0,0 +1,346 @@ +"""Cache Management +""" + +import hashlib +import json +import logging +import os + +from pip._vendor.packaging.tags import interpreter_name, interpreter_version +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.exceptions import InvalidWheelFilename +from pip._internal.models.link import Link +from pip._internal.models.wheel import Wheel +from pip._internal.utils.temp_dir import TempDirectory, tempdir_kinds +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.urls import path_to_url + +if MYPY_CHECK_RUNNING: + from typing import Any, Dict, List, Optional, Set + + from pip._vendor.packaging.tags import Tag + + from pip._internal.models.format_control import FormatControl + +logger = logging.getLogger(__name__) + + +def _hash_dict(d): + # type: (Dict[str, str]) -> str + """Return a stable sha224 of a dictionary.""" + s = json.dumps(d, sort_keys=True, separators=(",", ":"), ensure_ascii=True) + return hashlib.sha224(s.encode("ascii")).hexdigest() + + +class Cache(object): + """An abstract class - provides cache directories for data from links + + + :param cache_dir: The root of the cache. + :param format_control: An object of FormatControl class to limit + binaries being read from the cache. + :param allowed_formats: which formats of files the cache should store. + ('binary' and 'source' are the only allowed values) + """ + + def __init__(self, cache_dir, format_control, allowed_formats): + # type: (str, FormatControl, Set[str]) -> None + super(Cache, self).__init__() + assert not cache_dir or os.path.isabs(cache_dir) + self.cache_dir = cache_dir or None + self.format_control = format_control + self.allowed_formats = allowed_formats + + _valid_formats = {"source", "binary"} + assert self.allowed_formats.union(_valid_formats) == _valid_formats + + def _get_cache_path_parts_legacy(self, link): + # type: (Link) -> List[str] + """Get parts of part that must be os.path.joined with cache_dir + + Legacy cache key (pip < 20) for compatibility with older caches. + """ + + # We want to generate an url to use as our cache key, we don't want to + # just re-use the URL because it might have other items in the fragment + # and we don't care about those. + key_parts = [link.url_without_fragment] + if link.hash_name is not None and link.hash is not None: + key_parts.append("=".join([link.hash_name, link.hash])) + key_url = "#".join(key_parts) + + # Encode our key url with sha224, we'll use this because it has similar + # security properties to sha256, but with a shorter total output (and + # thus less secure). However the differences don't make a lot of + # difference for our use case here. + hashed = hashlib.sha224(key_url.encode()).hexdigest() + + # We want to nest the directories some to prevent having a ton of top + # level directories where we might run out of sub directories on some + # FS. + parts = [hashed[:2], hashed[2:4], hashed[4:6], hashed[6:]] + + return parts + + def _get_cache_path_parts(self, link): + # type: (Link) -> List[str] + """Get parts of part that must be os.path.joined with cache_dir + """ + + # We want to generate an url to use as our cache key, we don't want to + # just re-use the URL because it might have other items in the fragment + # and we don't care about those. + key_parts = {"url": link.url_without_fragment} + if link.hash_name is not None and link.hash is not None: + key_parts[link.hash_name] = link.hash + if link.subdirectory_fragment: + key_parts["subdirectory"] = link.subdirectory_fragment + + # Include interpreter name, major and minor version in cache key + # to cope with ill-behaved sdists that build a different wheel + # depending on the python version their setup.py is being run on, + # and don't encode the difference in compatibility tags. + # https://github.com/pypa/pip/issues/7296 + key_parts["interpreter_name"] = interpreter_name() + key_parts["interpreter_version"] = interpreter_version() + + # Encode our key url with sha224, we'll use this because it has similar + # security properties to sha256, but with a shorter total output (and + # thus less secure). However the differences don't make a lot of + # difference for our use case here. + hashed = _hash_dict(key_parts) + + # We want to nest the directories some to prevent having a ton of top + # level directories where we might run out of sub directories on some + # FS. + parts = [hashed[:2], hashed[2:4], hashed[4:6], hashed[6:]] + + return parts + + def _get_candidates(self, link, canonical_package_name): + # type: (Link, str) -> List[Any] + can_not_cache = ( + not self.cache_dir or + not canonical_package_name or + not link + ) + if can_not_cache: + return [] + + formats = self.format_control.get_allowed_formats( + canonical_package_name + ) + if not self.allowed_formats.intersection(formats): + return [] + + candidates = [] + path = self.get_path_for_link(link) + if os.path.isdir(path): + for candidate in os.listdir(path): + candidates.append((candidate, path)) + # TODO remove legacy path lookup in pip>=21 + legacy_path = self.get_path_for_link_legacy(link) + if os.path.isdir(legacy_path): + for candidate in os.listdir(legacy_path): + candidates.append((candidate, legacy_path)) + return candidates + + def get_path_for_link_legacy(self, link): + # type: (Link) -> str + raise NotImplementedError() + + def get_path_for_link(self, link): + # type: (Link) -> str + """Return a directory to store cached items in for link. + """ + raise NotImplementedError() + + def get( + self, + link, # type: Link + package_name, # type: Optional[str] + supported_tags, # type: List[Tag] + ): + # type: (...) -> Link + """Returns a link to a cached item if it exists, otherwise returns the + passed link. + """ + raise NotImplementedError() + + +class SimpleWheelCache(Cache): + """A cache of wheels for future installs. + """ + + def __init__(self, cache_dir, format_control): + # type: (str, FormatControl) -> None + super(SimpleWheelCache, self).__init__( + cache_dir, format_control, {"binary"} + ) + + def get_path_for_link_legacy(self, link): + # type: (Link) -> str + parts = self._get_cache_path_parts_legacy(link) + assert self.cache_dir + return os.path.join(self.cache_dir, "wheels", *parts) + + def get_path_for_link(self, link): + # type: (Link) -> str + """Return a directory to store cached wheels for link + + Because there are M wheels for any one sdist, we provide a directory + to cache them in, and then consult that directory when looking up + cache hits. + + We only insert things into the cache if they have plausible version + numbers, so that we don't contaminate the cache with things that were + not unique. E.g. ./package might have dozens of installs done for it + and build a version of 0.0...and if we built and cached a wheel, we'd + end up using the same wheel even if the source has been edited. + + :param link: The link of the sdist for which this will cache wheels. + """ + parts = self._get_cache_path_parts(link) + assert self.cache_dir + # Store wheels within the root cache_dir + return os.path.join(self.cache_dir, "wheels", *parts) + + def get( + self, + link, # type: Link + package_name, # type: Optional[str] + supported_tags, # type: List[Tag] + ): + # type: (...) -> Link + candidates = [] + + if not package_name: + return link + + canonical_package_name = canonicalize_name(package_name) + for wheel_name, wheel_dir in self._get_candidates( + link, canonical_package_name + ): + try: + wheel = Wheel(wheel_name) + except InvalidWheelFilename: + continue + if canonicalize_name(wheel.name) != canonical_package_name: + logger.debug( + "Ignoring cached wheel %s for %s as it " + "does not match the expected distribution name %s.", + wheel_name, link, package_name, + ) + continue + if not wheel.supported(supported_tags): + # Built for a different python/arch/etc + continue + candidates.append( + ( + wheel.support_index_min(supported_tags), + wheel_name, + wheel_dir, + ) + ) + + if not candidates: + return link + + _, wheel_name, wheel_dir = min(candidates) + return Link(path_to_url(os.path.join(wheel_dir, wheel_name))) + + +class EphemWheelCache(SimpleWheelCache): + """A SimpleWheelCache that creates it's own temporary cache directory + """ + + def __init__(self, format_control): + # type: (FormatControl) -> None + self._temp_dir = TempDirectory( + kind=tempdir_kinds.EPHEM_WHEEL_CACHE, + globally_managed=True, + ) + + super(EphemWheelCache, self).__init__( + self._temp_dir.path, format_control + ) + + +class CacheEntry(object): + def __init__( + self, + link, # type: Link + persistent, # type: bool + ): + self.link = link + self.persistent = persistent + + +class WheelCache(Cache): + """Wraps EphemWheelCache and SimpleWheelCache into a single Cache + + This Cache allows for gracefully degradation, using the ephem wheel cache + when a certain link is not found in the simple wheel cache first. + """ + + def __init__(self, cache_dir, format_control): + # type: (str, FormatControl) -> None + super(WheelCache, self).__init__( + cache_dir, format_control, {'binary'} + ) + self._wheel_cache = SimpleWheelCache(cache_dir, format_control) + self._ephem_cache = EphemWheelCache(format_control) + + def get_path_for_link_legacy(self, link): + # type: (Link) -> str + return self._wheel_cache.get_path_for_link_legacy(link) + + def get_path_for_link(self, link): + # type: (Link) -> str + return self._wheel_cache.get_path_for_link(link) + + def get_ephem_path_for_link(self, link): + # type: (Link) -> str + return self._ephem_cache.get_path_for_link(link) + + def get( + self, + link, # type: Link + package_name, # type: Optional[str] + supported_tags, # type: List[Tag] + ): + # type: (...) -> Link + cache_entry = self.get_cache_entry(link, package_name, supported_tags) + if cache_entry is None: + return link + return cache_entry.link + + def get_cache_entry( + self, + link, # type: Link + package_name, # type: Optional[str] + supported_tags, # type: List[Tag] + ): + # type: (...) -> Optional[CacheEntry] + """Returns a CacheEntry with a link to a cached item if it exists or + None. The cache entry indicates if the item was found in the persistent + or ephemeral cache. + """ + retval = self._wheel_cache.get( + link=link, + package_name=package_name, + supported_tags=supported_tags, + ) + if retval is not link: + return CacheEntry(retval, persistent=True) + + retval = self._ephem_cache.get( + link=link, + package_name=package_name, + supported_tags=supported_tags, + ) + if retval is not link: + return CacheEntry(retval, persistent=False) + + return None diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/__init__.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/__init__.py new file mode 100644 index 0000000000..e589bb917e --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/__init__.py @@ -0,0 +1,4 @@ +"""Subpackage containing all of pip's command line interface related code +""" + +# This file intentionally does not import submodules diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/autocompletion.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/autocompletion.py new file mode 100644 index 0000000000..329de60251 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/autocompletion.py @@ -0,0 +1,164 @@ +"""Logic that powers autocompletion installed by ``pip completion``. +""" + +import optparse +import os +import sys +from itertools import chain + +from pip._internal.cli.main_parser import create_main_parser +from pip._internal.commands import commands_dict, create_command +from pip._internal.utils.misc import get_installed_distributions +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Any, Iterable, List, Optional + + +def autocomplete(): + # type: () -> None + """Entry Point for completion of main and subcommand options. + """ + # Don't complete if user hasn't sourced bash_completion file. + if 'PIP_AUTO_COMPLETE' not in os.environ: + return + cwords = os.environ['COMP_WORDS'].split()[1:] + cword = int(os.environ['COMP_CWORD']) + try: + current = cwords[cword - 1] + except IndexError: + current = '' + + parser = create_main_parser() + subcommands = list(commands_dict) + options = [] + + # subcommand + subcommand_name = None # type: Optional[str] + for word in cwords: + if word in subcommands: + subcommand_name = word + break + # subcommand options + if subcommand_name is not None: + # special case: 'help' subcommand has no options + if subcommand_name == 'help': + sys.exit(1) + # special case: list locally installed dists for show and uninstall + should_list_installed = ( + subcommand_name in ['show', 'uninstall'] and + not current.startswith('-') + ) + if should_list_installed: + installed = [] + lc = current.lower() + for dist in get_installed_distributions(local_only=True): + if dist.key.startswith(lc) and dist.key not in cwords[1:]: + installed.append(dist.key) + # if there are no dists installed, fall back to option completion + if installed: + for dist in installed: + print(dist) + sys.exit(1) + + subcommand = create_command(subcommand_name) + + for opt in subcommand.parser.option_list_all: + if opt.help != optparse.SUPPRESS_HELP: + for opt_str in opt._long_opts + opt._short_opts: + options.append((opt_str, opt.nargs)) + + # filter out previously specified options from available options + prev_opts = [x.split('=')[0] for x in cwords[1:cword - 1]] + options = [(x, v) for (x, v) in options if x not in prev_opts] + # filter options by current input + options = [(k, v) for k, v in options if k.startswith(current)] + # get completion type given cwords and available subcommand options + completion_type = get_path_completion_type( + cwords, cword, subcommand.parser.option_list_all, + ) + # get completion files and directories if ``completion_type`` is + # ````, ```` or ```` + if completion_type: + paths = auto_complete_paths(current, completion_type) + options = [(path, 0) for path in paths] + for option in options: + opt_label = option[0] + # append '=' to options which require args + if option[1] and option[0][:2] == "--": + opt_label += '=' + print(opt_label) + else: + # show main parser options only when necessary + + opts = [i.option_list for i in parser.option_groups] + opts.append(parser.option_list) + flattened_opts = chain.from_iterable(opts) + if current.startswith('-'): + for opt in flattened_opts: + if opt.help != optparse.SUPPRESS_HELP: + subcommands += opt._long_opts + opt._short_opts + else: + # get completion type given cwords and all available options + completion_type = get_path_completion_type(cwords, cword, + flattened_opts) + if completion_type: + subcommands = list(auto_complete_paths(current, + completion_type)) + + print(' '.join([x for x in subcommands if x.startswith(current)])) + sys.exit(1) + + +def get_path_completion_type(cwords, cword, opts): + # type: (List[str], int, Iterable[Any]) -> Optional[str] + """Get the type of path completion (``file``, ``dir``, ``path`` or None) + + :param cwords: same as the environmental variable ``COMP_WORDS`` + :param cword: same as the environmental variable ``COMP_CWORD`` + :param opts: The available options to check + :return: path completion type (``file``, ``dir``, ``path`` or None) + """ + if cword < 2 or not cwords[cword - 2].startswith('-'): + return None + for opt in opts: + if opt.help == optparse.SUPPRESS_HELP: + continue + for o in str(opt).split('/'): + if cwords[cword - 2].split('=')[0] == o: + if not opt.metavar or any( + x in ('path', 'file', 'dir') + for x in opt.metavar.split('/')): + return opt.metavar + return None + + +def auto_complete_paths(current, completion_type): + # type: (str, str) -> Iterable[str] + """If ``completion_type`` is ``file`` or ``path``, list all regular files + and directories starting with ``current``; otherwise only list directories + starting with ``current``. + + :param current: The word to be completed + :param completion_type: path completion type(`file`, `path` or `dir`)i + :return: A generator of regular files and/or directories + """ + directory, filename = os.path.split(current) + current_path = os.path.abspath(directory) + # Don't complete paths if they can't be accessed + if not os.access(current_path, os.R_OK): + return + filename = os.path.normcase(filename) + # list all files that start with ``filename`` + file_list = (x for x in os.listdir(current_path) + if os.path.normcase(x).startswith(filename)) + for f in file_list: + opt = os.path.join(current_path, f) + comp_file = os.path.normcase(os.path.join(directory, f)) + # complete regular files when there is not ```` after option + # complete directories when there is ````, ```` or + # ````after option + if completion_type != 'dir' and os.path.isfile(opt): + yield comp_file + elif os.path.isdir(opt): + yield os.path.join(comp_file, '') diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/base_command.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/base_command.py new file mode 100644 index 0000000000..41e7dcf101 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/base_command.py @@ -0,0 +1,260 @@ +"""Base Command class, and related routines""" + +from __future__ import absolute_import, print_function + +import logging +import logging.config +import optparse +import os +import platform +import sys +import traceback + +from pip._vendor.six import PY2 + +from pip._internal.cli import cmdoptions +from pip._internal.cli.command_context import CommandContextMixIn +from pip._internal.cli.parser import ConfigOptionParser, UpdatingDefaultsHelpFormatter +from pip._internal.cli.status_codes import ( + ERROR, + PREVIOUS_BUILD_DIR_ERROR, + UNKNOWN_ERROR, + VIRTUALENV_NOT_FOUND, +) +from pip._internal.exceptions import ( + BadCommand, + CommandError, + InstallationError, + NetworkConnectionError, + PreviousBuildDirError, + UninstallationError, +) +from pip._internal.utils.deprecation import deprecated +from pip._internal.utils.filesystem import check_path_owner +from pip._internal.utils.logging import BrokenStdoutLoggingError, setup_logging +from pip._internal.utils.misc import get_prog, normalize_path +from pip._internal.utils.temp_dir import global_tempdir_manager, tempdir_registry +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.virtualenv import running_under_virtualenv + +if MYPY_CHECK_RUNNING: + from optparse import Values + from typing import Any, List, Optional, Tuple + + from pip._internal.utils.temp_dir import ( + TempDirectoryTypeRegistry as TempDirRegistry, + ) + +__all__ = ['Command'] + +logger = logging.getLogger(__name__) + + +class Command(CommandContextMixIn): + usage = None # type: str + ignore_require_venv = False # type: bool + + def __init__(self, name, summary, isolated=False): + # type: (str, str, bool) -> None + super(Command, self).__init__() + parser_kw = { + 'usage': self.usage, + 'prog': '{} {}'.format(get_prog(), name), + 'formatter': UpdatingDefaultsHelpFormatter(), + 'add_help_option': False, + 'name': name, + 'description': self.__doc__, + 'isolated': isolated, + } + + self.name = name + self.summary = summary + self.parser = ConfigOptionParser(**parser_kw) + + self.tempdir_registry = None # type: Optional[TempDirRegistry] + + # Commands should add options to this option group + optgroup_name = '{} Options'.format(self.name.capitalize()) + self.cmd_opts = optparse.OptionGroup(self.parser, optgroup_name) + + # Add the general options + gen_opts = cmdoptions.make_option_group( + cmdoptions.general_group, + self.parser, + ) + self.parser.add_option_group(gen_opts) + + self.add_options() + + def add_options(self): + # type: () -> None + pass + + def handle_pip_version_check(self, options): + # type: (Values) -> None + """ + This is a no-op so that commands by default do not do the pip version + check. + """ + # Make sure we do the pip version check if the index_group options + # are present. + assert not hasattr(options, 'no_index') + + def run(self, options, args): + # type: (Values, List[Any]) -> int + raise NotImplementedError + + def parse_args(self, args): + # type: (List[str]) -> Tuple[Any, Any] + # factored out for testability + return self.parser.parse_args(args) + + def main(self, args): + # type: (List[str]) -> int + try: + with self.main_context(): + return self._main(args) + finally: + logging.shutdown() + + def _main(self, args): + # type: (List[str]) -> int + # We must initialize this before the tempdir manager, otherwise the + # configuration would not be accessible by the time we clean up the + # tempdir manager. + self.tempdir_registry = self.enter_context(tempdir_registry()) + # Intentionally set as early as possible so globally-managed temporary + # directories are available to the rest of the code. + self.enter_context(global_tempdir_manager()) + + options, args = self.parse_args(args) + + # Set verbosity so that it can be used elsewhere. + self.verbosity = options.verbose - options.quiet + + level_number = setup_logging( + verbosity=self.verbosity, + no_color=options.no_color, + user_log_file=options.log, + ) + + if ( + sys.version_info[:2] == (2, 7) and + not options.no_python_version_warning + ): + message = ( + "pip 21.0 will drop support for Python 2.7 in January 2021. " + "More details about Python 2 support in pip can be found at " + "https://pip.pypa.io/en/latest/development/release-process/#python-2-support" # noqa + ) + if platform.python_implementation() == "CPython": + message = ( + "Python 2.7 reached the end of its life on January " + "1st, 2020. Please upgrade your Python as Python 2.7 " + "is no longer maintained. " + ) + message + deprecated(message, replacement=None, gone_in="21.0") + + if ( + sys.version_info[:2] == (3, 5) and + not options.no_python_version_warning + ): + message = ( + "Python 3.5 reached the end of its life on September " + "13th, 2020. Please upgrade your Python as Python 3.5 " + "is no longer maintained. pip 21.0 will drop support " + "for Python 3.5 in January 2021." + ) + deprecated(message, replacement=None, gone_in="21.0") + + # TODO: Try to get these passing down from the command? + # without resorting to os.environ to hold these. + # This also affects isolated builds and it should. + + if options.no_input: + os.environ['PIP_NO_INPUT'] = '1' + + if options.exists_action: + os.environ['PIP_EXISTS_ACTION'] = ' '.join(options.exists_action) + + if options.require_venv and not self.ignore_require_venv: + # If a venv is required check if it can really be found + if not running_under_virtualenv(): + logger.critical( + 'Could not find an activated virtualenv (required).' + ) + sys.exit(VIRTUALENV_NOT_FOUND) + + if options.cache_dir: + options.cache_dir = normalize_path(options.cache_dir) + if not check_path_owner(options.cache_dir): + logger.warning( + "The directory '%s' or its parent directory is not owned " + "or is not writable by the current user. The cache " + "has been disabled. Check the permissions and owner of " + "that directory. If executing pip with sudo, you may want " + "sudo's -H flag.", + options.cache_dir, + ) + options.cache_dir = None + + if getattr(options, "build_dir", None): + deprecated( + reason=( + "The -b/--build/--build-dir/--build-directory " + "option is deprecated and has no effect anymore." + ), + replacement=( + "use the TMPDIR/TEMP/TMP environment variable, " + "possibly combined with --no-clean" + ), + gone_in="21.1", + issue=8333, + ) + + if '2020-resolver' in options.features_enabled and not PY2: + logger.warning( + "--use-feature=2020-resolver no longer has any effect, " + "since it is now the default dependency resolver in pip. " + "This will become an error in pip 21.0." + ) + + try: + status = self.run(options, args) + assert isinstance(status, int) + return status + except PreviousBuildDirError as exc: + logger.critical(str(exc)) + logger.debug('Exception information:', exc_info=True) + + return PREVIOUS_BUILD_DIR_ERROR + except (InstallationError, UninstallationError, BadCommand, + NetworkConnectionError) as exc: + logger.critical(str(exc)) + logger.debug('Exception information:', exc_info=True) + + return ERROR + except CommandError as exc: + logger.critical('%s', exc) + logger.debug('Exception information:', exc_info=True) + + return ERROR + except BrokenStdoutLoggingError: + # Bypass our logger and write any remaining messages to stderr + # because stdout no longer works. + print('ERROR: Pipe to stdout was broken', file=sys.stderr) + if level_number <= logging.DEBUG: + traceback.print_exc(file=sys.stderr) + + return ERROR + except KeyboardInterrupt: + logger.critical('Operation cancelled by user') + logger.debug('Exception information:', exc_info=True) + + return ERROR + except BaseException: + logger.critical('Exception:', exc_info=True) + + return UNKNOWN_ERROR + finally: + self.handle_pip_version_check(options) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/cmdoptions.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/cmdoptions.py new file mode 100644 index 0000000000..d4af37baa0 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/cmdoptions.py @@ -0,0 +1,971 @@ +""" +shared options and groups + +The principle here is to define options once, but *not* instantiate them +globally. One reason being that options with action='append' can carry state +between parses. pip parses general options twice internally, and shouldn't +pass on state. To be consistent, all options will follow this design. +""" + +# The following comment should be removed at some point in the future. +# mypy: strict-optional=False + +from __future__ import absolute_import + +import os +import textwrap +import warnings +from distutils.util import strtobool +from functools import partial +from optparse import SUPPRESS_HELP, Option, OptionGroup +from textwrap import dedent + +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.cli.progress_bars import BAR_TYPES +from pip._internal.exceptions import CommandError +from pip._internal.locations import USER_CACHE_DIR, get_src_prefix +from pip._internal.models.format_control import FormatControl +from pip._internal.models.index import PyPI +from pip._internal.models.target_python import TargetPython +from pip._internal.utils.hashes import STRONG_HASHES +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from optparse import OptionParser, Values + from typing import Any, Callable, Dict, Optional, Tuple + + from pip._internal.cli.parser import ConfigOptionParser + + +def raise_option_error(parser, option, msg): + # type: (OptionParser, Option, str) -> None + """ + Raise an option parsing error using parser.error(). + + Args: + parser: an OptionParser instance. + option: an Option instance. + msg: the error text. + """ + msg = '{} error: {}'.format(option, msg) + msg = textwrap.fill(' '.join(msg.split())) + parser.error(msg) + + +def make_option_group(group, parser): + # type: (Dict[str, Any], ConfigOptionParser) -> OptionGroup + """ + Return an OptionGroup object + group -- assumed to be dict with 'name' and 'options' keys + parser -- an optparse Parser + """ + option_group = OptionGroup(parser, group['name']) + for option in group['options']: + option_group.add_option(option()) + return option_group + + +def check_install_build_global(options, check_options=None): + # type: (Values, Optional[Values]) -> None + """Disable wheels if per-setup.py call options are set. + + :param options: The OptionParser options to update. + :param check_options: The options to check, if not supplied defaults to + options. + """ + if check_options is None: + check_options = options + + def getname(n): + # type: (str) -> Optional[Any] + return getattr(check_options, n, None) + names = ["build_options", "global_options", "install_options"] + if any(map(getname, names)): + control = options.format_control + control.disallow_binaries() + warnings.warn( + 'Disabling all use of wheels due to the use of --build-option ' + '/ --global-option / --install-option.', stacklevel=2, + ) + + +def check_dist_restriction(options, check_target=False): + # type: (Values, bool) -> None + """Function for determining if custom platform options are allowed. + + :param options: The OptionParser options. + :param check_target: Whether or not to check if --target is being used. + """ + dist_restriction_set = any([ + options.python_version, + options.platforms, + options.abis, + options.implementation, + ]) + + binary_only = FormatControl(set(), {':all:'}) + sdist_dependencies_allowed = ( + options.format_control != binary_only and + not options.ignore_dependencies + ) + + # Installations or downloads using dist restrictions must not combine + # source distributions and dist-specific wheels, as they are not + # guaranteed to be locally compatible. + if dist_restriction_set and sdist_dependencies_allowed: + raise CommandError( + "When restricting platform and interpreter constraints using " + "--python-version, --platform, --abi, or --implementation, " + "either --no-deps must be set, or --only-binary=:all: must be " + "set and --no-binary must not be set (or must be set to " + ":none:)." + ) + + if check_target: + if dist_restriction_set and not options.target_dir: + raise CommandError( + "Can not use any platform or abi specific options unless " + "installing via '--target'" + ) + + +def _path_option_check(option, opt, value): + # type: (Option, str, str) -> str + return os.path.expanduser(value) + + +def _package_name_option_check(option, opt, value): + # type: (Option, str, str) -> str + return canonicalize_name(value) + + +class PipOption(Option): + TYPES = Option.TYPES + ("path", "package_name") + TYPE_CHECKER = Option.TYPE_CHECKER.copy() + TYPE_CHECKER["package_name"] = _package_name_option_check + TYPE_CHECKER["path"] = _path_option_check + + +########### +# options # +########### + +help_ = partial( + Option, + '-h', '--help', + dest='help', + action='help', + help='Show help.', +) # type: Callable[..., Option] + +isolated_mode = partial( + Option, + "--isolated", + dest="isolated_mode", + action="store_true", + default=False, + help=( + "Run pip in an isolated mode, ignoring environment variables and user " + "configuration." + ), +) # type: Callable[..., Option] + +require_virtualenv = partial( + Option, + # Run only if inside a virtualenv, bail if not. + '--require-virtualenv', '--require-venv', + dest='require_venv', + action='store_true', + default=False, + help=SUPPRESS_HELP +) # type: Callable[..., Option] + +verbose = partial( + Option, + '-v', '--verbose', + dest='verbose', + action='count', + default=0, + help='Give more output. Option is additive, and can be used up to 3 times.' +) # type: Callable[..., Option] + +no_color = partial( + Option, + '--no-color', + dest='no_color', + action='store_true', + default=False, + help="Suppress colored output.", +) # type: Callable[..., Option] + +version = partial( + Option, + '-V', '--version', + dest='version', + action='store_true', + help='Show version and exit.', +) # type: Callable[..., Option] + +quiet = partial( + Option, + '-q', '--quiet', + dest='quiet', + action='count', + default=0, + help=( + 'Give less output. Option is additive, and can be used up to 3' + ' times (corresponding to WARNING, ERROR, and CRITICAL logging' + ' levels).' + ), +) # type: Callable[..., Option] + +progress_bar = partial( + Option, + '--progress-bar', + dest='progress_bar', + type='choice', + choices=list(BAR_TYPES.keys()), + default='on', + help=( + 'Specify type of progress to be displayed [' + + '|'.join(BAR_TYPES.keys()) + '] (default: %default)' + ), +) # type: Callable[..., Option] + +log = partial( + PipOption, + "--log", "--log-file", "--local-log", + dest="log", + metavar="path", + type="path", + help="Path to a verbose appending log." +) # type: Callable[..., Option] + +no_input = partial( + Option, + # Don't ask for input + '--no-input', + dest='no_input', + action='store_true', + default=False, + help="Disable prompting for input." +) # type: Callable[..., Option] + +proxy = partial( + Option, + '--proxy', + dest='proxy', + type='str', + default='', + help="Specify a proxy in the form [user:passwd@]proxy.server:port." +) # type: Callable[..., Option] + +retries = partial( + Option, + '--retries', + dest='retries', + type='int', + default=5, + help="Maximum number of retries each connection should attempt " + "(default %default times).", +) # type: Callable[..., Option] + +timeout = partial( + Option, + '--timeout', '--default-timeout', + metavar='sec', + dest='timeout', + type='float', + default=15, + help='Set the socket timeout (default %default seconds).', +) # type: Callable[..., Option] + + +def exists_action(): + # type: () -> Option + return Option( + # Option when path already exist + '--exists-action', + dest='exists_action', + type='choice', + choices=['s', 'i', 'w', 'b', 'a'], + default=[], + action='append', + metavar='action', + help="Default action when a path already exists: " + "(s)witch, (i)gnore, (w)ipe, (b)ackup, (a)bort.", + ) + + +cert = partial( + PipOption, + '--cert', + dest='cert', + type='path', + metavar='path', + help="Path to alternate CA bundle.", +) # type: Callable[..., Option] + +client_cert = partial( + PipOption, + '--client-cert', + dest='client_cert', + type='path', + default=None, + metavar='path', + help="Path to SSL client certificate, a single file containing the " + "private key and the certificate in PEM format.", +) # type: Callable[..., Option] + +index_url = partial( + Option, + '-i', '--index-url', '--pypi-url', + dest='index_url', + metavar='URL', + default=PyPI.simple_url, + help="Base URL of the Python Package Index (default %default). " + "This should point to a repository compliant with PEP 503 " + "(the simple repository API) or a local directory laid out " + "in the same format.", +) # type: Callable[..., Option] + + +def extra_index_url(): + # type: () -> Option + return Option( + '--extra-index-url', + dest='extra_index_urls', + metavar='URL', + action='append', + default=[], + help="Extra URLs of package indexes to use in addition to " + "--index-url. Should follow the same rules as " + "--index-url.", + ) + + +no_index = partial( + Option, + '--no-index', + dest='no_index', + action='store_true', + default=False, + help='Ignore package index (only looking at --find-links URLs instead).', +) # type: Callable[..., Option] + + +def find_links(): + # type: () -> Option + return Option( + '-f', '--find-links', + dest='find_links', + action='append', + default=[], + metavar='url', + help="If a URL or path to an html file, then parse for links to " + "archives such as sdist (.tar.gz) or wheel (.whl) files. " + "If a local path or file:// URL that's a directory, " + "then look for archives in the directory listing. " + "Links to VCS project URLs are not supported.", + ) + + +def trusted_host(): + # type: () -> Option + return Option( + "--trusted-host", + dest="trusted_hosts", + action="append", + metavar="HOSTNAME", + default=[], + help="Mark this host or host:port pair as trusted, even though it " + "does not have valid or any HTTPS.", + ) + + +def constraints(): + # type: () -> Option + return Option( + '-c', '--constraint', + dest='constraints', + action='append', + default=[], + metavar='file', + help='Constrain versions using the given constraints file. ' + 'This option can be used multiple times.' + ) + + +def requirements(): + # type: () -> Option + return Option( + '-r', '--requirement', + dest='requirements', + action='append', + default=[], + metavar='file', + help='Install from the given requirements file. ' + 'This option can be used multiple times.' + ) + + +def editable(): + # type: () -> Option + return Option( + '-e', '--editable', + dest='editables', + action='append', + default=[], + metavar='path/url', + help=('Install a project in editable mode (i.e. setuptools ' + '"develop mode") from a local project path or a VCS url.'), + ) + + +def _handle_src(option, opt_str, value, parser): + # type: (Option, str, str, OptionParser) -> None + value = os.path.abspath(value) + setattr(parser.values, option.dest, value) + + +src = partial( + PipOption, + '--src', '--source', '--source-dir', '--source-directory', + dest='src_dir', + type='path', + metavar='dir', + default=get_src_prefix(), + action='callback', + callback=_handle_src, + help='Directory to check out editable projects into. ' + 'The default in a virtualenv is "/src". ' + 'The default for global installs is "/src".' +) # type: Callable[..., Option] + + +def _get_format_control(values, option): + # type: (Values, Option) -> Any + """Get a format_control object.""" + return getattr(values, option.dest) + + +def _handle_no_binary(option, opt_str, value, parser): + # type: (Option, str, str, OptionParser) -> None + existing = _get_format_control(parser.values, option) + FormatControl.handle_mutual_excludes( + value, existing.no_binary, existing.only_binary, + ) + + +def _handle_only_binary(option, opt_str, value, parser): + # type: (Option, str, str, OptionParser) -> None + existing = _get_format_control(parser.values, option) + FormatControl.handle_mutual_excludes( + value, existing.only_binary, existing.no_binary, + ) + + +def no_binary(): + # type: () -> Option + format_control = FormatControl(set(), set()) + return Option( + "--no-binary", dest="format_control", action="callback", + callback=_handle_no_binary, type="str", + default=format_control, + help='Do not use binary packages. Can be supplied multiple times, and ' + 'each time adds to the existing value. Accepts either ":all:" to ' + 'disable all binary packages, ":none:" to empty the set (notice ' + 'the colons), or one or more package names with commas between ' + 'them (no colons). Note that some packages are tricky to compile ' + 'and may fail to install when this option is used on them.', + ) + + +def only_binary(): + # type: () -> Option + format_control = FormatControl(set(), set()) + return Option( + "--only-binary", dest="format_control", action="callback", + callback=_handle_only_binary, type="str", + default=format_control, + help='Do not use source packages. Can be supplied multiple times, and ' + 'each time adds to the existing value. Accepts either ":all:" to ' + 'disable all source packages, ":none:" to empty the set, or one ' + 'or more package names with commas between them. Packages ' + 'without binary distributions will fail to install when this ' + 'option is used on them.', + ) + + +platforms = partial( + Option, + '--platform', + dest='platforms', + metavar='platform', + action='append', + default=None, + help=("Only use wheels compatible with . Defaults to the " + "platform of the running system. Use this option multiple times to " + "specify multiple platforms supported by the target interpreter."), +) # type: Callable[..., Option] + + +# This was made a separate function for unit-testing purposes. +def _convert_python_version(value): + # type: (str) -> Tuple[Tuple[int, ...], Optional[str]] + """ + Convert a version string like "3", "37", or "3.7.3" into a tuple of ints. + + :return: A 2-tuple (version_info, error_msg), where `error_msg` is + non-None if and only if there was a parsing error. + """ + if not value: + # The empty string is the same as not providing a value. + return (None, None) + + parts = value.split('.') + if len(parts) > 3: + return ((), 'at most three version parts are allowed') + + if len(parts) == 1: + # Then we are in the case of "3" or "37". + value = parts[0] + if len(value) > 1: + parts = [value[0], value[1:]] + + try: + version_info = tuple(int(part) for part in parts) + except ValueError: + return ((), 'each version part must be an integer') + + return (version_info, None) + + +def _handle_python_version(option, opt_str, value, parser): + # type: (Option, str, str, OptionParser) -> None + """ + Handle a provided --python-version value. + """ + version_info, error_msg = _convert_python_version(value) + if error_msg is not None: + msg = ( + 'invalid --python-version value: {!r}: {}'.format( + value, error_msg, + ) + ) + raise_option_error(parser, option=option, msg=msg) + + parser.values.python_version = version_info + + +python_version = partial( + Option, + '--python-version', + dest='python_version', + metavar='python_version', + action='callback', + callback=_handle_python_version, type='str', + default=None, + help=dedent("""\ + The Python interpreter version to use for wheel and "Requires-Python" + compatibility checks. Defaults to a version derived from the running + interpreter. The version can be specified using up to three dot-separated + integers (e.g. "3" for 3.0.0, "3.7" for 3.7.0, or "3.7.3"). A major-minor + version can also be given as a string without dots (e.g. "37" for 3.7.0). + """), +) # type: Callable[..., Option] + + +implementation = partial( + Option, + '--implementation', + dest='implementation', + metavar='implementation', + default=None, + help=("Only use wheels compatible with Python " + "implementation , e.g. 'pp', 'jy', 'cp', " + " or 'ip'. If not specified, then the current " + "interpreter implementation is used. Use 'py' to force " + "implementation-agnostic wheels."), +) # type: Callable[..., Option] + + +abis = partial( + Option, + '--abi', + dest='abis', + metavar='abi', + action='append', + default=None, + help=("Only use wheels compatible with Python abi , e.g. 'pypy_41'. " + "If not specified, then the current interpreter abi tag is used. " + "Use this option multiple times to specify multiple abis supported " + "by the target interpreter. Generally you will need to specify " + "--implementation, --platform, and --python-version when using this " + "option."), +) # type: Callable[..., Option] + + +def add_target_python_options(cmd_opts): + # type: (OptionGroup) -> None + cmd_opts.add_option(platforms()) + cmd_opts.add_option(python_version()) + cmd_opts.add_option(implementation()) + cmd_opts.add_option(abis()) + + +def make_target_python(options): + # type: (Values) -> TargetPython + target_python = TargetPython( + platforms=options.platforms, + py_version_info=options.python_version, + abis=options.abis, + implementation=options.implementation, + ) + + return target_python + + +def prefer_binary(): + # type: () -> Option + return Option( + "--prefer-binary", + dest="prefer_binary", + action="store_true", + default=False, + help="Prefer older binary packages over newer source packages." + ) + + +cache_dir = partial( + PipOption, + "--cache-dir", + dest="cache_dir", + default=USER_CACHE_DIR, + metavar="dir", + type='path', + help="Store the cache data in ." +) # type: Callable[..., Option] + + +def _handle_no_cache_dir(option, opt, value, parser): + # type: (Option, str, str, OptionParser) -> None + """ + Process a value provided for the --no-cache-dir option. + + This is an optparse.Option callback for the --no-cache-dir option. + """ + # The value argument will be None if --no-cache-dir is passed via the + # command-line, since the option doesn't accept arguments. However, + # the value can be non-None if the option is triggered e.g. by an + # environment variable, like PIP_NO_CACHE_DIR=true. + if value is not None: + # Then parse the string value to get argument error-checking. + try: + strtobool(value) + except ValueError as exc: + raise_option_error(parser, option=option, msg=str(exc)) + + # Originally, setting PIP_NO_CACHE_DIR to a value that strtobool() + # converted to 0 (like "false" or "no") caused cache_dir to be disabled + # rather than enabled (logic would say the latter). Thus, we disable + # the cache directory not just on values that parse to True, but (for + # backwards compatibility reasons) also on values that parse to False. + # In other words, always set it to False if the option is provided in + # some (valid) form. + parser.values.cache_dir = False + + +no_cache = partial( + Option, + "--no-cache-dir", + dest="cache_dir", + action="callback", + callback=_handle_no_cache_dir, + help="Disable the cache.", +) # type: Callable[..., Option] + +no_deps = partial( + Option, + '--no-deps', '--no-dependencies', + dest='ignore_dependencies', + action='store_true', + default=False, + help="Don't install package dependencies.", +) # type: Callable[..., Option] + +build_dir = partial( + PipOption, + '-b', '--build', '--build-dir', '--build-directory', + dest='build_dir', + type='path', + metavar='dir', + help=SUPPRESS_HELP, +) # type: Callable[..., Option] + +ignore_requires_python = partial( + Option, + '--ignore-requires-python', + dest='ignore_requires_python', + action='store_true', + help='Ignore the Requires-Python information.' +) # type: Callable[..., Option] + +no_build_isolation = partial( + Option, + '--no-build-isolation', + dest='build_isolation', + action='store_false', + default=True, + help='Disable isolation when building a modern source distribution. ' + 'Build dependencies specified by PEP 518 must be already installed ' + 'if this option is used.' +) # type: Callable[..., Option] + + +def _handle_no_use_pep517(option, opt, value, parser): + # type: (Option, str, str, OptionParser) -> None + """ + Process a value provided for the --no-use-pep517 option. + + This is an optparse.Option callback for the no_use_pep517 option. + """ + # Since --no-use-pep517 doesn't accept arguments, the value argument + # will be None if --no-use-pep517 is passed via the command-line. + # However, the value can be non-None if the option is triggered e.g. + # by an environment variable, for example "PIP_NO_USE_PEP517=true". + if value is not None: + msg = """A value was passed for --no-use-pep517, + probably using either the PIP_NO_USE_PEP517 environment variable + or the "no-use-pep517" config file option. Use an appropriate value + of the PIP_USE_PEP517 environment variable or the "use-pep517" + config file option instead. + """ + raise_option_error(parser, option=option, msg=msg) + + # Otherwise, --no-use-pep517 was passed via the command-line. + parser.values.use_pep517 = False + + +use_pep517 = partial( + Option, + '--use-pep517', + dest='use_pep517', + action='store_true', + default=None, + help='Use PEP 517 for building source distributions ' + '(use --no-use-pep517 to force legacy behaviour).' +) # type: Any + +no_use_pep517 = partial( + Option, + '--no-use-pep517', + dest='use_pep517', + action='callback', + callback=_handle_no_use_pep517, + default=None, + help=SUPPRESS_HELP +) # type: Any + +install_options = partial( + Option, + '--install-option', + dest='install_options', + action='append', + metavar='options', + help="Extra arguments to be supplied to the setup.py install " + "command (use like --install-option=\"--install-scripts=/usr/local/" + "bin\"). Use multiple --install-option options to pass multiple " + "options to setup.py install. If you are using an option with a " + "directory path, be sure to use absolute path.", +) # type: Callable[..., Option] + +global_options = partial( + Option, + '--global-option', + dest='global_options', + action='append', + metavar='options', + help="Extra global options to be supplied to the setup.py " + "call before the install command.", +) # type: Callable[..., Option] + +no_clean = partial( + Option, + '--no-clean', + action='store_true', + default=False, + help="Don't clean up build directories." +) # type: Callable[..., Option] + +pre = partial( + Option, + '--pre', + action='store_true', + default=False, + help="Include pre-release and development versions. By default, " + "pip only finds stable versions.", +) # type: Callable[..., Option] + +disable_pip_version_check = partial( + Option, + "--disable-pip-version-check", + dest="disable_pip_version_check", + action="store_true", + default=True, + help="Don't periodically check PyPI to determine whether a new version " + "of pip is available for download. Implied with --no-index.", +) # type: Callable[..., Option] + + +def _handle_merge_hash(option, opt_str, value, parser): + # type: (Option, str, str, OptionParser) -> None + """Given a value spelled "algo:digest", append the digest to a list + pointed to in a dict by the algo name.""" + if not parser.values.hashes: + parser.values.hashes = {} + try: + algo, digest = value.split(':', 1) + except ValueError: + parser.error('Arguments to {} must be a hash name ' # noqa + 'followed by a value, like --hash=sha256:' + 'abcde...'.format(opt_str)) + if algo not in STRONG_HASHES: + parser.error('Allowed hash algorithms for {} are {}.'.format( # noqa + opt_str, ', '.join(STRONG_HASHES))) + parser.values.hashes.setdefault(algo, []).append(digest) + + +hash = partial( + Option, + '--hash', + # Hash values eventually end up in InstallRequirement.hashes due to + # __dict__ copying in process_line(). + dest='hashes', + action='callback', + callback=_handle_merge_hash, + type='string', + help="Verify that the package's archive matches this " + 'hash before installing. Example: --hash=sha256:abcdef...', +) # type: Callable[..., Option] + + +require_hashes = partial( + Option, + '--require-hashes', + dest='require_hashes', + action='store_true', + default=False, + help='Require a hash to check each requirement against, for ' + 'repeatable installs. This option is implied when any package in a ' + 'requirements file has a --hash option.', +) # type: Callable[..., Option] + + +list_path = partial( + PipOption, + '--path', + dest='path', + type='path', + action='append', + help='Restrict to the specified installation path for listing ' + 'packages (can be used multiple times).' +) # type: Callable[..., Option] + + +def check_list_path_option(options): + # type: (Values) -> None + if options.path and (options.user or options.local): + raise CommandError( + "Cannot combine '--path' with '--user' or '--local'" + ) + + +list_exclude = partial( + PipOption, + '--exclude', + dest='excludes', + action='append', + metavar='package', + type='package_name', + help="Exclude specified package from the output", +) # type: Callable[..., Option] + + +no_python_version_warning = partial( + Option, + '--no-python-version-warning', + dest='no_python_version_warning', + action='store_true', + default=False, + help='Silence deprecation warnings for upcoming unsupported Pythons.', +) # type: Callable[..., Option] + + +use_new_feature = partial( + Option, + '--use-feature', + dest='features_enabled', + metavar='feature', + action='append', + default=[], + choices=['2020-resolver', 'fast-deps'], + help='Enable new functionality, that may be backward incompatible.', +) # type: Callable[..., Option] + +use_deprecated_feature = partial( + Option, + '--use-deprecated', + dest='deprecated_features_enabled', + metavar='feature', + action='append', + default=[], + choices=['legacy-resolver'], + help=( + 'Enable deprecated functionality, that will be removed in the future.' + ), +) # type: Callable[..., Option] + + +########## +# groups # +########## + +general_group = { + 'name': 'General Options', + 'options': [ + help_, + isolated_mode, + require_virtualenv, + verbose, + version, + quiet, + log, + no_input, + proxy, + retries, + timeout, + exists_action, + trusted_host, + cert, + client_cert, + cache_dir, + no_cache, + disable_pip_version_check, + no_color, + no_python_version_warning, + use_new_feature, + use_deprecated_feature, + ] +} # type: Dict[str, Any] + +index_group = { + 'name': 'Package Index Options', + 'options': [ + index_url, + extra_index_url, + no_index, + find_links, + ] +} # type: Dict[str, Any] diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/command_context.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/command_context.py new file mode 100644 index 0000000000..669c777749 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/command_context.py @@ -0,0 +1,36 @@ +from contextlib import contextmanager + +from pip._vendor.contextlib2 import ExitStack + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import ContextManager, Iterator, TypeVar + + _T = TypeVar('_T', covariant=True) + + +class CommandContextMixIn(object): + def __init__(self): + # type: () -> None + super(CommandContextMixIn, self).__init__() + self._in_main_context = False + self._main_context = ExitStack() + + @contextmanager + def main_context(self): + # type: () -> Iterator[None] + assert not self._in_main_context + + self._in_main_context = True + try: + with self._main_context: + yield + finally: + self._in_main_context = False + + def enter_context(self, context_provider): + # type: (ContextManager[_T]) -> _T + assert self._in_main_context + + return self._main_context.enter_context(context_provider) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/main.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/main.py new file mode 100644 index 0000000000..172f30dd5b --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/main.py @@ -0,0 +1,75 @@ +"""Primary application entrypoint. +""" +from __future__ import absolute_import + +import locale +import logging +import os +import sys + +from pip._internal.cli.autocompletion import autocomplete +from pip._internal.cli.main_parser import parse_command +from pip._internal.commands import create_command +from pip._internal.exceptions import PipError +from pip._internal.utils import deprecation +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List, Optional + +logger = logging.getLogger(__name__) + + +# Do not import and use main() directly! Using it directly is actively +# discouraged by pip's maintainers. The name, location and behavior of +# this function is subject to change, so calling it directly is not +# portable across different pip versions. + +# In addition, running pip in-process is unsupported and unsafe. This is +# elaborated in detail at +# https://pip.pypa.io/en/stable/user_guide/#using-pip-from-your-program. +# That document also provides suggestions that should work for nearly +# all users that are considering importing and using main() directly. + +# However, we know that certain users will still want to invoke pip +# in-process. If you understand and accept the implications of using pip +# in an unsupported manner, the best approach is to use runpy to avoid +# depending on the exact location of this entry point. + +# The following example shows how to use runpy to invoke pip in that +# case: +# +# sys.argv = ["pip", your, args, here] +# runpy.run_module("pip", run_name="__main__") +# +# Note that this will exit the process after running, unlike a direct +# call to main. As it is not safe to do any processing after calling +# main, this should not be an issue in practice. + +def main(args=None): + # type: (Optional[List[str]]) -> int + if args is None: + args = sys.argv[1:] + + # Configure our deprecation warnings to be sent through loggers + deprecation.install_warning_logger() + + autocomplete() + + try: + cmd_name, cmd_args = parse_command(args) + except PipError as exc: + sys.stderr.write("ERROR: {}".format(exc)) + sys.stderr.write(os.linesep) + sys.exit(1) + + # Needed for locale.getpreferredencoding(False) to work + # in pip._internal.utils.encoding.auto_decode + try: + locale.setlocale(locale.LC_ALL, '') + except locale.Error as e: + # setlocale can apparently crash if locale are uninitialized + logger.debug("Ignoring error %s when setting locale", e) + command = create_command(cmd_name, isolated=("--isolated" in cmd_args)) + + return command.main(cmd_args) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/main_parser.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/main_parser.py new file mode 100644 index 0000000000..ba3cf68aaf --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/main_parser.py @@ -0,0 +1,96 @@ +"""A single place for constructing and exposing the main parser +""" + +import os +import sys + +from pip._internal.cli import cmdoptions +from pip._internal.cli.parser import ConfigOptionParser, UpdatingDefaultsHelpFormatter +from pip._internal.commands import commands_dict, get_similar_commands +from pip._internal.exceptions import CommandError +from pip._internal.utils.misc import get_pip_version, get_prog +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List, Tuple + + +__all__ = ["create_main_parser", "parse_command"] + + +def create_main_parser(): + # type: () -> ConfigOptionParser + """Creates and returns the main parser for pip's CLI + """ + + parser_kw = { + 'usage': '\n%prog [options]', + 'add_help_option': False, + 'formatter': UpdatingDefaultsHelpFormatter(), + 'name': 'global', + 'prog': get_prog(), + } + + parser = ConfigOptionParser(**parser_kw) + parser.disable_interspersed_args() + + parser.version = get_pip_version() + + # add the general options + gen_opts = cmdoptions.make_option_group(cmdoptions.general_group, parser) + parser.add_option_group(gen_opts) + + # so the help formatter knows + parser.main = True # type: ignore + + # create command listing for description + description = [''] + [ + '{name:27} {command_info.summary}'.format(**locals()) + for name, command_info in commands_dict.items() + ] + parser.description = '\n'.join(description) + + return parser + + +def parse_command(args): + # type: (List[str]) -> Tuple[str, List[str]] + parser = create_main_parser() + + # Note: parser calls disable_interspersed_args(), so the result of this + # call is to split the initial args into the general options before the + # subcommand and everything else. + # For example: + # args: ['--timeout=5', 'install', '--user', 'INITools'] + # general_options: ['--timeout==5'] + # args_else: ['install', '--user', 'INITools'] + general_options, args_else = parser.parse_args(args) + + # --version + if general_options.version: + sys.stdout.write(parser.version) # type: ignore + sys.stdout.write(os.linesep) + sys.exit() + + # pip || pip help -> print_help() + if not args_else or (args_else[0] == 'help' and len(args_else) == 1): + parser.print_help() + sys.exit() + + # the subcommand name + cmd_name = args_else[0] + + if cmd_name not in commands_dict: + guess = get_similar_commands(cmd_name) + + msg = ['unknown command "{}"'.format(cmd_name)] + if guess: + msg.append('maybe you meant "{}"'.format(guess)) + + raise CommandError(' - '.join(msg)) + + # all the args without the subcommand + cmd_args = args[:] + cmd_args.remove(cmd_name) + + return cmd_name, cmd_args diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/parser.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/parser.py new file mode 100644 index 0000000000..7170bfd384 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/parser.py @@ -0,0 +1,285 @@ +"""Base option parser setup""" + +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False + +from __future__ import absolute_import + +import logging +import optparse +import sys +import textwrap +from distutils.util import strtobool + +from pip._vendor.contextlib2 import suppress +from pip._vendor.six import string_types + +from pip._internal.cli.status_codes import UNKNOWN_ERROR +from pip._internal.configuration import Configuration, ConfigurationError +from pip._internal.utils.compat import get_terminal_size +from pip._internal.utils.misc import redact_auth_from_url + +logger = logging.getLogger(__name__) + + +class PrettyHelpFormatter(optparse.IndentedHelpFormatter): + """A prettier/less verbose help formatter for optparse.""" + + def __init__(self, *args, **kwargs): + # help position must be aligned with __init__.parseopts.description + kwargs['max_help_position'] = 30 + kwargs['indent_increment'] = 1 + kwargs['width'] = get_terminal_size()[0] - 2 + optparse.IndentedHelpFormatter.__init__(self, *args, **kwargs) + + def format_option_strings(self, option): + return self._format_option_strings(option) + + def _format_option_strings(self, option, mvarfmt=' <{}>', optsep=', '): + """ + Return a comma-separated list of option strings and metavars. + + :param option: tuple of (short opt, long opt), e.g: ('-f', '--format') + :param mvarfmt: metavar format string + :param optsep: separator + """ + opts = [] + + if option._short_opts: + opts.append(option._short_opts[0]) + if option._long_opts: + opts.append(option._long_opts[0]) + if len(opts) > 1: + opts.insert(1, optsep) + + if option.takes_value(): + metavar = option.metavar or option.dest.lower() + opts.append(mvarfmt.format(metavar.lower())) + + return ''.join(opts) + + def format_heading(self, heading): + if heading == 'Options': + return '' + return heading + ':\n' + + def format_usage(self, usage): + """ + Ensure there is only one newline between usage and the first heading + if there is no description. + """ + msg = '\nUsage: {}\n'.format( + self.indent_lines(textwrap.dedent(usage), " ")) + return msg + + def format_description(self, description): + # leave full control over description to us + if description: + if hasattr(self.parser, 'main'): + label = 'Commands' + else: + label = 'Description' + # some doc strings have initial newlines, some don't + description = description.lstrip('\n') + # some doc strings have final newlines and spaces, some don't + description = description.rstrip() + # dedent, then reindent + description = self.indent_lines(textwrap.dedent(description), " ") + description = '{}:\n{}\n'.format(label, description) + return description + else: + return '' + + def format_epilog(self, epilog): + # leave full control over epilog to us + if epilog: + return epilog + else: + return '' + + def indent_lines(self, text, indent): + new_lines = [indent + line for line in text.split('\n')] + return "\n".join(new_lines) + + +class UpdatingDefaultsHelpFormatter(PrettyHelpFormatter): + """Custom help formatter for use in ConfigOptionParser. + + This is updates the defaults before expanding them, allowing + them to show up correctly in the help listing. + + Also redact auth from url type options + """ + + def expand_default(self, option): + default_values = None + if self.parser is not None: + self.parser._update_defaults(self.parser.defaults) + default_values = self.parser.defaults.get(option.dest) + help_text = optparse.IndentedHelpFormatter.expand_default(self, option) + + if default_values and option.metavar == 'URL': + if isinstance(default_values, string_types): + default_values = [default_values] + + # If its not a list, we should abort and just return the help text + if not isinstance(default_values, list): + default_values = [] + + for val in default_values: + help_text = help_text.replace( + val, redact_auth_from_url(val)) + + return help_text + + +class CustomOptionParser(optparse.OptionParser): + + def insert_option_group(self, idx, *args, **kwargs): + """Insert an OptionGroup at a given position.""" + group = self.add_option_group(*args, **kwargs) + + self.option_groups.pop() + self.option_groups.insert(idx, group) + + return group + + @property + def option_list_all(self): + """Get a list of all options, including those in option groups.""" + res = self.option_list[:] + for i in self.option_groups: + res.extend(i.option_list) + + return res + + +class ConfigOptionParser(CustomOptionParser): + """Custom option parser which updates its defaults by checking the + configuration files and environmental variables""" + + def __init__(self, *args, **kwargs): + self.name = kwargs.pop('name') + + isolated = kwargs.pop("isolated", False) + self.config = Configuration(isolated) + + assert self.name + optparse.OptionParser.__init__(self, *args, **kwargs) + + def check_default(self, option, key, val): + try: + return option.check_value(key, val) + except optparse.OptionValueError as exc: + print("An error occurred during configuration: {}".format(exc)) + sys.exit(3) + + def _get_ordered_configuration_items(self): + # Configuration gives keys in an unordered manner. Order them. + override_order = ["global", self.name, ":env:"] + + # Pool the options into different groups + section_items = {name: [] for name in override_order} + for section_key, val in self.config.items(): + # ignore empty values + if not val: + logger.debug( + "Ignoring configuration key '%s' as it's value is empty.", + section_key + ) + continue + + section, key = section_key.split(".", 1) + if section in override_order: + section_items[section].append((key, val)) + + # Yield each group in their override order + for section in override_order: + for key, val in section_items[section]: + yield key, val + + def _update_defaults(self, defaults): + """Updates the given defaults with values from the config files and + the environ. Does a little special handling for certain types of + options (lists).""" + + # Accumulate complex default state. + self.values = optparse.Values(self.defaults) + late_eval = set() + # Then set the options with those values + for key, val in self._get_ordered_configuration_items(): + # '--' because configuration supports only long names + option = self.get_option('--' + key) + + # Ignore options not present in this parser. E.g. non-globals put + # in [global] by users that want them to apply to all applicable + # commands. + if option is None: + continue + + if option.action in ('store_true', 'store_false'): + try: + val = strtobool(val) + except ValueError: + self.error( + '{} is not a valid value for {} option, ' # noqa + 'please specify a boolean value like yes/no, ' + 'true/false or 1/0 instead.'.format(val, key) + ) + elif option.action == 'count': + with suppress(ValueError): + val = strtobool(val) + with suppress(ValueError): + val = int(val) + if not isinstance(val, int) or val < 0: + self.error( + '{} is not a valid value for {} option, ' # noqa + 'please instead specify either a non-negative integer ' + 'or a boolean value like yes/no or false/true ' + 'which is equivalent to 1/0.'.format(val, key) + ) + elif option.action == 'append': + val = val.split() + val = [self.check_default(option, key, v) for v in val] + elif option.action == 'callback': + late_eval.add(option.dest) + opt_str = option.get_opt_string() + val = option.convert_value(opt_str, val) + # From take_action + args = option.callback_args or () + kwargs = option.callback_kwargs or {} + option.callback(option, opt_str, val, self, *args, **kwargs) + else: + val = self.check_default(option, key, val) + + defaults[option.dest] = val + + for key in late_eval: + defaults[key] = getattr(self.values, key) + self.values = None + return defaults + + def get_default_values(self): + """Overriding to make updating the defaults after instantiation of + the option parser possible, _update_defaults() does the dirty work.""" + if not self.process_default_values: + # Old, pre-Optik 1.5 behaviour. + return optparse.Values(self.defaults) + + # Load the configuration, or error out in case of an error + try: + self.config.load() + except ConfigurationError as err: + self.exit(UNKNOWN_ERROR, str(err)) + + defaults = self._update_defaults(self.defaults.copy()) # ours + for option in self._get_all_options(): + default = defaults.get(option.dest) + if isinstance(default, string_types): + opt_str = option.get_opt_string() + defaults[option.dest] = option.check_value(opt_str, default) + return optparse.Values(defaults) + + def error(self, msg): + self.print_usage(sys.stderr) + self.exit(UNKNOWN_ERROR, "{}\n".format(msg)) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/progress_bars.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/progress_bars.py new file mode 100644 index 0000000000..69338552f1 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/progress_bars.py @@ -0,0 +1,280 @@ +from __future__ import division + +import itertools +import sys +from signal import SIGINT, default_int_handler, signal + +from pip._vendor import six +from pip._vendor.progress.bar import Bar, FillingCirclesBar, IncrementalBar +from pip._vendor.progress.spinner import Spinner + +from pip._internal.utils.compat import WINDOWS +from pip._internal.utils.logging import get_indentation +from pip._internal.utils.misc import format_size +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Any, Dict, List + +try: + from pip._vendor import colorama +# Lots of different errors can come from this, including SystemError and +# ImportError. +except Exception: + colorama = None + + +def _select_progress_class(preferred, fallback): + # type: (Bar, Bar) -> Bar + encoding = getattr(preferred.file, "encoding", None) + + # If we don't know what encoding this file is in, then we'll just assume + # that it doesn't support unicode and use the ASCII bar. + if not encoding: + return fallback + + # Collect all of the possible characters we want to use with the preferred + # bar. + characters = [ + getattr(preferred, "empty_fill", six.text_type()), + getattr(preferred, "fill", six.text_type()), + ] + characters += list(getattr(preferred, "phases", [])) + + # Try to decode the characters we're using for the bar using the encoding + # of the given file, if this works then we'll assume that we can use the + # fancier bar and if not we'll fall back to the plaintext bar. + try: + six.text_type().join(characters).encode(encoding) + except UnicodeEncodeError: + return fallback + else: + return preferred + + +_BaseBar = _select_progress_class(IncrementalBar, Bar) # type: Any + + +class InterruptibleMixin(object): + """ + Helper to ensure that self.finish() gets called on keyboard interrupt. + + This allows downloads to be interrupted without leaving temporary state + (like hidden cursors) behind. + + This class is similar to the progress library's existing SigIntMixin + helper, but as of version 1.2, that helper has the following problems: + + 1. It calls sys.exit(). + 2. It discards the existing SIGINT handler completely. + 3. It leaves its own handler in place even after an uninterrupted finish, + which will have unexpected delayed effects if the user triggers an + unrelated keyboard interrupt some time after a progress-displaying + download has already completed, for example. + """ + + def __init__(self, *args, **kwargs): + # type: (List[Any], Dict[Any, Any]) -> None + """ + Save the original SIGINT handler for later. + """ + # https://github.com/python/mypy/issues/5887 + super(InterruptibleMixin, self).__init__( # type: ignore + *args, + **kwargs + ) + + self.original_handler = signal(SIGINT, self.handle_sigint) + + # If signal() returns None, the previous handler was not installed from + # Python, and we cannot restore it. This probably should not happen, + # but if it does, we must restore something sensible instead, at least. + # The least bad option should be Python's default SIGINT handler, which + # just raises KeyboardInterrupt. + if self.original_handler is None: + self.original_handler = default_int_handler + + def finish(self): + # type: () -> None + """ + Restore the original SIGINT handler after finishing. + + This should happen regardless of whether the progress display finishes + normally, or gets interrupted. + """ + super(InterruptibleMixin, self).finish() # type: ignore + signal(SIGINT, self.original_handler) + + def handle_sigint(self, signum, frame): # type: ignore + """ + Call self.finish() before delegating to the original SIGINT handler. + + This handler should only be in place while the progress display is + active. + """ + self.finish() + self.original_handler(signum, frame) + + +class SilentBar(Bar): + + def update(self): + # type: () -> None + pass + + +class BlueEmojiBar(IncrementalBar): + + suffix = "%(percent)d%%" + bar_prefix = " " + bar_suffix = " " + phases = (u"\U0001F539", u"\U0001F537", u"\U0001F535") # type: Any + + +class DownloadProgressMixin(object): + + def __init__(self, *args, **kwargs): + # type: (List[Any], Dict[Any, Any]) -> None + # https://github.com/python/mypy/issues/5887 + super(DownloadProgressMixin, self).__init__( # type: ignore + *args, + **kwargs + ) + self.message = (" " * ( + get_indentation() + 2 + )) + self.message # type: str + + @property + def downloaded(self): + # type: () -> str + return format_size(self.index) # type: ignore + + @property + def download_speed(self): + # type: () -> str + # Avoid zero division errors... + if self.avg == 0.0: # type: ignore + return "..." + return format_size(1 / self.avg) + "/s" # type: ignore + + @property + def pretty_eta(self): + # type: () -> str + if self.eta: # type: ignore + return "eta {}".format(self.eta_td) # type: ignore + return "" + + def iter(self, it): # type: ignore + for x in it: + yield x + # B305 is incorrectly raised here + # https://github.com/PyCQA/flake8-bugbear/issues/59 + self.next(len(x)) # noqa: B305 + self.finish() + + +class WindowsMixin(object): + + def __init__(self, *args, **kwargs): + # type: (List[Any], Dict[Any, Any]) -> None + # The Windows terminal does not support the hide/show cursor ANSI codes + # even with colorama. So we'll ensure that hide_cursor is False on + # Windows. + # This call needs to go before the super() call, so that hide_cursor + # is set in time. The base progress bar class writes the "hide cursor" + # code to the terminal in its init, so if we don't set this soon + # enough, we get a "hide" with no corresponding "show"... + if WINDOWS and self.hide_cursor: # type: ignore + self.hide_cursor = False + + # https://github.com/python/mypy/issues/5887 + super(WindowsMixin, self).__init__(*args, **kwargs) # type: ignore + + # Check if we are running on Windows and we have the colorama module, + # if we do then wrap our file with it. + if WINDOWS and colorama: + self.file = colorama.AnsiToWin32(self.file) # type: ignore + # The progress code expects to be able to call self.file.isatty() + # but the colorama.AnsiToWin32() object doesn't have that, so we'll + # add it. + self.file.isatty = lambda: self.file.wrapped.isatty() + # The progress code expects to be able to call self.file.flush() + # but the colorama.AnsiToWin32() object doesn't have that, so we'll + # add it. + self.file.flush = lambda: self.file.wrapped.flush() + + +class BaseDownloadProgressBar(WindowsMixin, InterruptibleMixin, + DownloadProgressMixin): + + file = sys.stdout + message = "%(percent)d%%" + suffix = "%(downloaded)s %(download_speed)s %(pretty_eta)s" + + +class DefaultDownloadProgressBar(BaseDownloadProgressBar, + _BaseBar): + pass + + +class DownloadSilentBar(BaseDownloadProgressBar, SilentBar): + pass + + +class DownloadBar(BaseDownloadProgressBar, + Bar): + pass + + +class DownloadFillingCirclesBar(BaseDownloadProgressBar, + FillingCirclesBar): + pass + + +class DownloadBlueEmojiProgressBar(BaseDownloadProgressBar, + BlueEmojiBar): + pass + + +class DownloadProgressSpinner(WindowsMixin, InterruptibleMixin, + DownloadProgressMixin, Spinner): + + file = sys.stdout + suffix = "%(downloaded)s %(download_speed)s" + + def next_phase(self): + # type: () -> str + if not hasattr(self, "_phaser"): + self._phaser = itertools.cycle(self.phases) + return next(self._phaser) + + def update(self): + # type: () -> None + message = self.message % self + phase = self.next_phase() + suffix = self.suffix % self + line = ''.join([ + message, + " " if message else "", + phase, + " " if suffix else "", + suffix, + ]) + + self.writeln(line) + + +BAR_TYPES = { + "off": (DownloadSilentBar, DownloadSilentBar), + "on": (DefaultDownloadProgressBar, DownloadProgressSpinner), + "ascii": (DownloadBar, DownloadProgressSpinner), + "pretty": (DownloadFillingCirclesBar, DownloadProgressSpinner), + "emoji": (DownloadBlueEmojiProgressBar, DownloadProgressSpinner) +} + + +def DownloadProgressProvider(progress_bar, max=None): # type: ignore + if max is None or max == 0: + return BAR_TYPES[progress_bar][1]().iter + else: + return BAR_TYPES[progress_bar][0](max=max).iter diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/req_command.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/req_command.py new file mode 100644 index 0000000000..008066ab1c --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/req_command.py @@ -0,0 +1,436 @@ +"""Contains the Command base classes that depend on PipSession. + +The classes in this module are in a separate module so the commands not +needing download / PackageFinder capability don't unnecessarily import the +PackageFinder machinery and all its vendored dependencies, etc. +""" + +import logging +import os +from functools import partial + +from pip._vendor.six import PY2 + +from pip._internal.cli import cmdoptions +from pip._internal.cli.base_command import Command +from pip._internal.cli.command_context import CommandContextMixIn +from pip._internal.exceptions import CommandError, PreviousBuildDirError +from pip._internal.index.collector import LinkCollector +from pip._internal.index.package_finder import PackageFinder +from pip._internal.models.selection_prefs import SelectionPreferences +from pip._internal.network.session import PipSession +from pip._internal.operations.prepare import RequirementPreparer +from pip._internal.req.constructors import ( + install_req_from_editable, + install_req_from_line, + install_req_from_parsed_requirement, + install_req_from_req_string, +) +from pip._internal.req.req_file import parse_requirements +from pip._internal.self_outdated_check import pip_self_version_check +from pip._internal.utils.temp_dir import tempdir_kinds +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from optparse import Values + from typing import Any, List, Optional, Tuple + + from pip._internal.cache import WheelCache + from pip._internal.models.target_python import TargetPython + from pip._internal.req.req_install import InstallRequirement + from pip._internal.req.req_tracker import RequirementTracker + from pip._internal.resolution.base import BaseResolver + from pip._internal.utils.temp_dir import TempDirectory, TempDirectoryTypeRegistry + + +logger = logging.getLogger(__name__) + + +class SessionCommandMixin(CommandContextMixIn): + + """ + A class mixin for command classes needing _build_session(). + """ + def __init__(self): + # type: () -> None + super(SessionCommandMixin, self).__init__() + self._session = None # Optional[PipSession] + + @classmethod + def _get_index_urls(cls, options): + # type: (Values) -> Optional[List[str]] + """Return a list of index urls from user-provided options.""" + index_urls = [] + if not getattr(options, "no_index", False): + url = getattr(options, "index_url", None) + if url: + index_urls.append(url) + urls = getattr(options, "extra_index_urls", None) + if urls: + index_urls.extend(urls) + # Return None rather than an empty list + return index_urls or None + + def get_default_session(self, options): + # type: (Values) -> PipSession + """Get a default-managed session.""" + if self._session is None: + self._session = self.enter_context(self._build_session(options)) + # there's no type annotation on requests.Session, so it's + # automatically ContextManager[Any] and self._session becomes Any, + # then https://github.com/python/mypy/issues/7696 kicks in + assert self._session is not None + return self._session + + def _build_session(self, options, retries=None, timeout=None): + # type: (Values, Optional[int], Optional[int]) -> PipSession + assert not options.cache_dir or os.path.isabs(options.cache_dir) + session = PipSession( + cache=( + os.path.join(options.cache_dir, "http") + if options.cache_dir else None + ), + retries=retries if retries is not None else options.retries, + trusted_hosts=options.trusted_hosts, + index_urls=self._get_index_urls(options), + ) + + # Handle custom ca-bundles from the user + if options.cert: + session.verify = options.cert + + # Handle SSL client certificate + if options.client_cert: + session.cert = options.client_cert + + # Handle timeouts + if options.timeout or timeout: + session.timeout = ( + timeout if timeout is not None else options.timeout + ) + + # Handle configured proxies + if options.proxy: + session.proxies = { + "http": options.proxy, + "https": options.proxy, + } + + # Determine if we can prompt the user for authentication or not + session.auth.prompting = not options.no_input + + return session + + +class IndexGroupCommand(Command, SessionCommandMixin): + + """ + Abstract base class for commands with the index_group options. + + This also corresponds to the commands that permit the pip version check. + """ + + def handle_pip_version_check(self, options): + # type: (Values) -> None + """ + Do the pip version check if not disabled. + + This overrides the default behavior of not doing the check. + """ + # Make sure the index_group options are present. + assert hasattr(options, 'no_index') + + if options.disable_pip_version_check or options.no_index: + return + + # Otherwise, check if we're using the latest version of pip available. + session = self._build_session( + options, + retries=0, + timeout=min(5, options.timeout) + ) + with session: + pip_self_version_check(session, options) + + +KEEPABLE_TEMPDIR_TYPES = [ + tempdir_kinds.BUILD_ENV, + tempdir_kinds.EPHEM_WHEEL_CACHE, + tempdir_kinds.REQ_BUILD, +] + + +def with_cleanup(func): + # type: (Any) -> Any + """Decorator for common logic related to managing temporary + directories. + """ + def configure_tempdir_registry(registry): + # type: (TempDirectoryTypeRegistry) -> None + for t in KEEPABLE_TEMPDIR_TYPES: + registry.set_delete(t, False) + + def wrapper(self, options, args): + # type: (RequirementCommand, Values, List[Any]) -> Optional[int] + assert self.tempdir_registry is not None + if options.no_clean: + configure_tempdir_registry(self.tempdir_registry) + + try: + return func(self, options, args) + except PreviousBuildDirError: + # This kind of conflict can occur when the user passes an explicit + # build directory with a pre-existing folder. In that case we do + # not want to accidentally remove it. + configure_tempdir_registry(self.tempdir_registry) + raise + + return wrapper + + +class RequirementCommand(IndexGroupCommand): + + def __init__(self, *args, **kw): + # type: (Any, Any) -> None + super(RequirementCommand, self).__init__(*args, **kw) + + self.cmd_opts.add_option(cmdoptions.no_clean()) + + @staticmethod + def determine_resolver_variant(options): + # type: (Values) -> str + """Determines which resolver should be used, based on the given options.""" + # We didn't want to change things for Python 2, since it's nearly done with + # and we're using performance improvements that only work on Python 3. + if PY2: + if '2020-resolver' in options.features_enabled: + return "2020-resolver" + else: + return "legacy" + + if "legacy-resolver" in options.deprecated_features_enabled: + return "legacy" + + return "2020-resolver" + + @classmethod + def make_requirement_preparer( + cls, + temp_build_dir, # type: TempDirectory + options, # type: Values + req_tracker, # type: RequirementTracker + session, # type: PipSession + finder, # type: PackageFinder + use_user_site, # type: bool + download_dir=None, # type: str + ): + # type: (...) -> RequirementPreparer + """ + Create a RequirementPreparer instance for the given parameters. + """ + temp_build_dir_path = temp_build_dir.path + assert temp_build_dir_path is not None + + resolver_variant = cls.determine_resolver_variant(options) + if resolver_variant == "2020-resolver": + lazy_wheel = 'fast-deps' in options.features_enabled + if lazy_wheel: + logger.warning( + 'pip is using lazily downloaded wheels using HTTP ' + 'range requests to obtain dependency information. ' + 'This experimental feature is enabled through ' + '--use-feature=fast-deps and it is not ready for ' + 'production.' + ) + else: + lazy_wheel = False + if 'fast-deps' in options.features_enabled: + logger.warning( + 'fast-deps has no effect when used with the legacy resolver.' + ) + + return RequirementPreparer( + build_dir=temp_build_dir_path, + src_dir=options.src_dir, + download_dir=download_dir, + build_isolation=options.build_isolation, + req_tracker=req_tracker, + session=session, + progress_bar=options.progress_bar, + finder=finder, + require_hashes=options.require_hashes, + use_user_site=use_user_site, + lazy_wheel=lazy_wheel, + ) + + @classmethod + def make_resolver( + cls, + preparer, # type: RequirementPreparer + finder, # type: PackageFinder + options, # type: Values + wheel_cache=None, # type: Optional[WheelCache] + use_user_site=False, # type: bool + ignore_installed=True, # type: bool + ignore_requires_python=False, # type: bool + force_reinstall=False, # type: bool + upgrade_strategy="to-satisfy-only", # type: str + use_pep517=None, # type: Optional[bool] + py_version_info=None, # type: Optional[Tuple[int, ...]] + ): + # type: (...) -> BaseResolver + """ + Create a Resolver instance for the given parameters. + """ + make_install_req = partial( + install_req_from_req_string, + isolated=options.isolated_mode, + use_pep517=use_pep517, + ) + resolver_variant = cls.determine_resolver_variant(options) + # The long import name and duplicated invocation is needed to convince + # Mypy into correctly typechecking. Otherwise it would complain the + # "Resolver" class being redefined. + if resolver_variant == "2020-resolver": + import pip._internal.resolution.resolvelib.resolver + + return pip._internal.resolution.resolvelib.resolver.Resolver( + preparer=preparer, + finder=finder, + wheel_cache=wheel_cache, + make_install_req=make_install_req, + use_user_site=use_user_site, + ignore_dependencies=options.ignore_dependencies, + ignore_installed=ignore_installed, + ignore_requires_python=ignore_requires_python, + force_reinstall=force_reinstall, + upgrade_strategy=upgrade_strategy, + py_version_info=py_version_info, + ) + import pip._internal.resolution.legacy.resolver + return pip._internal.resolution.legacy.resolver.Resolver( + preparer=preparer, + finder=finder, + wheel_cache=wheel_cache, + make_install_req=make_install_req, + use_user_site=use_user_site, + ignore_dependencies=options.ignore_dependencies, + ignore_installed=ignore_installed, + ignore_requires_python=ignore_requires_python, + force_reinstall=force_reinstall, + upgrade_strategy=upgrade_strategy, + py_version_info=py_version_info, + ) + + def get_requirements( + self, + args, # type: List[str] + options, # type: Values + finder, # type: PackageFinder + session, # type: PipSession + ): + # type: (...) -> List[InstallRequirement] + """ + Parse command-line arguments into the corresponding requirements. + """ + requirements = [] # type: List[InstallRequirement] + for filename in options.constraints: + for parsed_req in parse_requirements( + filename, + constraint=True, finder=finder, options=options, + session=session): + req_to_add = install_req_from_parsed_requirement( + parsed_req, + isolated=options.isolated_mode, + user_supplied=False, + ) + requirements.append(req_to_add) + + for req in args: + req_to_add = install_req_from_line( + req, None, isolated=options.isolated_mode, + use_pep517=options.use_pep517, + user_supplied=True, + ) + requirements.append(req_to_add) + + for req in options.editables: + req_to_add = install_req_from_editable( + req, + user_supplied=True, + isolated=options.isolated_mode, + use_pep517=options.use_pep517, + ) + requirements.append(req_to_add) + + # NOTE: options.require_hashes may be set if --require-hashes is True + for filename in options.requirements: + for parsed_req in parse_requirements( + filename, + finder=finder, options=options, session=session): + req_to_add = install_req_from_parsed_requirement( + parsed_req, + isolated=options.isolated_mode, + use_pep517=options.use_pep517, + user_supplied=True, + ) + requirements.append(req_to_add) + + # If any requirement has hash options, enable hash checking. + if any(req.has_hash_options for req in requirements): + options.require_hashes = True + + if not (args or options.editables or options.requirements): + opts = {'name': self.name} + if options.find_links: + raise CommandError( + 'You must give at least one requirement to {name} ' + '(maybe you meant "pip {name} {links}"?)'.format( + **dict(opts, links=' '.join(options.find_links)))) + else: + raise CommandError( + 'You must give at least one requirement to {name} ' + '(see "pip help {name}")'.format(**opts)) + + return requirements + + @staticmethod + def trace_basic_info(finder): + # type: (PackageFinder) -> None + """ + Trace basic information about the provided objects. + """ + # Display where finder is looking for packages + search_scope = finder.search_scope + locations = search_scope.get_formatted_locations() + if locations: + logger.info(locations) + + def _build_package_finder( + self, + options, # type: Values + session, # type: PipSession + target_python=None, # type: Optional[TargetPython] + ignore_requires_python=None, # type: Optional[bool] + ): + # type: (...) -> PackageFinder + """ + Create a package finder appropriate to this requirement command. + + :param ignore_requires_python: Whether to ignore incompatible + "Requires-Python" values in links. Defaults to False. + """ + link_collector = LinkCollector.create(session, options=options) + selection_prefs = SelectionPreferences( + allow_yanked=True, + format_control=options.format_control, + allow_all_prereleases=options.pre, + prefer_binary=options.prefer_binary, + ignore_requires_python=ignore_requires_python, + ) + + return PackageFinder.create( + link_collector=link_collector, + selection_prefs=selection_prefs, + target_python=target_python, + ) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/spinners.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/spinners.py new file mode 100644 index 0000000000..65c3c23d74 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/spinners.py @@ -0,0 +1,173 @@ +from __future__ import absolute_import, division + +import contextlib +import itertools +import logging +import sys +import time + +from pip._vendor.progress import HIDE_CURSOR, SHOW_CURSOR + +from pip._internal.utils.compat import WINDOWS +from pip._internal.utils.logging import get_indentation +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import IO, Iterator + +logger = logging.getLogger(__name__) + + +class SpinnerInterface(object): + def spin(self): + # type: () -> None + raise NotImplementedError() + + def finish(self, final_status): + # type: (str) -> None + raise NotImplementedError() + + +class InteractiveSpinner(SpinnerInterface): + def __init__(self, message, file=None, spin_chars="-\\|/", + # Empirically, 8 updates/second looks nice + min_update_interval_seconds=0.125): + # type: (str, IO[str], str, float) -> None + self._message = message + if file is None: + file = sys.stdout + self._file = file + self._rate_limiter = RateLimiter(min_update_interval_seconds) + self._finished = False + + self._spin_cycle = itertools.cycle(spin_chars) + + self._file.write(" " * get_indentation() + self._message + " ... ") + self._width = 0 + + def _write(self, status): + # type: (str) -> None + assert not self._finished + # Erase what we wrote before by backspacing to the beginning, writing + # spaces to overwrite the old text, and then backspacing again + backup = "\b" * self._width + self._file.write(backup + " " * self._width + backup) + # Now we have a blank slate to add our status + self._file.write(status) + self._width = len(status) + self._file.flush() + self._rate_limiter.reset() + + def spin(self): + # type: () -> None + if self._finished: + return + if not self._rate_limiter.ready(): + return + self._write(next(self._spin_cycle)) + + def finish(self, final_status): + # type: (str) -> None + if self._finished: + return + self._write(final_status) + self._file.write("\n") + self._file.flush() + self._finished = True + + +# Used for dumb terminals, non-interactive installs (no tty), etc. +# We still print updates occasionally (once every 60 seconds by default) to +# act as a keep-alive for systems like Travis-CI that take lack-of-output as +# an indication that a task has frozen. +class NonInteractiveSpinner(SpinnerInterface): + def __init__(self, message, min_update_interval_seconds=60): + # type: (str, float) -> None + self._message = message + self._finished = False + self._rate_limiter = RateLimiter(min_update_interval_seconds) + self._update("started") + + def _update(self, status): + # type: (str) -> None + assert not self._finished + self._rate_limiter.reset() + logger.info("%s: %s", self._message, status) + + def spin(self): + # type: () -> None + if self._finished: + return + if not self._rate_limiter.ready(): + return + self._update("still running...") + + def finish(self, final_status): + # type: (str) -> None + if self._finished: + return + self._update( + "finished with status '{final_status}'".format(**locals())) + self._finished = True + + +class RateLimiter(object): + def __init__(self, min_update_interval_seconds): + # type: (float) -> None + self._min_update_interval_seconds = min_update_interval_seconds + self._last_update = 0 # type: float + + def ready(self): + # type: () -> bool + now = time.time() + delta = now - self._last_update + return delta >= self._min_update_interval_seconds + + def reset(self): + # type: () -> None + self._last_update = time.time() + + +@contextlib.contextmanager +def open_spinner(message): + # type: (str) -> Iterator[SpinnerInterface] + # Interactive spinner goes directly to sys.stdout rather than being routed + # through the logging system, but it acts like it has level INFO, + # i.e. it's only displayed if we're at level INFO or better. + # Non-interactive spinner goes through the logging system, so it is always + # in sync with logging configuration. + if sys.stdout.isatty() and logger.getEffectiveLevel() <= logging.INFO: + spinner = InteractiveSpinner(message) # type: SpinnerInterface + else: + spinner = NonInteractiveSpinner(message) + try: + with hidden_cursor(sys.stdout): + yield spinner + except KeyboardInterrupt: + spinner.finish("canceled") + raise + except Exception: + spinner.finish("error") + raise + else: + spinner.finish("done") + + +@contextlib.contextmanager +def hidden_cursor(file): + # type: (IO[str]) -> Iterator[None] + # The Windows terminal does not support the hide/show cursor ANSI codes, + # even via colorama. So don't even try. + if WINDOWS: + yield + # We don't want to clutter the output with control characters if we're + # writing to a file, or if the user is running with --quiet. + # See https://github.com/pypa/pip/issues/3418 + elif not file.isatty() or logger.getEffectiveLevel() > logging.INFO: + yield + else: + file.write(HIDE_CURSOR) + try: + yield + finally: + file.write(SHOW_CURSOR) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/status_codes.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/status_codes.py new file mode 100644 index 0000000000..275360a317 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/cli/status_codes.py @@ -0,0 +1,8 @@ +from __future__ import absolute_import + +SUCCESS = 0 +ERROR = 1 +UNKNOWN_ERROR = 2 +VIRTUALENV_NOT_FOUND = 3 +PREVIOUS_BUILD_DIR_ERROR = 4 +NO_MATCHES_FOUND = 23 diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/__init__.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/__init__.py new file mode 100644 index 0000000000..4f0c4ba3ab --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/__init__.py @@ -0,0 +1,123 @@ +""" +Package containing all pip commands +""" + +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False +# There is currently a bug in python/typeshed mentioned at +# https://github.com/python/typeshed/issues/3906 which causes the +# return type of difflib.get_close_matches to be reported +# as List[Sequence[str]] whereas it should have been List[str] + +from __future__ import absolute_import + +import importlib +from collections import OrderedDict, namedtuple + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Any + + from pip._internal.cli.base_command import Command + + +CommandInfo = namedtuple('CommandInfo', 'module_path, class_name, summary') + +# The ordering matters for help display. +# Also, even though the module path starts with the same +# "pip._internal.commands" prefix in each case, we include the full path +# because it makes testing easier (specifically when modifying commands_dict +# in test setup / teardown by adding info for a FakeCommand class defined +# in a test-related module). +# Finally, we need to pass an iterable of pairs here rather than a dict +# so that the ordering won't be lost when using Python 2.7. +commands_dict = OrderedDict([ + ('install', CommandInfo( + 'pip._internal.commands.install', 'InstallCommand', + 'Install packages.', + )), + ('download', CommandInfo( + 'pip._internal.commands.download', 'DownloadCommand', + 'Download packages.', + )), + ('uninstall', CommandInfo( + 'pip._internal.commands.uninstall', 'UninstallCommand', + 'Uninstall packages.', + )), + ('freeze', CommandInfo( + 'pip._internal.commands.freeze', 'FreezeCommand', + 'Output installed packages in requirements format.', + )), + ('list', CommandInfo( + 'pip._internal.commands.list', 'ListCommand', + 'List installed packages.', + )), + ('show', CommandInfo( + 'pip._internal.commands.show', 'ShowCommand', + 'Show information about installed packages.', + )), + ('check', CommandInfo( + 'pip._internal.commands.check', 'CheckCommand', + 'Verify installed packages have compatible dependencies.', + )), + ('config', CommandInfo( + 'pip._internal.commands.configuration', 'ConfigurationCommand', + 'Manage local and global configuration.', + )), + ('search', CommandInfo( + 'pip._internal.commands.search', 'SearchCommand', + 'Search PyPI for packages.', + )), + ('cache', CommandInfo( + 'pip._internal.commands.cache', 'CacheCommand', + "Inspect and manage pip's wheel cache.", + )), + ('wheel', CommandInfo( + 'pip._internal.commands.wheel', 'WheelCommand', + 'Build wheels from your requirements.', + )), + ('hash', CommandInfo( + 'pip._internal.commands.hash', 'HashCommand', + 'Compute hashes of package archives.', + )), + ('completion', CommandInfo( + 'pip._internal.commands.completion', 'CompletionCommand', + 'A helper command used for command completion.', + )), + ('debug', CommandInfo( + 'pip._internal.commands.debug', 'DebugCommand', + 'Show information useful for debugging.', + )), + ('help', CommandInfo( + 'pip._internal.commands.help', 'HelpCommand', + 'Show help for commands.', + )), +]) # type: OrderedDict[str, CommandInfo] + + +def create_command(name, **kwargs): + # type: (str, **Any) -> Command + """ + Create an instance of the Command class with the given name. + """ + module_path, class_name, summary = commands_dict[name] + module = importlib.import_module(module_path) + command_class = getattr(module, class_name) + command = command_class(name=name, summary=summary, **kwargs) + + return command + + +def get_similar_commands(name): + """Command name auto-correct.""" + from difflib import get_close_matches + + name = name.lower() + + close_commands = get_close_matches(name, commands_dict.keys()) + + if close_commands: + return close_commands[0] + else: + return False diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/cache.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/cache.py new file mode 100644 index 0000000000..ec21be68fb --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/cache.py @@ -0,0 +1,234 @@ +from __future__ import absolute_import + +import logging +import os +import textwrap + +import pip._internal.utils.filesystem as filesystem +from pip._internal.cli.base_command import Command +from pip._internal.cli.status_codes import ERROR, SUCCESS +from pip._internal.exceptions import CommandError, PipError +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from optparse import Values + from typing import Any, List + + +logger = logging.getLogger(__name__) + + +class CacheCommand(Command): + """ + Inspect and manage pip's wheel cache. + + Subcommands: + + - dir: Show the cache directory. + - info: Show information about the cache. + - list: List filenames of packages stored in the cache. + - remove: Remove one or more package from the cache. + - purge: Remove all items from the cache. + + ```` can be a glob expression or a package name. + """ + + ignore_require_venv = True + usage = """ + %prog dir + %prog info + %prog list [] [--format=[human, abspath]] + %prog remove + %prog purge + """ + + def add_options(self): + # type: () -> None + + self.cmd_opts.add_option( + '--format', + action='store', + dest='list_format', + default="human", + choices=('human', 'abspath'), + help="Select the output format among: human (default) or abspath" + ) + + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options, args): + # type: (Values, List[Any]) -> int + handlers = { + "dir": self.get_cache_dir, + "info": self.get_cache_info, + "list": self.list_cache_items, + "remove": self.remove_cache_items, + "purge": self.purge_cache, + } + + if not options.cache_dir: + logger.error("pip cache commands can not " + "function since cache is disabled.") + return ERROR + + # Determine action + if not args or args[0] not in handlers: + logger.error( + "Need an action (%s) to perform.", + ", ".join(sorted(handlers)), + ) + return ERROR + + action = args[0] + + # Error handling happens here, not in the action-handlers. + try: + handlers[action](options, args[1:]) + except PipError as e: + logger.error(e.args[0]) + return ERROR + + return SUCCESS + + def get_cache_dir(self, options, args): + # type: (Values, List[Any]) -> None + if args: + raise CommandError('Too many arguments') + + logger.info(options.cache_dir) + + def get_cache_info(self, options, args): + # type: (Values, List[Any]) -> None + if args: + raise CommandError('Too many arguments') + + num_http_files = len(self._find_http_files(options)) + num_packages = len(self._find_wheels(options, '*')) + + http_cache_location = self._cache_dir(options, 'http') + wheels_cache_location = self._cache_dir(options, 'wheels') + http_cache_size = filesystem.format_directory_size(http_cache_location) + wheels_cache_size = filesystem.format_directory_size( + wheels_cache_location + ) + + message = textwrap.dedent(""" + Package index page cache location: {http_cache_location} + Package index page cache size: {http_cache_size} + Number of HTTP files: {num_http_files} + Wheels location: {wheels_cache_location} + Wheels size: {wheels_cache_size} + Number of wheels: {package_count} + """).format( + http_cache_location=http_cache_location, + http_cache_size=http_cache_size, + num_http_files=num_http_files, + wheels_cache_location=wheels_cache_location, + package_count=num_packages, + wheels_cache_size=wheels_cache_size, + ).strip() + + logger.info(message) + + def list_cache_items(self, options, args): + # type: (Values, List[Any]) -> None + if len(args) > 1: + raise CommandError('Too many arguments') + + if args: + pattern = args[0] + else: + pattern = '*' + + files = self._find_wheels(options, pattern) + if options.list_format == 'human': + self.format_for_human(files) + else: + self.format_for_abspath(files) + + def format_for_human(self, files): + # type: (List[str]) -> None + if not files: + logger.info('Nothing cached.') + return + + results = [] + for filename in files: + wheel = os.path.basename(filename) + size = filesystem.format_file_size(filename) + results.append(' - {} ({})'.format(wheel, size)) + logger.info('Cache contents:\n') + logger.info('\n'.join(sorted(results))) + + def format_for_abspath(self, files): + # type: (List[str]) -> None + if not files: + return + + results = [] + for filename in files: + results.append(filename) + + logger.info('\n'.join(sorted(results))) + + def remove_cache_items(self, options, args): + # type: (Values, List[Any]) -> None + if len(args) > 1: + raise CommandError('Too many arguments') + + if not args: + raise CommandError('Please provide a pattern') + + files = self._find_wheels(options, args[0]) + + # Only fetch http files if no specific pattern given + if args[0] == '*': + files += self._find_http_files(options) + + if not files: + raise CommandError('No matching packages') + + for filename in files: + os.unlink(filename) + logger.debug('Removed %s', filename) + logger.info('Files removed: %s', len(files)) + + def purge_cache(self, options, args): + # type: (Values, List[Any]) -> None + if args: + raise CommandError('Too many arguments') + + return self.remove_cache_items(options, ['*']) + + def _cache_dir(self, options, subdir): + # type: (Values, str) -> str + return os.path.join(options.cache_dir, subdir) + + def _find_http_files(self, options): + # type: (Values) -> List[str] + http_dir = self._cache_dir(options, 'http') + return filesystem.find_files(http_dir, '*') + + def _find_wheels(self, options, pattern): + # type: (Values, str) -> List[str] + wheel_dir = self._cache_dir(options, 'wheels') + + # The wheel filename format, as specified in PEP 427, is: + # {distribution}-{version}(-{build})?-{python}-{abi}-{platform}.whl + # + # Additionally, non-alphanumeric values in the distribution are + # normalized to underscores (_), meaning hyphens can never occur + # before `-{version}`. + # + # Given that information: + # - If the pattern we're given contains a hyphen (-), the user is + # providing at least the version. Thus, we can just append `*.whl` + # to match the rest of it. + # - If the pattern we're given doesn't contain a hyphen (-), the + # user is only providing the name. Thus, we append `-*.whl` to + # match the hyphen before the version, followed by anything else. + # + # PEP 427: https://www.python.org/dev/peps/pep-0427/ + pattern = pattern + ("*.whl" if "-" in pattern else "-*.whl") + + return filesystem.find_files(wheel_dir, pattern) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/check.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/check.py new file mode 100644 index 0000000000..e066bb63c7 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/check.py @@ -0,0 +1,51 @@ +import logging + +from pip._internal.cli.base_command import Command +from pip._internal.cli.status_codes import ERROR, SUCCESS +from pip._internal.operations.check import ( + check_package_set, + create_package_set_from_installed, +) +from pip._internal.utils.misc import write_output +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +logger = logging.getLogger(__name__) + +if MYPY_CHECK_RUNNING: + from optparse import Values + from typing import Any, List + + +class CheckCommand(Command): + """Verify installed packages have compatible dependencies.""" + + usage = """ + %prog [options]""" + + def run(self, options, args): + # type: (Values, List[Any]) -> int + + package_set, parsing_probs = create_package_set_from_installed() + missing, conflicting = check_package_set(package_set) + + for project_name in missing: + version = package_set[project_name].version + for dependency in missing[project_name]: + write_output( + "%s %s requires %s, which is not installed.", + project_name, version, dependency[0], + ) + + for project_name in conflicting: + version = package_set[project_name].version + for dep_name, dep_version, req in conflicting[project_name]: + write_output( + "%s %s has requirement %s, but you have %s %s.", + project_name, version, req, dep_name, dep_version, + ) + + if missing or conflicting or parsing_probs: + return ERROR + else: + write_output("No broken requirements found.") + return SUCCESS diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/completion.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/completion.py new file mode 100644 index 0000000000..b19f1ed1a5 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/completion.py @@ -0,0 +1,98 @@ +from __future__ import absolute_import + +import sys +import textwrap + +from pip._internal.cli.base_command import Command +from pip._internal.cli.status_codes import SUCCESS +from pip._internal.utils.misc import get_prog +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from optparse import Values + from typing import List + +BASE_COMPLETION = """ +# pip {shell} completion start{script}# pip {shell} completion end +""" + +COMPLETION_SCRIPTS = { + 'bash': """ + _pip_completion() + {{ + COMPREPLY=( $( COMP_WORDS="${{COMP_WORDS[*]}}" \\ + COMP_CWORD=$COMP_CWORD \\ + PIP_AUTO_COMPLETE=1 $1 2>/dev/null ) ) + }} + complete -o default -F _pip_completion {prog} + """, + 'zsh': """ + function _pip_completion {{ + local words cword + read -Ac words + read -cn cword + reply=( $( COMP_WORDS="$words[*]" \\ + COMP_CWORD=$(( cword-1 )) \\ + PIP_AUTO_COMPLETE=1 $words[1] 2>/dev/null )) + }} + compctl -K _pip_completion {prog} + """, + 'fish': """ + function __fish_complete_pip + set -lx COMP_WORDS (commandline -o) "" + set -lx COMP_CWORD ( \\ + math (contains -i -- (commandline -t) $COMP_WORDS)-1 \\ + ) + set -lx PIP_AUTO_COMPLETE 1 + string split \\ -- (eval $COMP_WORDS[1]) + end + complete -fa "(__fish_complete_pip)" -c {prog} + """, +} + + +class CompletionCommand(Command): + """A helper command to be used for command completion.""" + + ignore_require_venv = True + + def add_options(self): + # type: () -> None + self.cmd_opts.add_option( + '--bash', '-b', + action='store_const', + const='bash', + dest='shell', + help='Emit completion code for bash') + self.cmd_opts.add_option( + '--zsh', '-z', + action='store_const', + const='zsh', + dest='shell', + help='Emit completion code for zsh') + self.cmd_opts.add_option( + '--fish', '-f', + action='store_const', + const='fish', + dest='shell', + help='Emit completion code for fish') + + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options, args): + # type: (Values, List[str]) -> int + """Prints the completion code of the given shell""" + shells = COMPLETION_SCRIPTS.keys() + shell_options = ['--' + shell for shell in sorted(shells)] + if options.shell in shells: + script = textwrap.dedent( + COMPLETION_SCRIPTS.get(options.shell, '').format( + prog=get_prog()) + ) + print(BASE_COMPLETION.format(script=script, shell=options.shell)) + return SUCCESS + else: + sys.stderr.write( + 'ERROR: You must pass {}\n' .format(' or '.join(shell_options)) + ) + return SUCCESS diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/configuration.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/configuration.py new file mode 100644 index 0000000000..1ab90b47b4 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/configuration.py @@ -0,0 +1,280 @@ +import logging +import os +import subprocess + +from pip._internal.cli.base_command import Command +from pip._internal.cli.status_codes import ERROR, SUCCESS +from pip._internal.configuration import Configuration, get_configuration_files, kinds +from pip._internal.exceptions import PipError +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import get_prog, write_output +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from optparse import Values + from typing import Any, List, Optional + + from pip._internal.configuration import Kind + +logger = logging.getLogger(__name__) + + +class ConfigurationCommand(Command): + """ + Manage local and global configuration. + + Subcommands: + + - list: List the active configuration (or from the file specified) + - edit: Edit the configuration file in an editor + - get: Get the value associated with name + - set: Set the name=value + - unset: Unset the value associated with name + - debug: List the configuration files and values defined under them + + If none of --user, --global and --site are passed, a virtual + environment configuration file is used if one is active and the file + exists. Otherwise, all modifications happen on the to the user file by + default. + """ + + ignore_require_venv = True + usage = """ + %prog [] list + %prog [] [--editor ] edit + + %prog [] get name + %prog [] set name value + %prog [] unset name + %prog [] debug + """ + + def add_options(self): + # type: () -> None + self.cmd_opts.add_option( + '--editor', + dest='editor', + action='store', + default=None, + help=( + 'Editor to use to edit the file. Uses VISUAL or EDITOR ' + 'environment variables if not provided.' + ) + ) + + self.cmd_opts.add_option( + '--global', + dest='global_file', + action='store_true', + default=False, + help='Use the system-wide configuration file only' + ) + + self.cmd_opts.add_option( + '--user', + dest='user_file', + action='store_true', + default=False, + help='Use the user configuration file only' + ) + + self.cmd_opts.add_option( + '--site', + dest='site_file', + action='store_true', + default=False, + help='Use the current environment configuration file only' + ) + + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options, args): + # type: (Values, List[str]) -> int + handlers = { + "list": self.list_values, + "edit": self.open_in_editor, + "get": self.get_name, + "set": self.set_name_value, + "unset": self.unset_name, + "debug": self.list_config_values, + } + + # Determine action + if not args or args[0] not in handlers: + logger.error( + "Need an action (%s) to perform.", + ", ".join(sorted(handlers)), + ) + return ERROR + + action = args[0] + + # Determine which configuration files are to be loaded + # Depends on whether the command is modifying. + try: + load_only = self._determine_file( + options, need_value=(action in ["get", "set", "unset", "edit"]) + ) + except PipError as e: + logger.error(e.args[0]) + return ERROR + + # Load a new configuration + self.configuration = Configuration( + isolated=options.isolated_mode, load_only=load_only + ) + self.configuration.load() + + # Error handling happens here, not in the action-handlers. + try: + handlers[action](options, args[1:]) + except PipError as e: + logger.error(e.args[0]) + return ERROR + + return SUCCESS + + def _determine_file(self, options, need_value): + # type: (Values, bool) -> Optional[Kind] + file_options = [key for key, value in ( + (kinds.USER, options.user_file), + (kinds.GLOBAL, options.global_file), + (kinds.SITE, options.site_file), + ) if value] + + if not file_options: + if not need_value: + return None + # Default to user, unless there's a site file. + elif any( + os.path.exists(site_config_file) + for site_config_file in get_configuration_files()[kinds.SITE] + ): + return kinds.SITE + else: + return kinds.USER + elif len(file_options) == 1: + return file_options[0] + + raise PipError( + "Need exactly one file to operate upon " + "(--user, --site, --global) to perform." + ) + + def list_values(self, options, args): + # type: (Values, List[str]) -> None + self._get_n_args(args, "list", n=0) + + for key, value in sorted(self.configuration.items()): + write_output("%s=%r", key, value) + + def get_name(self, options, args): + # type: (Values, List[str]) -> None + key = self._get_n_args(args, "get [name]", n=1) + value = self.configuration.get_value(key) + + write_output("%s", value) + + def set_name_value(self, options, args): + # type: (Values, List[str]) -> None + key, value = self._get_n_args(args, "set [name] [value]", n=2) + self.configuration.set_value(key, value) + + self._save_configuration() + + def unset_name(self, options, args): + # type: (Values, List[str]) -> None + key = self._get_n_args(args, "unset [name]", n=1) + self.configuration.unset_value(key) + + self._save_configuration() + + def list_config_values(self, options, args): + # type: (Values, List[str]) -> None + """List config key-value pairs across different config files""" + self._get_n_args(args, "debug", n=0) + + self.print_env_var_values() + # Iterate over config files and print if they exist, and the + # key-value pairs present in them if they do + for variant, files in sorted(self.configuration.iter_config_files()): + write_output("%s:", variant) + for fname in files: + with indent_log(): + file_exists = os.path.exists(fname) + write_output("%s, exists: %r", + fname, file_exists) + if file_exists: + self.print_config_file_values(variant) + + def print_config_file_values(self, variant): + # type: (Kind) -> None + """Get key-value pairs from the file of a variant""" + for name, value in self.configuration.\ + get_values_in_config(variant).items(): + with indent_log(): + write_output("%s: %s", name, value) + + def print_env_var_values(self): + # type: () -> None + """Get key-values pairs present as environment variables""" + write_output("%s:", 'env_var') + with indent_log(): + for key, value in sorted(self.configuration.get_environ_vars()): + env_var = 'PIP_{}'.format(key.upper()) + write_output("%s=%r", env_var, value) + + def open_in_editor(self, options, args): + # type: (Values, List[str]) -> None + editor = self._determine_editor(options) + + fname = self.configuration.get_file_to_edit() + if fname is None: + raise PipError("Could not determine appropriate file.") + + try: + subprocess.check_call([editor, fname]) + except subprocess.CalledProcessError as e: + raise PipError( + "Editor Subprocess exited with exit code {}" + .format(e.returncode) + ) + + def _get_n_args(self, args, example, n): + # type: (List[str], str, int) -> Any + """Helper to make sure the command got the right number of arguments + """ + if len(args) != n: + msg = ( + 'Got unexpected number of arguments, expected {}. ' + '(example: "{} config {}")' + ).format(n, get_prog(), example) + raise PipError(msg) + + if n == 1: + return args[0] + else: + return args + + def _save_configuration(self): + # type: () -> None + # We successfully ran a modifying command. Need to save the + # configuration. + try: + self.configuration.save() + except Exception: + logger.exception( + "Unable to save configuration. Please report this as a bug." + ) + raise PipError("Internal Error.") + + def _determine_editor(self, options): + # type: (Values) -> str + if options.editor is not None: + return options.editor + elif "VISUAL" in os.environ: + return os.environ["VISUAL"] + elif "EDITOR" in os.environ: + return os.environ["EDITOR"] + else: + raise PipError("Could not determine editor to use.") diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/debug.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/debug.py new file mode 100644 index 0000000000..0ccc63af26 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/debug.py @@ -0,0 +1,251 @@ +from __future__ import absolute_import + +import locale +import logging +import os +import sys + +import pip._vendor +from pip._vendor import pkg_resources +from pip._vendor.certifi import where + +from pip import __file__ as pip_location +from pip._internal.cli import cmdoptions +from pip._internal.cli.base_command import Command +from pip._internal.cli.cmdoptions import make_target_python +from pip._internal.cli.status_codes import SUCCESS +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import get_pip_version +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from optparse import Values + from types import ModuleType + from typing import Dict, List, Optional + + from pip._internal.configuration import Configuration + +logger = logging.getLogger(__name__) + + +def show_value(name, value): + # type: (str, Optional[str]) -> None + logger.info('%s: %s', name, value) + + +def show_sys_implementation(): + # type: () -> None + logger.info('sys.implementation:') + if hasattr(sys, 'implementation'): + implementation = sys.implementation # type: ignore + implementation_name = implementation.name + else: + implementation_name = '' + + with indent_log(): + show_value('name', implementation_name) + + +def create_vendor_txt_map(): + # type: () -> Dict[str, str] + vendor_txt_path = os.path.join( + os.path.dirname(pip_location), + '_vendor', + 'vendor.txt' + ) + + with open(vendor_txt_path) as f: + # Purge non version specifying lines. + # Also, remove any space prefix or suffixes (including comments). + lines = [line.strip().split(' ', 1)[0] + for line in f.readlines() if '==' in line] + + # Transform into "module" -> version dict. + return dict(line.split('==', 1) for line in lines) # type: ignore + +def create_debundle_txt_map(): + # type: () -> Dict[str, str] + wheels = [fn for fn in os.listdir(pip._vendor.WHEEL_DIR)] + # Transform into "module" -> version dict. + return dict((wheel.split('-')[0], wheel.split('-')[1]) for wheel in wheels) # type: ignore + +def get_module_from_module_name(module_name): + # type: (str) -> ModuleType + # Module name can be uppercase in vendor.txt for some reason... + module_name = module_name.lower() + # PATCH: setuptools is actually only pkg_resources. + if module_name == 'setuptools': + module_name = 'pkg_resources' + + __import__( + 'pip._vendor.{}'.format(module_name), + globals(), + locals(), + level=0 + ) + return getattr(pip._vendor, module_name) + + +def get_vendor_version_from_module(module_name): + # type: (str) -> Optional[str] + module = get_module_from_module_name(module_name) + version = getattr(module, '__version__', None) + + if not version: + # Try to find version in debundled module info + # The type for module.__file__ is Optional[str] in + # Python 2, and str in Python 3. The type: ignore is + # added to account for Python 2, instead of a cast + # and should be removed once we drop Python 2 support + pkg_set = pkg_resources.WorkingSet( + [os.path.dirname(module.__file__)] # type: ignore + ) + package = pkg_set.find(pkg_resources.Requirement.parse(module_name)) + version = getattr(package, 'version', None) + + return version + + +def show_actual_vendor_versions(vendor_txt_versions): + # type: (Dict[str, str]) -> None + """Log the actual version and print extra info if there is + a conflict or if the actual version could not be imported. + """ + for module_name, expected_version in vendor_txt_versions.items(): + extra_message = '' + actual_version = get_vendor_version_from_module(module_name) + if not actual_version: + extra_message = ' (Unable to locate actual module version, using'\ + ' vendor.txt specified version)' + actual_version = expected_version + elif actual_version != expected_version: + extra_message = ' (CONFLICT: vendor.txt suggests version should'\ + ' be {})'.format(expected_version) + logger.info('%s==%s%s', module_name, actual_version, extra_message) + + +def show_vendor_versions(): + # type: () -> None + logger.info('vendored library versions:') + + vendor_txt_versions = create_vendor_txt_map() + with indent_log(): + show_actual_vendor_versions(vendor_txt_versions) + +def show_debundled_versions(): + # type: () -> None + logger.info('debundled wheel versions:') + debundle_txt_versions = create_debundle_txt_map() + for module_name, installed_version in sorted(debundle_txt_versions.items()): + with indent_log(): + logger.info( + '{name}=={actual}'.format( + name=module_name, + actual=installed_version, + ) + ) + +def show_tags(options): + # type: (Values) -> None + tag_limit = 10 + + target_python = make_target_python(options) + tags = target_python.get_tags() + + # Display the target options that were explicitly provided. + formatted_target = target_python.format_given() + suffix = '' + if formatted_target: + suffix = ' (target: {})'.format(formatted_target) + + msg = 'Compatible tags: {}{}'.format(len(tags), suffix) + logger.info(msg) + + if options.verbose < 1 and len(tags) > tag_limit: + tags_limited = True + tags = tags[:tag_limit] + else: + tags_limited = False + + with indent_log(): + for tag in tags: + logger.info(str(tag)) + + if tags_limited: + msg = ( + '...\n' + '[First {tag_limit} tags shown. Pass --verbose to show all.]' + ).format(tag_limit=tag_limit) + logger.info(msg) + + +def ca_bundle_info(config): + # type: (Configuration) -> str + levels = set() + for key, _ in config.items(): + levels.add(key.split('.')[0]) + + if not levels: + return "Not specified" + + levels_that_override_global = ['install', 'wheel', 'download'] + global_overriding_level = [ + level for level in levels if level in levels_that_override_global + ] + if not global_overriding_level: + return 'global' + + if 'global' in levels: + levels.remove('global') + return ", ".join(levels) + + +class DebugCommand(Command): + """ + Display debug information. + """ + + usage = """ + %prog """ + ignore_require_venv = True + + def add_options(self): + # type: () -> None + cmdoptions.add_target_python_options(self.cmd_opts) + self.parser.insert_option_group(0, self.cmd_opts) + self.parser.config.load() + + def run(self, options, args): + # type: (Values, List[str]) -> int + logger.warning( + "This command is only meant for debugging. " + "Do not use this with automation for parsing and getting these " + "details, since the output and options of this command may " + "change without notice." + ) + show_value('pip version', get_pip_version()) + show_value('sys.version', sys.version) + show_value('sys.executable', sys.executable) + show_value('sys.getdefaultencoding', sys.getdefaultencoding()) + show_value('sys.getfilesystemencoding', sys.getfilesystemencoding()) + show_value( + 'locale.getpreferredencoding', locale.getpreferredencoding(), + ) + show_value('sys.platform', sys.platform) + show_sys_implementation() + + show_value("'cert' config value", ca_bundle_info(self.parser.config)) + show_value("REQUESTS_CA_BUNDLE", os.environ.get('REQUESTS_CA_BUNDLE')) + show_value("CURL_CA_BUNDLE", os.environ.get('CURL_CA_BUNDLE')) + show_value("pip._vendor.certifi.where()", where()) + show_value("pip._vendor.DEBUNDLED", pip._vendor.DEBUNDLED) + + if not pip._vendor.DEBUNDLED: + show_vendor_versions() + else: + show_value("pip._vendor.WHEEL_DIR", pip._vendor.WHEEL_DIR) + show_debundled_versions() + + show_tags(options) + + return SUCCESS diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/download.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/download.py new file mode 100644 index 0000000000..7405870aef --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/download.py @@ -0,0 +1,143 @@ +from __future__ import absolute_import + +import logging +import os + +from pip._internal.cli import cmdoptions +from pip._internal.cli.cmdoptions import make_target_python +from pip._internal.cli.req_command import RequirementCommand, with_cleanup +from pip._internal.cli.status_codes import SUCCESS +from pip._internal.req.req_tracker import get_requirement_tracker +from pip._internal.utils.misc import ensure_dir, normalize_path, write_output +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from optparse import Values + from typing import List + +logger = logging.getLogger(__name__) + + +class DownloadCommand(RequirementCommand): + """ + Download packages from: + + - PyPI (and other indexes) using requirement specifiers. + - VCS project urls. + - Local project directories. + - Local or remote source archives. + + pip also supports downloading from "requirements files", which provide + an easy way to specify a whole environment to be downloaded. + """ + + usage = """ + %prog [options] [package-index-options] ... + %prog [options] -r [package-index-options] ... + %prog [options] ... + %prog [options] ... + %prog [options] ...""" + + def add_options(self): + # type: () -> None + self.cmd_opts.add_option(cmdoptions.constraints()) + self.cmd_opts.add_option(cmdoptions.requirements()) + self.cmd_opts.add_option(cmdoptions.build_dir()) + self.cmd_opts.add_option(cmdoptions.no_deps()) + self.cmd_opts.add_option(cmdoptions.global_options()) + self.cmd_opts.add_option(cmdoptions.no_binary()) + self.cmd_opts.add_option(cmdoptions.only_binary()) + self.cmd_opts.add_option(cmdoptions.prefer_binary()) + self.cmd_opts.add_option(cmdoptions.src()) + self.cmd_opts.add_option(cmdoptions.pre()) + self.cmd_opts.add_option(cmdoptions.require_hashes()) + self.cmd_opts.add_option(cmdoptions.progress_bar()) + self.cmd_opts.add_option(cmdoptions.no_build_isolation()) + self.cmd_opts.add_option(cmdoptions.use_pep517()) + self.cmd_opts.add_option(cmdoptions.no_use_pep517()) + + self.cmd_opts.add_option( + '-d', '--dest', '--destination-dir', '--destination-directory', + dest='download_dir', + metavar='dir', + default=os.curdir, + help=("Download packages into ."), + ) + + cmdoptions.add_target_python_options(self.cmd_opts) + + index_opts = cmdoptions.make_option_group( + cmdoptions.index_group, + self.parser, + ) + + self.parser.insert_option_group(0, index_opts) + self.parser.insert_option_group(0, self.cmd_opts) + + @with_cleanup + def run(self, options, args): + # type: (Values, List[str]) -> int + + options.ignore_installed = True + # editable doesn't really make sense for `pip download`, but the bowels + # of the RequirementSet code require that property. + options.editables = [] + + cmdoptions.check_dist_restriction(options) + + options.download_dir = normalize_path(options.download_dir) + ensure_dir(options.download_dir) + + session = self.get_default_session(options) + + target_python = make_target_python(options) + finder = self._build_package_finder( + options=options, + session=session, + target_python=target_python, + ) + + req_tracker = self.enter_context(get_requirement_tracker()) + + directory = TempDirectory( + delete=not options.no_clean, + kind="download", + globally_managed=True, + ) + + reqs = self.get_requirements(args, options, finder, session) + + preparer = self.make_requirement_preparer( + temp_build_dir=directory, + options=options, + req_tracker=req_tracker, + session=session, + finder=finder, + download_dir=options.download_dir, + use_user_site=False, + ) + + resolver = self.make_resolver( + preparer=preparer, + finder=finder, + options=options, + py_version_info=options.python_version, + ) + + self.trace_basic_info(finder) + + requirement_set = resolver.resolve( + reqs, check_supported_wheels=True + ) + + downloaded = [] # type: List[str] + for req in requirement_set.requirements.values(): + if req.satisfied_by is None: + assert req.name is not None + preparer.save_linked_requirement(req) + downloaded.append(req.name) + if downloaded: + write_output('Successfully downloaded %s', ' '.join(downloaded)) + + return SUCCESS diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/freeze.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/freeze.py new file mode 100644 index 0000000000..5f71bd305c --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/freeze.py @@ -0,0 +1,116 @@ +from __future__ import absolute_import + +import sys + +from pip._internal.cache import WheelCache +from pip._internal.cli import cmdoptions +from pip._internal.cli.base_command import Command +from pip._internal.cli.status_codes import SUCCESS +from pip._internal.models.format_control import FormatControl +from pip._internal.operations.freeze import freeze +from pip._internal.utils.compat import stdlib_pkgs +from pip._internal.utils.deprecation import deprecated +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +DEV_PKGS = {'pip', 'setuptools', 'distribute', 'wheel', 'pkg-resources'} + +if MYPY_CHECK_RUNNING: + from optparse import Values + from typing import List + + +class FreezeCommand(Command): + """ + Output installed packages in requirements format. + + packages are listed in a case-insensitive sorted order. + """ + + usage = """ + %prog [options]""" + log_streams = ("ext://sys.stderr", "ext://sys.stderr") + + def add_options(self): + # type: () -> None + self.cmd_opts.add_option( + '-r', '--requirement', + dest='requirements', + action='append', + default=[], + metavar='file', + help="Use the order in the given requirements file and its " + "comments when generating output. This option can be " + "used multiple times.") + self.cmd_opts.add_option( + '-f', '--find-links', + dest='find_links', + action='append', + default=[], + metavar='URL', + help='URL for finding packages, which will be added to the ' + 'output.') + self.cmd_opts.add_option( + '-l', '--local', + dest='local', + action='store_true', + default=False, + help='If in a virtualenv that has global access, do not output ' + 'globally-installed packages.') + self.cmd_opts.add_option( + '--user', + dest='user', + action='store_true', + default=False, + help='Only output packages installed in user-site.') + self.cmd_opts.add_option(cmdoptions.list_path()) + self.cmd_opts.add_option( + '--all', + dest='freeze_all', + action='store_true', + help='Do not skip these packages in the output:' + ' {}'.format(', '.join(DEV_PKGS))) + self.cmd_opts.add_option( + '--exclude-editable', + dest='exclude_editable', + action='store_true', + help='Exclude editable package from output.') + self.cmd_opts.add_option(cmdoptions.list_exclude()) + + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options, args): + # type: (Values, List[str]) -> int + format_control = FormatControl(set(), set()) + wheel_cache = WheelCache(options.cache_dir, format_control) + skip = set(stdlib_pkgs) + if not options.freeze_all: + skip.update(DEV_PKGS) + + if options.excludes: + skip.update(options.excludes) + + cmdoptions.check_list_path_option(options) + + if options.find_links: + deprecated( + "--find-links option in pip freeze is deprecated.", + replacement=None, + gone_in="21.2", + issue=9069, + ) + + freeze_kwargs = dict( + requirement=options.requirements, + find_links=options.find_links, + local_only=options.local, + user_only=options.user, + paths=options.path, + isolated=options.isolated_mode, + wheel_cache=wheel_cache, + skip=skip, + exclude_editable=options.exclude_editable, + ) + + for line in freeze(**freeze_kwargs): + sys.stdout.write(line + '\n') + return SUCCESS diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/hash.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/hash.py new file mode 100644 index 0000000000..37831c3952 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/hash.py @@ -0,0 +1,63 @@ +from __future__ import absolute_import + +import hashlib +import logging +import sys + +from pip._internal.cli.base_command import Command +from pip._internal.cli.status_codes import ERROR, SUCCESS +from pip._internal.utils.hashes import FAVORITE_HASH, STRONG_HASHES +from pip._internal.utils.misc import read_chunks, write_output +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from optparse import Values + from typing import List + +logger = logging.getLogger(__name__) + + +class HashCommand(Command): + """ + Compute a hash of a local package archive. + + These can be used with --hash in a requirements file to do repeatable + installs. + """ + + usage = '%prog [options] ...' + ignore_require_venv = True + + def add_options(self): + # type: () -> None + self.cmd_opts.add_option( + '-a', '--algorithm', + dest='algorithm', + choices=STRONG_HASHES, + action='store', + default=FAVORITE_HASH, + help='The hash algorithm to use: one of {}'.format( + ', '.join(STRONG_HASHES))) + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options, args): + # type: (Values, List[str]) -> int + if not args: + self.parser.print_usage(sys.stderr) + return ERROR + + algorithm = options.algorithm + for path in args: + write_output('%s:\n--hash=%s:%s', + path, algorithm, _hash_of_file(path, algorithm)) + return SUCCESS + + +def _hash_of_file(path, algorithm): + # type: (str, str) -> str + """Return the hash digest of a file.""" + with open(path, 'rb') as archive: + hash = hashlib.new(algorithm) + for chunk in read_chunks(archive): + hash.update(chunk) + return hash.hexdigest() diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/help.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/help.py new file mode 100644 index 0000000000..2ab2b6d8f2 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/help.py @@ -0,0 +1,46 @@ +from __future__ import absolute_import + +from pip._internal.cli.base_command import Command +from pip._internal.cli.status_codes import SUCCESS +from pip._internal.exceptions import CommandError +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from optparse import Values + from typing import List + + +class HelpCommand(Command): + """Show help for commands""" + + usage = """ + %prog """ + ignore_require_venv = True + + def run(self, options, args): + # type: (Values, List[str]) -> int + from pip._internal.commands import ( + commands_dict, + create_command, + get_similar_commands, + ) + + try: + # 'pip help' with no args is handled by pip.__init__.parseopt() + cmd_name = args[0] # the command we need help for + except IndexError: + return SUCCESS + + if cmd_name not in commands_dict: + guess = get_similar_commands(cmd_name) + + msg = ['unknown command "{}"'.format(cmd_name)] + if guess: + msg.append('maybe you meant "{}"'.format(guess)) + + raise CommandError(' - '.join(msg)) + + command = create_command(cmd_name) + command.parser.print_help() + + return SUCCESS diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/install.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/install.py new file mode 100644 index 0000000000..d91af88834 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/install.py @@ -0,0 +1,763 @@ +from __future__ import absolute_import + +import errno +import logging +import operator +import os +import shutil +import site +from optparse import SUPPRESS_HELP + +from pip._vendor import pkg_resources +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.cache import WheelCache +from pip._internal.cli import cmdoptions +from pip._internal.cli.cmdoptions import make_target_python +from pip._internal.cli.req_command import RequirementCommand, with_cleanup +from pip._internal.cli.status_codes import ERROR, SUCCESS +from pip._internal.exceptions import CommandError, InstallationError +from pip._internal.locations import distutils_scheme +from pip._internal.operations.check import check_install_conflicts +from pip._internal.req import install_given_reqs +from pip._internal.req.req_tracker import get_requirement_tracker +from pip._internal.utils.distutils_args import parse_distutils_args +from pip._internal.utils.filesystem import test_writable_dir +from pip._internal.utils.misc import ( + ensure_dir, + get_installed_version, + get_pip_version, + protect_pip_from_modification_on_windows, + write_output, +) +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.virtualenv import virtualenv_no_global +from pip._internal.wheel_builder import build, should_build_for_install_command + +if MYPY_CHECK_RUNNING: + from optparse import Values + from typing import Iterable, List, Optional + + from pip._internal.models.format_control import FormatControl + from pip._internal.operations.check import ConflictDetails + from pip._internal.req.req_install import InstallRequirement + from pip._internal.wheel_builder import BinaryAllowedPredicate + +from pip._internal.locations import running_under_virtualenv + +logger = logging.getLogger(__name__) + + +def get_check_binary_allowed(format_control): + # type: (FormatControl) -> BinaryAllowedPredicate + def check_binary_allowed(req): + # type: (InstallRequirement) -> bool + if req.use_pep517: + return True + canonical_name = canonicalize_name(req.name) + allowed_formats = format_control.get_allowed_formats(canonical_name) + return "binary" in allowed_formats + + return check_binary_allowed + + +class InstallCommand(RequirementCommand): + """ + Install packages from: + + - PyPI (and other indexes) using requirement specifiers. + - VCS project urls. + - Local project directories. + - Local or remote source archives. + + pip also supports installing from "requirements files", which provide + an easy way to specify a whole environment to be installed. + """ + + usage = """ + %prog [options] [package-index-options] ... + %prog [options] -r [package-index-options] ... + %prog [options] [-e] ... + %prog [options] [-e] ... + %prog [options] ...""" + + def add_options(self): + # type: () -> None + self.cmd_opts.add_option(cmdoptions.requirements()) + self.cmd_opts.add_option(cmdoptions.constraints()) + self.cmd_opts.add_option(cmdoptions.no_deps()) + self.cmd_opts.add_option(cmdoptions.pre()) + + self.cmd_opts.add_option(cmdoptions.editable()) + self.cmd_opts.add_option( + '-t', '--target', + dest='target_dir', + metavar='dir', + default=None, + help='Install packages into . ' + 'By default this will not replace existing files/folders in ' + '. Use --upgrade to replace existing packages in ' + 'with new versions.' + ) + cmdoptions.add_target_python_options(self.cmd_opts) + + self.cmd_opts.add_option( + '--user', + dest='use_user_site', + action='store_true', + help="Install to the Python user install directory for your " + "platform. Typically ~/.local/, or %APPDATA%\\Python on " + "Windows. (See the Python documentation for site.USER_BASE " + "for full details.) On Debian systems, this is the " + "default when running outside of a virtual environment " + "and not as root.") + + self.cmd_opts.add_option( + '--no-user', + dest='use_system_location', + action='store_true', + help=SUPPRESS_HELP) + self.cmd_opts.add_option( + '--root', + dest='root_path', + metavar='dir', + default=None, + help="Install everything relative to this alternate root " + "directory.") + self.cmd_opts.add_option( + '--prefix', + dest='prefix_path', + metavar='dir', + default=None, + help="Installation prefix where lib, bin and other top-level " + "folders are placed") + + self.cmd_opts.add_option( + '--system', + dest='use_system_location', + action='store_true', + help="Install using the system scheme (overrides --user on " + "Debian systems)") + + self.cmd_opts.add_option(cmdoptions.build_dir()) + + self.cmd_opts.add_option(cmdoptions.src()) + + self.cmd_opts.add_option( + '-U', '--upgrade', + dest='upgrade', + action='store_true', + help='Upgrade all specified packages to the newest available ' + 'version. The handling of dependencies depends on the ' + 'upgrade-strategy used.' + ) + + self.cmd_opts.add_option( + '--upgrade-strategy', + dest='upgrade_strategy', + default='only-if-needed', + choices=['only-if-needed', 'eager'], + help='Determines how dependency upgrading should be handled ' + '[default: %default]. ' + '"eager" - dependencies are upgraded regardless of ' + 'whether the currently installed version satisfies the ' + 'requirements of the upgraded package(s). ' + '"only-if-needed" - are upgraded only when they do not ' + 'satisfy the requirements of the upgraded package(s).' + ) + + self.cmd_opts.add_option( + '--force-reinstall', + dest='force_reinstall', + action='store_true', + help='Reinstall all packages even if they are already ' + 'up-to-date.') + + self.cmd_opts.add_option( + '-I', '--ignore-installed', + dest='ignore_installed', + action='store_true', + help='Ignore the installed packages, overwriting them. ' + 'This can break your system if the existing package ' + 'is of a different version or was installed ' + 'with a different package manager!' + ) + + self.cmd_opts.add_option(cmdoptions.ignore_requires_python()) + self.cmd_opts.add_option(cmdoptions.no_build_isolation()) + self.cmd_opts.add_option(cmdoptions.use_pep517()) + self.cmd_opts.add_option(cmdoptions.no_use_pep517()) + + self.cmd_opts.add_option(cmdoptions.install_options()) + self.cmd_opts.add_option(cmdoptions.global_options()) + + self.cmd_opts.add_option( + "--compile", + action="store_true", + dest="compile", + default=True, + help="Compile Python source files to bytecode", + ) + + self.cmd_opts.add_option( + "--no-compile", + action="store_false", + dest="compile", + help="Do not compile Python source files to bytecode", + ) + + self.cmd_opts.add_option( + "--no-warn-script-location", + action="store_false", + dest="warn_script_location", + default=True, + help="Do not warn when installing scripts outside PATH", + ) + self.cmd_opts.add_option( + "--no-warn-conflicts", + action="store_false", + dest="warn_about_conflicts", + default=True, + help="Do not warn about broken dependencies", + ) + + self.cmd_opts.add_option(cmdoptions.no_binary()) + self.cmd_opts.add_option(cmdoptions.only_binary()) + self.cmd_opts.add_option(cmdoptions.prefer_binary()) + self.cmd_opts.add_option(cmdoptions.require_hashes()) + self.cmd_opts.add_option(cmdoptions.progress_bar()) + + index_opts = cmdoptions.make_option_group( + cmdoptions.index_group, + self.parser, + ) + + self.parser.insert_option_group(0, index_opts) + self.parser.insert_option_group(0, self.cmd_opts) + + @with_cleanup + def run(self, options, args): + # type: (Values, List[str]) -> int + if options.use_user_site and options.target_dir is not None: + raise CommandError("Can not combine '--user' and '--target'") + + cmdoptions.check_install_build_global(options) + upgrade_strategy = "to-satisfy-only" + if options.upgrade: + upgrade_strategy = options.upgrade_strategy + + cmdoptions.check_dist_restriction(options, check_target=True) + + if options.python_version: + python_versions = [options.python_version] + else: + python_versions = None + + # compute install location defaults + if (not options.use_user_site and not options.prefix_path and not + options.target_dir and not options.use_system_location): + if not running_under_virtualenv() and os.geteuid() != 0: + options.use_user_site = True + + if options.use_system_location: + options.use_user_site = False + + options.src_dir = os.path.abspath(options.src_dir) + install_options = options.install_options or [] + + logger.debug("Using %s", get_pip_version()) + options.use_user_site = decide_user_install( + options.use_user_site, + prefix_path=options.prefix_path, + target_dir=options.target_dir, + root_path=options.root_path, + isolated_mode=options.isolated_mode, + ) + + target_temp_dir = None # type: Optional[TempDirectory] + target_temp_dir_path = None # type: Optional[str] + if options.target_dir: + options.ignore_installed = True + options.target_dir = os.path.abspath(options.target_dir) + if (os.path.exists(options.target_dir) and not + os.path.isdir(options.target_dir)): + raise CommandError( + "Target path exists but is not a directory, will not " + "continue." + ) + + # Create a target directory for using with the target option + target_temp_dir = TempDirectory(kind="target") + target_temp_dir_path = target_temp_dir.path + self.enter_context(target_temp_dir) + + global_options = options.global_options or [] + + session = self.get_default_session(options) + + target_python = make_target_python(options) + finder = self._build_package_finder( + options=options, + session=session, + target_python=target_python, + ignore_requires_python=options.ignore_requires_python, + ) + wheel_cache = WheelCache(options.cache_dir, options.format_control) + + req_tracker = self.enter_context(get_requirement_tracker()) + + directory = TempDirectory( + delete=not options.no_clean, + kind="install", + globally_managed=True, + ) + + try: + reqs = self.get_requirements(args, options, finder, session) + + reject_location_related_install_options( + reqs, options.install_options + ) + + preparer = self.make_requirement_preparer( + temp_build_dir=directory, + options=options, + req_tracker=req_tracker, + session=session, + finder=finder, + use_user_site=options.use_user_site, + ) + resolver = self.make_resolver( + preparer=preparer, + finder=finder, + options=options, + wheel_cache=wheel_cache, + use_user_site=options.use_user_site, + ignore_installed=options.ignore_installed, + ignore_requires_python=options.ignore_requires_python, + force_reinstall=options.force_reinstall, + upgrade_strategy=upgrade_strategy, + use_pep517=options.use_pep517, + ) + + self.trace_basic_info(finder) + + requirement_set = resolver.resolve( + reqs, check_supported_wheels=not options.target_dir + ) + + try: + pip_req = requirement_set.get_requirement("pip") + except KeyError: + modifying_pip = False + else: + # If we're not replacing an already installed pip, + # we're not modifying it. + modifying_pip = pip_req.satisfied_by is None + protect_pip_from_modification_on_windows( + modifying_pip=modifying_pip + ) + + check_binary_allowed = get_check_binary_allowed( + finder.format_control + ) + + reqs_to_build = [ + r for r in requirement_set.requirements.values() + if should_build_for_install_command( + r, check_binary_allowed + ) + ] + + _, build_failures = build( + reqs_to_build, + wheel_cache=wheel_cache, + verify=True, + build_options=[], + global_options=[], + ) + + # If we're using PEP 517, we cannot do a direct install + # so we fail here. + pep517_build_failure_names = [ + r.name # type: ignore + for r in build_failures if r.use_pep517 + ] # type: List[str] + if pep517_build_failure_names: + raise InstallationError( + "Could not build wheels for {} which use" + " PEP 517 and cannot be installed directly".format( + ", ".join(pep517_build_failure_names) + ) + ) + + # For now, we just warn about failures building legacy + # requirements, as we'll fall through to a direct + # install for those. + for r in build_failures: + if not r.use_pep517: + r.legacy_install_reason = 8368 + + to_install = resolver.get_installation_order( + requirement_set + ) + + # Check for conflicts in the package set we're installing. + conflicts = None # type: Optional[ConflictDetails] + should_warn_about_conflicts = ( + not options.ignore_dependencies and + options.warn_about_conflicts + ) + if should_warn_about_conflicts: + conflicts = self._determine_conflicts(to_install) + + # Don't warn about script install locations if + # --target has been specified + warn_script_location = options.warn_script_location + if options.target_dir: + warn_script_location = False + + installed = install_given_reqs( + to_install, + install_options, + global_options, + root=options.root_path, + home=target_temp_dir_path, + prefix=options.prefix_path, + warn_script_location=warn_script_location, + use_user_site=options.use_user_site, + pycompile=options.compile, + ) + + lib_locations = get_lib_location_guesses( + user=options.use_user_site, + home=target_temp_dir_path, + root=options.root_path, + prefix=options.prefix_path, + isolated=options.isolated_mode, + ) + working_set = pkg_resources.WorkingSet(lib_locations) + + installed.sort(key=operator.attrgetter('name')) + items = [] + for result in installed: + item = result.name + try: + installed_version = get_installed_version( + result.name, working_set=working_set + ) + if installed_version: + item += '-' + installed_version + except Exception: + pass + items.append(item) + + if conflicts is not None: + self._warn_about_conflicts( + conflicts, + resolver_variant=self.determine_resolver_variant(options), + ) + + installed_desc = ' '.join(items) + if installed_desc: + write_output( + 'Successfully installed %s', installed_desc, + ) + except EnvironmentError as error: + show_traceback = (self.verbosity >= 1) + + message = create_env_error_message( + error, show_traceback, options.use_user_site, + ) + logger.error(message, exc_info=show_traceback) # noqa + + return ERROR + + if options.target_dir: + assert target_temp_dir + self._handle_target_dir( + options.target_dir, target_temp_dir, options.upgrade + ) + + return SUCCESS + + def _handle_target_dir(self, target_dir, target_temp_dir, upgrade): + # type: (str, TempDirectory, bool) -> None + ensure_dir(target_dir) + + # Checking both purelib and platlib directories for installed + # packages to be moved to target directory + lib_dir_list = [] + + # Checking both purelib and platlib directories for installed + # packages to be moved to target directory + scheme = distutils_scheme('', home=target_temp_dir.path) + purelib_dir = scheme['purelib'] + platlib_dir = scheme['platlib'] + data_dir = scheme['data'] + + if os.path.exists(purelib_dir): + lib_dir_list.append(purelib_dir) + if os.path.exists(platlib_dir) and platlib_dir != purelib_dir: + lib_dir_list.append(platlib_dir) + if os.path.exists(data_dir): + lib_dir_list.append(data_dir) + + for lib_dir in lib_dir_list: + for item in os.listdir(lib_dir): + if lib_dir == data_dir: + ddir = os.path.join(data_dir, item) + if any(s.startswith(ddir) for s in lib_dir_list[:-1]): + continue + target_item_dir = os.path.join(target_dir, item) + if os.path.exists(target_item_dir): + if not upgrade: + logger.warning( + 'Target directory %s already exists. Specify ' + '--upgrade to force replacement.', + target_item_dir + ) + continue + if os.path.islink(target_item_dir): + logger.warning( + 'Target directory %s already exists and is ' + 'a link. pip will not automatically replace ' + 'links, please remove if replacement is ' + 'desired.', + target_item_dir + ) + continue + if os.path.isdir(target_item_dir): + shutil.rmtree(target_item_dir) + else: + os.remove(target_item_dir) + + shutil.move( + os.path.join(lib_dir, item), + target_item_dir + ) + + def _determine_conflicts(self, to_install): + # type: (List[InstallRequirement]) -> Optional[ConflictDetails] + try: + return check_install_conflicts(to_install) + except Exception: + logger.exception( + "Error while checking for conflicts. Please file an issue on " + "pip's issue tracker: https://github.com/pypa/pip/issues/new" + ) + return None + + def _warn_about_conflicts(self, conflict_details, resolver_variant): + # type: (ConflictDetails, str) -> None + package_set, (missing, conflicting) = conflict_details + if not missing and not conflicting: + return + + parts = [] # type: List[str] + if resolver_variant == "legacy": + parts.append( + "pip's legacy dependency resolver does not consider dependency " + "conflicts when selecting packages. This behaviour is the " + "source of the following dependency conflicts." + ) + else: + assert resolver_variant == "2020-resolver" + parts.append( + "pip's dependency resolver does not currently take into account " + "all the packages that are installed. This behaviour is the " + "source of the following dependency conflicts." + ) + + # NOTE: There is some duplication here, with commands/check.py + for project_name in missing: + version = package_set[project_name][0] + for dependency in missing[project_name]: + message = ( + "{name} {version} requires {requirement}, " + "which is not installed." + ).format( + name=project_name, + version=version, + requirement=dependency[1], + ) + parts.append(message) + + for project_name in conflicting: + version = package_set[project_name][0] + for dep_name, dep_version, req in conflicting[project_name]: + message = ( + "{name} {version} requires {requirement}, but {you} have " + "{dep_name} {dep_version} which is incompatible." + ).format( + name=project_name, + version=version, + requirement=req, + dep_name=dep_name, + dep_version=dep_version, + you=("you" if resolver_variant == "2020-resolver" else "you'll") + ) + parts.append(message) + + logger.critical("\n".join(parts)) + + +def get_lib_location_guesses( + user=False, # type: bool + home=None, # type: Optional[str] + root=None, # type: Optional[str] + isolated=False, # type: bool + prefix=None # type: Optional[str] +): + # type:(...) -> List[str] + scheme = distutils_scheme('', user=user, home=home, root=root, + isolated=isolated, prefix=prefix) + return [scheme['purelib'], scheme['platlib']] + + +def site_packages_writable(root, isolated): + # type: (Optional[str], bool) -> bool + return all( + test_writable_dir(d) for d in set( + get_lib_location_guesses(root=root, isolated=isolated)) + ) + + +def decide_user_install( + use_user_site, # type: Optional[bool] + prefix_path=None, # type: Optional[str] + target_dir=None, # type: Optional[str] + root_path=None, # type: Optional[str] + isolated_mode=False, # type: bool +): + # type: (...) -> bool + """Determine whether to do a user install based on the input options. + + If use_user_site is False, no additional checks are done. + If use_user_site is True, it is checked for compatibility with other + options. + If use_user_site is None, the default behaviour depends on the environment, + which is provided by the other arguments. + """ + # In some cases (config from tox), use_user_site can be set to an integer + # rather than a bool, which 'use_user_site is False' wouldn't catch. + if (use_user_site is not None) and (not use_user_site): + logger.debug("Non-user install by explicit request") + return False + + if use_user_site: + if prefix_path: + raise CommandError( + "Can not combine '--user' and '--prefix' as they imply " + "different installation locations" + ) + if virtualenv_no_global(): + raise InstallationError( + "Can not perform a '--user' install. User site-packages " + "are not visible in this virtualenv." + ) + logger.debug("User install by explicit request") + return True + + # If we are here, user installs have not been explicitly requested/avoided + assert use_user_site is None + + # user install incompatible with --prefix/--target + if prefix_path or target_dir: + logger.debug("Non-user install due to --prefix or --target option") + return False + + # If user installs are not enabled, choose a non-user install + if not site.ENABLE_USER_SITE: + logger.debug("Non-user install because user site-packages disabled") + return False + + # If we have permission for a non-user install, do that, + # otherwise do a user install. + if site_packages_writable(root=root_path, isolated=isolated_mode): + logger.debug("Non-user install because site-packages writeable") + return False + + logger.info("Defaulting to user installation because normal site-packages " + "is not writeable") + return True + + +def reject_location_related_install_options(requirements, options): + # type: (List[InstallRequirement], Optional[List[str]]) -> None + """If any location-changing --install-option arguments were passed for + requirements or on the command-line, then show a deprecation warning. + """ + def format_options(option_names): + # type: (Iterable[str]) -> List[str] + return ["--{}".format(name.replace("_", "-")) for name in option_names] + + offenders = [] + + for requirement in requirements: + install_options = requirement.install_options + location_options = parse_distutils_args(install_options) + if location_options: + offenders.append( + "{!r} from {}".format( + format_options(location_options.keys()), requirement + ) + ) + + if options: + location_options = parse_distutils_args(options) + if location_options: + offenders.append( + "{!r} from command line".format( + format_options(location_options.keys()) + ) + ) + + if not offenders: + return + + raise CommandError( + "Location-changing options found in --install-option: {}." + " This is unsupported, use pip-level options like --user," + " --prefix, --root, and --target instead.".format( + "; ".join(offenders) + ) + ) + + +def create_env_error_message(error, show_traceback, using_user_site): + # type: (EnvironmentError, bool, bool) -> str + """Format an error message for an EnvironmentError + + It may occur anytime during the execution of the install command. + """ + parts = [] + + # Mention the error if we are not going to show a traceback + parts.append("Could not install packages due to an EnvironmentError") + if not show_traceback: + parts.append(": ") + parts.append(str(error)) + else: + parts.append(".") + + # Spilt the error indication from a helper message (if any) + parts[-1] += "\n" + + # Suggest useful actions to the user: + # (1) using user site-packages or (2) verifying the permissions + if error.errno == errno.EACCES: + user_option_part = "Consider using the `--user` option" + permissions_part = "Check the permissions" + + if not using_user_site: + parts.extend([ + user_option_part, " or ", + permissions_part.lower(), + ]) + else: + parts.append(permissions_part) + parts.append(".\n") + + return "".join(parts).strip() + "\n" diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/list.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/list.py new file mode 100644 index 0000000000..8e63eea23c --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/list.py @@ -0,0 +1,328 @@ +from __future__ import absolute_import + +import json +import logging + +from pip._vendor import six + +from pip._internal.cli import cmdoptions +from pip._internal.cli.req_command import IndexGroupCommand +from pip._internal.cli.status_codes import SUCCESS +from pip._internal.exceptions import CommandError +from pip._internal.index.collector import LinkCollector +from pip._internal.index.package_finder import PackageFinder +from pip._internal.models.selection_prefs import SelectionPreferences +from pip._internal.utils.compat import stdlib_pkgs +from pip._internal.utils.misc import ( + dist_is_editable, + get_installed_distributions, + tabulate, + write_output, +) +from pip._internal.utils.packaging import get_installer +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from optparse import Values + from typing import Iterator, List, Set, Tuple + + from pip._vendor.pkg_resources import Distribution + + from pip._internal.network.session import PipSession + +from pip._vendor.packaging.version import parse + +logger = logging.getLogger(__name__) + + +class ListCommand(IndexGroupCommand): + """ + List installed packages, including editables. + + Packages are listed in a case-insensitive sorted order. + """ + + ignore_require_venv = True + usage = """ + %prog [options]""" + + def add_options(self): + # type: () -> None + self.cmd_opts.add_option( + '-o', '--outdated', + action='store_true', + default=False, + help='List outdated packages') + self.cmd_opts.add_option( + '-u', '--uptodate', + action='store_true', + default=False, + help='List uptodate packages') + self.cmd_opts.add_option( + '-e', '--editable', + action='store_true', + default=False, + help='List editable projects.') + self.cmd_opts.add_option( + '-l', '--local', + action='store_true', + default=False, + help=('If in a virtualenv that has global access, do not list ' + 'globally-installed packages.'), + ) + self.cmd_opts.add_option( + '--user', + dest='user', + action='store_true', + default=False, + help='Only output packages installed in user-site.') + self.cmd_opts.add_option(cmdoptions.list_path()) + self.cmd_opts.add_option( + '--pre', + action='store_true', + default=False, + help=("Include pre-release and development versions. By default, " + "pip only finds stable versions."), + ) + + self.cmd_opts.add_option( + '--format', + action='store', + dest='list_format', + default="columns", + choices=('columns', 'freeze', 'json'), + help="Select the output format among: columns (default), freeze, " + "or json", + ) + + self.cmd_opts.add_option( + '--not-required', + action='store_true', + dest='not_required', + help="List packages that are not dependencies of " + "installed packages.", + ) + + self.cmd_opts.add_option( + '--exclude-editable', + action='store_false', + dest='include_editable', + help='Exclude editable package from output.', + ) + self.cmd_opts.add_option( + '--include-editable', + action='store_true', + dest='include_editable', + help='Include editable package from output.', + default=True, + ) + self.cmd_opts.add_option(cmdoptions.list_exclude()) + index_opts = cmdoptions.make_option_group( + cmdoptions.index_group, self.parser + ) + + self.parser.insert_option_group(0, index_opts) + self.parser.insert_option_group(0, self.cmd_opts) + + def _build_package_finder(self, options, session): + # type: (Values, PipSession) -> PackageFinder + """ + Create a package finder appropriate to this list command. + """ + link_collector = LinkCollector.create(session, options=options) + + # Pass allow_yanked=False to ignore yanked versions. + selection_prefs = SelectionPreferences( + allow_yanked=False, + allow_all_prereleases=options.pre, + ) + + return PackageFinder.create( + link_collector=link_collector, + selection_prefs=selection_prefs, + ) + + def run(self, options, args): + # type: (Values, List[str]) -> int + if options.outdated and options.uptodate: + raise CommandError( + "Options --outdated and --uptodate cannot be combined.") + + cmdoptions.check_list_path_option(options) + + skip = set(stdlib_pkgs) + if options.excludes: + skip.update(options.excludes) + + packages = get_installed_distributions( + local_only=options.local, + user_only=options.user, + editables_only=options.editable, + include_editables=options.include_editable, + paths=options.path, + skip=skip, + ) + + # get_not_required must be called firstly in order to find and + # filter out all dependencies correctly. Otherwise a package + # can't be identified as requirement because some parent packages + # could be filtered out before. + if options.not_required: + packages = self.get_not_required(packages, options) + + if options.outdated: + packages = self.get_outdated(packages, options) + elif options.uptodate: + packages = self.get_uptodate(packages, options) + + self.output_package_listing(packages, options) + return SUCCESS + + def get_outdated(self, packages, options): + # type: (List[Distribution], Values) -> List[Distribution] + return [ + dist for dist in self.iter_packages_latest_infos(packages, options) + if parse(str(dist.latest_version)) > parse(str(dist.parsed_version)) + ] + + def get_uptodate(self, packages, options): + # type: (List[Distribution], Values) -> List[Distribution] + return [ + dist for dist in self.iter_packages_latest_infos(packages, options) + if parse(str(dist.latest_version)) == parse(str(dist.parsed_version)) + ] + + def get_not_required(self, packages, options): + # type: (List[Distribution], Values) -> List[Distribution] + dep_keys = set() # type: Set[Distribution] + for dist in packages: + dep_keys.update(requirement.key for requirement in dist.requires()) + + # Create a set to remove duplicate packages, and cast it to a list + # to keep the return type consistent with get_outdated and + # get_uptodate + return list({pkg for pkg in packages if pkg.key not in dep_keys}) + + def iter_packages_latest_infos(self, packages, options): + # type: (List[Distribution], Values) -> Iterator[Distribution] + with self._build_session(options) as session: + finder = self._build_package_finder(options, session) + + def latest_info(dist): + # type: (Distribution) -> Distribution + all_candidates = finder.find_all_candidates(dist.key) + if not options.pre: + # Remove prereleases + all_candidates = [candidate for candidate in all_candidates + if not candidate.version.is_prerelease] + + evaluator = finder.make_candidate_evaluator( + project_name=dist.project_name, + ) + best_candidate = evaluator.sort_best_candidate(all_candidates) + if best_candidate is None: + return None + + remote_version = best_candidate.version + if best_candidate.link.is_wheel: + typ = 'wheel' + else: + typ = 'sdist' + # This is dirty but makes the rest of the code much cleaner + dist.latest_version = remote_version + dist.latest_filetype = typ + return dist + + for dist in map(latest_info, packages): + if dist is not None: + yield dist + + def output_package_listing(self, packages, options): + # type: (List[Distribution], Values) -> None + packages = sorted( + packages, + key=lambda dist: dist.project_name.lower(), + ) + if options.list_format == 'columns' and packages: + data, header = format_for_columns(packages, options) + self.output_package_listing_columns(data, header) + elif options.list_format == 'freeze': + for dist in packages: + if options.verbose >= 1: + write_output("%s==%s (%s)", dist.project_name, + dist.version, dist.location) + else: + write_output("%s==%s", dist.project_name, dist.version) + elif options.list_format == 'json': + write_output(format_for_json(packages, options)) + + def output_package_listing_columns(self, data, header): + # type: (List[List[str]], List[str]) -> None + # insert the header first: we need to know the size of column names + if len(data) > 0: + data.insert(0, header) + + pkg_strings, sizes = tabulate(data) + + # Create and add a separator. + if len(data) > 0: + pkg_strings.insert(1, " ".join(map(lambda x: '-' * x, sizes))) + + for val in pkg_strings: + write_output(val) + + +def format_for_columns(pkgs, options): + # type: (List[Distribution], Values) -> Tuple[List[List[str]], List[str]] + """ + Convert the package data into something usable + by output_package_listing_columns. + """ + running_outdated = options.outdated + # Adjust the header for the `pip list --outdated` case. + if running_outdated: + header = ["Package", "Version", "Latest", "Type"] + else: + header = ["Package", "Version"] + + data = [] + if options.verbose >= 1 or any(dist_is_editable(x) for x in pkgs): + header.append("Location") + if options.verbose >= 1: + header.append("Installer") + + for proj in pkgs: + # if we're working on the 'outdated' list, separate out the + # latest_version and type + row = [proj.project_name, proj.version] + + if running_outdated: + row.append(proj.latest_version) + row.append(proj.latest_filetype) + + if options.verbose >= 1 or dist_is_editable(proj): + row.append(proj.location) + if options.verbose >= 1: + row.append(get_installer(proj)) + + data.append(row) + + return data, header + + +def format_for_json(packages, options): + # type: (List[Distribution], Values) -> str + data = [] + for dist in packages: + info = { + 'name': dist.project_name, + 'version': six.text_type(dist.version), + } + if options.verbose >= 1: + info['location'] = dist.location + info['installer'] = get_installer(dist) + if options.outdated: + info['latest_version'] = six.text_type(dist.latest_version) + info['latest_filetype'] = dist.latest_filetype + data.append(info) + return json.dumps(data) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/search.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/search.py new file mode 100644 index 0000000000..185495e768 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/search.py @@ -0,0 +1,169 @@ +from __future__ import absolute_import + +import logging +import sys +import textwrap +from collections import OrderedDict + +from pip._vendor import pkg_resources +from pip._vendor.packaging.version import parse as parse_version + +# NOTE: XMLRPC Client is not annotated in typeshed as on 2017-07-17, which is +# why we ignore the type on this import +from pip._vendor.six.moves import xmlrpc_client # type: ignore + +from pip._internal.cli.base_command import Command +from pip._internal.cli.req_command import SessionCommandMixin +from pip._internal.cli.status_codes import NO_MATCHES_FOUND, SUCCESS +from pip._internal.exceptions import CommandError +from pip._internal.models.index import PyPI +from pip._internal.network.xmlrpc import PipXmlrpcTransport +from pip._internal.utils.compat import get_terminal_size +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import get_distribution, write_output +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from optparse import Values + from typing import Dict, List, Optional + + from typing_extensions import TypedDict + TransformedHit = TypedDict( + 'TransformedHit', + {'name': str, 'summary': str, 'versions': List[str]}, + ) + +logger = logging.getLogger(__name__) + + +class SearchCommand(Command, SessionCommandMixin): + """Search for PyPI packages whose name or summary contains .""" + + usage = """ + %prog [options] """ + ignore_require_venv = True + + def add_options(self): + # type: () -> None + self.cmd_opts.add_option( + '-i', '--index', + dest='index', + metavar='URL', + default=PyPI.pypi_url, + help='Base URL of Python Package Index (default %default)') + + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options, args): + # type: (Values, List[str]) -> int + if not args: + raise CommandError('Missing required argument (search query).') + query = args + pypi_hits = self.search(query, options) + hits = transform_hits(pypi_hits) + + terminal_width = None + if sys.stdout.isatty(): + terminal_width = get_terminal_size()[0] + + print_results(hits, terminal_width=terminal_width) + if pypi_hits: + return SUCCESS + return NO_MATCHES_FOUND + + def search(self, query, options): + # type: (List[str], Values) -> List[Dict[str, str]] + index_url = options.index + + session = self.get_default_session(options) + + transport = PipXmlrpcTransport(index_url, session) + pypi = xmlrpc_client.ServerProxy(index_url, transport) + try: + hits = pypi.search({'name': query, 'summary': query}, 'or') + except xmlrpc_client.Fault as fault: + message = "XMLRPC request failed [code: {code}]\n{string}".format( + code=fault.faultCode, + string=fault.faultString, + ) + raise CommandError(message) + return hits + + +def transform_hits(hits): + # type: (List[Dict[str, str]]) -> List[TransformedHit] + """ + The list from pypi is really a list of versions. We want a list of + packages with the list of versions stored inline. This converts the + list from pypi into one we can use. + """ + packages = OrderedDict() # type: OrderedDict[str, TransformedHit] + for hit in hits: + name = hit['name'] + summary = hit['summary'] + version = hit['version'] + + if name not in packages.keys(): + packages[name] = { + 'name': name, + 'summary': summary, + 'versions': [version], + } + else: + packages[name]['versions'].append(version) + + # if this is the highest version, replace summary and score + if version == highest_version(packages[name]['versions']): + packages[name]['summary'] = summary + + return list(packages.values()) + + +def print_results(hits, name_column_width=None, terminal_width=None): + # type: (List[TransformedHit], Optional[int], Optional[int]) -> None + if not hits: + return + if name_column_width is None: + name_column_width = max([ + len(hit['name']) + len(highest_version(hit.get('versions', ['-']))) + for hit in hits + ]) + 4 + + installed_packages = [p.project_name for p in pkg_resources.working_set] + for hit in hits: + name = hit['name'] + summary = hit['summary'] or '' + latest = highest_version(hit.get('versions', ['-'])) + if terminal_width is not None: + target_width = terminal_width - name_column_width - 5 + if target_width > 10: + # wrap and indent summary to fit terminal + summary_lines = textwrap.wrap(summary, target_width) + summary = ('\n' + ' ' * (name_column_width + 3)).join( + summary_lines) + + line = '{name_latest:{name_column_width}} - {summary}'.format( + name_latest='{name} ({latest})'.format(**locals()), + **locals()) + try: + write_output(line) + if name in installed_packages: + dist = get_distribution(name) + assert dist is not None + with indent_log(): + if dist.version == latest: + write_output('INSTALLED: %s (latest)', dist.version) + else: + write_output('INSTALLED: %s', dist.version) + if parse_version(latest).pre: + write_output('LATEST: %s (pre-release; install' + ' with "pip install --pre")', latest) + else: + write_output('LATEST: %s', latest) + except UnicodeEncodeError: + pass + + +def highest_version(versions): + # type: (List[str]) -> str + return max(versions, key=parse_version) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/show.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/show.py new file mode 100644 index 0000000000..b0b3f3abdc --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/show.py @@ -0,0 +1,186 @@ +from __future__ import absolute_import + +import logging +import os +from email.parser import FeedParser + +from pip._vendor import pkg_resources +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.cli.base_command import Command +from pip._internal.cli.status_codes import ERROR, SUCCESS +from pip._internal.utils.misc import write_output +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from optparse import Values + from typing import Dict, Iterator, List + +logger = logging.getLogger(__name__) + + +class ShowCommand(Command): + """ + Show information about one or more installed packages. + + The output is in RFC-compliant mail header format. + """ + + usage = """ + %prog [options] ...""" + ignore_require_venv = True + + def add_options(self): + # type: () -> None + self.cmd_opts.add_option( + '-f', '--files', + dest='files', + action='store_true', + default=False, + help='Show the full list of installed files for each package.') + + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options, args): + # type: (Values, List[str]) -> int + if not args: + logger.warning('ERROR: Please provide a package name or names.') + return ERROR + query = args + + results = search_packages_info(query) + if not print_results( + results, list_files=options.files, verbose=options.verbose): + return ERROR + return SUCCESS + + +def search_packages_info(query): + # type: (List[str]) -> Iterator[Dict[str, str]] + """ + Gather details from installed distributions. Print distribution name, + version, location, and installed files. Installed files requires a + pip generated 'installed-files.txt' in the distributions '.egg-info' + directory. + """ + installed = {} + for p in pkg_resources.working_set: + installed[canonicalize_name(p.project_name)] = p + + query_names = [canonicalize_name(name) for name in query] + missing = sorted( + [name for name, pkg in zip(query, query_names) if pkg not in installed] + ) + if missing: + logger.warning('Package(s) not found: %s', ', '.join(missing)) + + def get_requiring_packages(package_name): + # type: (str) -> List[str] + canonical_name = canonicalize_name(package_name) + return [ + pkg.project_name for pkg in pkg_resources.working_set + if canonical_name in + [canonicalize_name(required.name) for required in + pkg.requires()] + ] + + for dist in [installed[pkg] for pkg in query_names if pkg in installed]: + package = { + 'name': dist.project_name, + 'version': dist.version, + 'location': dist.location, + 'requires': [dep.project_name for dep in dist.requires()], + 'required_by': get_requiring_packages(dist.project_name) + } + file_list = None + metadata = '' + if isinstance(dist, pkg_resources.DistInfoDistribution): + # RECORDs should be part of .dist-info metadatas + if dist.has_metadata('RECORD'): + lines = dist.get_metadata_lines('RECORD') + paths = [line.split(',')[0] for line in lines] + paths = [os.path.join(dist.location, p) for p in paths] + file_list = [os.path.relpath(p, dist.location) for p in paths] + + if dist.has_metadata('METADATA'): + metadata = dist.get_metadata('METADATA') + else: + # Otherwise use pip's log for .egg-info's + if dist.has_metadata('installed-files.txt'): + paths = dist.get_metadata_lines('installed-files.txt') + paths = [os.path.join(dist.egg_info, p) for p in paths] + file_list = [os.path.relpath(p, dist.location) for p in paths] + + if dist.has_metadata('PKG-INFO'): + metadata = dist.get_metadata('PKG-INFO') + + if dist.has_metadata('entry_points.txt'): + entry_points = dist.get_metadata_lines('entry_points.txt') + package['entry_points'] = entry_points + + if dist.has_metadata('INSTALLER'): + for line in dist.get_metadata_lines('INSTALLER'): + if line.strip(): + package['installer'] = line.strip() + break + + # @todo: Should pkg_resources.Distribution have a + # `get_pkg_info` method? + feed_parser = FeedParser() + feed_parser.feed(metadata) + pkg_info_dict = feed_parser.close() + for key in ('metadata-version', 'summary', + 'home-page', 'author', 'author-email', 'license'): + package[key] = pkg_info_dict.get(key) + + # It looks like FeedParser cannot deal with repeated headers + classifiers = [] + for line in metadata.splitlines(): + if line.startswith('Classifier: '): + classifiers.append(line[len('Classifier: '):]) + package['classifiers'] = classifiers + + if file_list: + package['files'] = sorted(file_list) + yield package + + +def print_results(distributions, list_files=False, verbose=False): + # type: (Iterator[Dict[str, str]], bool, bool) -> bool + """ + Print the information from installed distributions found. + """ + results_printed = False + for i, dist in enumerate(distributions): + results_printed = True + if i > 0: + write_output("---") + + write_output("Name: %s", dist.get('name', '')) + write_output("Version: %s", dist.get('version', '')) + write_output("Summary: %s", dist.get('summary', '')) + write_output("Home-page: %s", dist.get('home-page', '')) + write_output("Author: %s", dist.get('author', '')) + write_output("Author-email: %s", dist.get('author-email', '')) + write_output("License: %s", dist.get('license', '')) + write_output("Location: %s", dist.get('location', '')) + write_output("Requires: %s", ', '.join(dist.get('requires', []))) + write_output("Required-by: %s", ', '.join(dist.get('required_by', []))) + + if verbose: + write_output("Metadata-Version: %s", + dist.get('metadata-version', '')) + write_output("Installer: %s", dist.get('installer', '')) + write_output("Classifiers:") + for classifier in dist.get('classifiers', []): + write_output(" %s", classifier) + write_output("Entry-points:") + for entry in dist.get('entry_points', []): + write_output(" %s", entry.strip()) + if list_files: + write_output("Files:") + for line in dist.get('files', []): + write_output(" %s", line.strip()) + if "files" not in dist: + write_output("Cannot locate installed-files.txt") + return results_printed diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/uninstall.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/uninstall.py new file mode 100644 index 0000000000..3371fe47ff --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/uninstall.py @@ -0,0 +1,95 @@ +from __future__ import absolute_import + +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.cli.base_command import Command +from pip._internal.cli.req_command import SessionCommandMixin +from pip._internal.cli.status_codes import SUCCESS +from pip._internal.exceptions import InstallationError +from pip._internal.req import parse_requirements +from pip._internal.req.constructors import ( + install_req_from_line, + install_req_from_parsed_requirement, +) +from pip._internal.utils.misc import protect_pip_from_modification_on_windows +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from optparse import Values + from typing import List + + +class UninstallCommand(Command, SessionCommandMixin): + """ + Uninstall packages. + + pip is able to uninstall most installed packages. Known exceptions are: + + - Pure distutils packages installed with ``python setup.py install``, which + leave behind no metadata to determine what files were installed. + - Script wrappers installed by ``python setup.py develop``. + """ + + usage = """ + %prog [options] ... + %prog [options] -r ...""" + + def add_options(self): + # type: () -> None + self.cmd_opts.add_option( + '-r', '--requirement', + dest='requirements', + action='append', + default=[], + metavar='file', + help='Uninstall all the packages listed in the given requirements ' + 'file. This option can be used multiple times.', + ) + self.cmd_opts.add_option( + '-y', '--yes', + dest='yes', + action='store_true', + help="Don't ask for confirmation of uninstall deletions.") + + self.parser.insert_option_group(0, self.cmd_opts) + + def run(self, options, args): + # type: (Values, List[str]) -> int + session = self.get_default_session(options) + + reqs_to_uninstall = {} + for name in args: + req = install_req_from_line( + name, isolated=options.isolated_mode, + ) + if req.name: + reqs_to_uninstall[canonicalize_name(req.name)] = req + for filename in options.requirements: + for parsed_req in parse_requirements( + filename, + options=options, + session=session): + req = install_req_from_parsed_requirement( + parsed_req, + isolated=options.isolated_mode + ) + if req.name: + reqs_to_uninstall[canonicalize_name(req.name)] = req + if not reqs_to_uninstall: + raise InstallationError( + 'You must give at least one requirement to {self.name} (see ' + '"pip help {self.name}")'.format(**locals()) + ) + + protect_pip_from_modification_on_windows( + modifying_pip="pip" in reqs_to_uninstall + ) + + for req in reqs_to_uninstall.values(): + uninstall_pathset = req.uninstall( + auto_confirm=options.yes, verbose=self.verbosity > 0, + ) + if uninstall_pathset: + uninstall_pathset.commit() + + return SUCCESS diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/wheel.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/wheel.py new file mode 100644 index 0000000000..a9f66258a1 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/commands/wheel.py @@ -0,0 +1,198 @@ +# -*- coding: utf-8 -*- + +from __future__ import absolute_import + +import logging +import os +import shutil + +from pip._internal.cache import WheelCache +from pip._internal.cli import cmdoptions +from pip._internal.cli.req_command import RequirementCommand, with_cleanup +from pip._internal.cli.status_codes import SUCCESS +from pip._internal.exceptions import CommandError +from pip._internal.req.req_tracker import get_requirement_tracker +from pip._internal.utils.misc import ensure_dir, normalize_path +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.wheel_builder import build, should_build_for_wheel_command + +if MYPY_CHECK_RUNNING: + from optparse import Values + from typing import List + + from pip._internal.req.req_install import InstallRequirement + +logger = logging.getLogger(__name__) + + +class WheelCommand(RequirementCommand): + """ + Build Wheel archives for your requirements and dependencies. + + Wheel is a built-package format, and offers the advantage of not + recompiling your software during every install. For more details, see the + wheel docs: https://wheel.readthedocs.io/en/latest/ + + Requirements: setuptools>=0.8, and wheel. + + 'pip wheel' uses the bdist_wheel setuptools extension from the wheel + package to build individual wheels. + + """ + + usage = """ + %prog [options] ... + %prog [options] -r ... + %prog [options] [-e] ... + %prog [options] [-e] ... + %prog [options] ...""" + + def add_options(self): + # type: () -> None + + self.cmd_opts.add_option( + '-w', '--wheel-dir', + dest='wheel_dir', + metavar='dir', + default=os.curdir, + help=("Build wheels into , where the default is the " + "current working directory."), + ) + self.cmd_opts.add_option(cmdoptions.no_binary()) + self.cmd_opts.add_option(cmdoptions.only_binary()) + self.cmd_opts.add_option(cmdoptions.prefer_binary()) + self.cmd_opts.add_option( + '--build-option', + dest='build_options', + metavar='options', + action='append', + help="Extra arguments to be supplied to 'setup.py bdist_wheel'.", + ) + self.cmd_opts.add_option(cmdoptions.no_build_isolation()) + self.cmd_opts.add_option(cmdoptions.use_pep517()) + self.cmd_opts.add_option(cmdoptions.no_use_pep517()) + self.cmd_opts.add_option(cmdoptions.constraints()) + self.cmd_opts.add_option(cmdoptions.editable()) + self.cmd_opts.add_option(cmdoptions.requirements()) + self.cmd_opts.add_option(cmdoptions.src()) + self.cmd_opts.add_option(cmdoptions.ignore_requires_python()) + self.cmd_opts.add_option(cmdoptions.no_deps()) + self.cmd_opts.add_option(cmdoptions.build_dir()) + self.cmd_opts.add_option(cmdoptions.progress_bar()) + + self.cmd_opts.add_option( + '--no-verify', + dest='no_verify', + action='store_true', + default=False, + help="Don't verify if built wheel is valid.", + ) + + self.cmd_opts.add_option( + '--global-option', + dest='global_options', + action='append', + metavar='options', + help="Extra global options to be supplied to the setup.py " + "call before the 'bdist_wheel' command.") + + self.cmd_opts.add_option( + '--pre', + action='store_true', + default=False, + help=("Include pre-release and development versions. By default, " + "pip only finds stable versions."), + ) + + self.cmd_opts.add_option(cmdoptions.require_hashes()) + + index_opts = cmdoptions.make_option_group( + cmdoptions.index_group, + self.parser, + ) + + self.parser.insert_option_group(0, index_opts) + self.parser.insert_option_group(0, self.cmd_opts) + + @with_cleanup + def run(self, options, args): + # type: (Values, List[str]) -> int + cmdoptions.check_install_build_global(options) + + session = self.get_default_session(options) + + finder = self._build_package_finder(options, session) + wheel_cache = WheelCache(options.cache_dir, options.format_control) + + options.wheel_dir = normalize_path(options.wheel_dir) + ensure_dir(options.wheel_dir) + + req_tracker = self.enter_context(get_requirement_tracker()) + + directory = TempDirectory( + delete=not options.no_clean, + kind="wheel", + globally_managed=True, + ) + + reqs = self.get_requirements(args, options, finder, session) + + preparer = self.make_requirement_preparer( + temp_build_dir=directory, + options=options, + req_tracker=req_tracker, + session=session, + finder=finder, + download_dir=options.wheel_dir, + use_user_site=False, + ) + + resolver = self.make_resolver( + preparer=preparer, + finder=finder, + options=options, + wheel_cache=wheel_cache, + ignore_requires_python=options.ignore_requires_python, + use_pep517=options.use_pep517, + ) + + self.trace_basic_info(finder) + + requirement_set = resolver.resolve( + reqs, check_supported_wheels=True + ) + + reqs_to_build = [] # type: List[InstallRequirement] + for req in requirement_set.requirements.values(): + if req.is_wheel: + preparer.save_linked_requirement(req) + elif should_build_for_wheel_command(req): + reqs_to_build.append(req) + + # build wheels + build_successes, build_failures = build( + reqs_to_build, + wheel_cache=wheel_cache, + verify=(not options.no_verify), + build_options=options.build_options or [], + global_options=options.global_options or [], + ) + for req in build_successes: + assert req.link and req.link.is_wheel + assert req.local_file_path + # copy from cache to target directory + try: + shutil.copy(req.local_file_path, options.wheel_dir) + except OSError as e: + logger.warning( + "Building wheel for %s failed: %s", + req.name, e, + ) + build_failures.append(req) + if len(build_failures) != 0: + raise CommandError( + "Failed to build one or more wheels" + ) + + return SUCCESS diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/configuration.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/configuration.py new file mode 100644 index 0000000000..23614fd2bb --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/configuration.py @@ -0,0 +1,407 @@ +"""Configuration management setup + +Some terminology: +- name + As written in config files. +- value + Value associated with a name +- key + Name combined with it's section (section.name) +- variant + A single word describing where the configuration key-value pair came from +""" + +import locale +import logging +import os +import sys + +from pip._vendor.six.moves import configparser + +from pip._internal.exceptions import ( + ConfigurationError, + ConfigurationFileCouldNotBeLoaded, +) +from pip._internal.utils import appdirs +from pip._internal.utils.compat import WINDOWS, expanduser +from pip._internal.utils.misc import ensure_dir, enum +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Any, Dict, Iterable, List, NewType, Optional, Tuple + + RawConfigParser = configparser.RawConfigParser # Shorthand + Kind = NewType("Kind", str) + +CONFIG_BASENAME = 'pip.ini' if WINDOWS else 'pip.conf' +ENV_NAMES_IGNORED = "version", "help" + +# The kinds of configurations there are. +kinds = enum( + USER="user", # User Specific + GLOBAL="global", # System Wide + SITE="site", # [Virtual] Environment Specific + ENV="env", # from PIP_CONFIG_FILE + ENV_VAR="env-var", # from Environment Variables +) +OVERRIDE_ORDER = kinds.GLOBAL, kinds.USER, kinds.SITE, kinds.ENV, kinds.ENV_VAR +VALID_LOAD_ONLY = kinds.USER, kinds.GLOBAL, kinds.SITE + +logger = logging.getLogger(__name__) + + +# NOTE: Maybe use the optionx attribute to normalize keynames. +def _normalize_name(name): + # type: (str) -> str + """Make a name consistent regardless of source (environment or file) + """ + name = name.lower().replace('_', '-') + if name.startswith('--'): + name = name[2:] # only prefer long opts + return name + + +def _disassemble_key(name): + # type: (str) -> List[str] + if "." not in name: + error_message = ( + "Key does not contain dot separated section and key. " + "Perhaps you wanted to use 'global.{}' instead?" + ).format(name) + raise ConfigurationError(error_message) + return name.split(".", 1) + + +def get_configuration_files(): + # type: () -> Dict[Kind, List[str]] + global_config_files = [ + os.path.join(path, CONFIG_BASENAME) + for path in appdirs.site_config_dirs('pip') + ] + + site_config_file = os.path.join(sys.prefix, CONFIG_BASENAME) + legacy_config_file = os.path.join( + expanduser('~'), + 'pip' if WINDOWS else '.pip', + CONFIG_BASENAME, + ) + new_config_file = os.path.join( + appdirs.user_config_dir("pip"), CONFIG_BASENAME + ) + return { + kinds.GLOBAL: global_config_files, + kinds.SITE: [site_config_file], + kinds.USER: [legacy_config_file, new_config_file], + } + + +class Configuration(object): + """Handles management of configuration. + + Provides an interface to accessing and managing configuration files. + + This class converts provides an API that takes "section.key-name" style + keys and stores the value associated with it as "key-name" under the + section "section". + + This allows for a clean interface wherein the both the section and the + key-name are preserved in an easy to manage form in the configuration files + and the data stored is also nice. + """ + + def __init__(self, isolated, load_only=None): + # type: (bool, Optional[Kind]) -> None + super(Configuration, self).__init__() + + if load_only is not None and load_only not in VALID_LOAD_ONLY: + raise ConfigurationError( + "Got invalid value for load_only - should be one of {}".format( + ", ".join(map(repr, VALID_LOAD_ONLY)) + ) + ) + self.isolated = isolated + self.load_only = load_only + + # Because we keep track of where we got the data from + self._parsers = { + variant: [] for variant in OVERRIDE_ORDER + } # type: Dict[Kind, List[Tuple[str, RawConfigParser]]] + self._config = { + variant: {} for variant in OVERRIDE_ORDER + } # type: Dict[Kind, Dict[str, Any]] + self._modified_parsers = [] # type: List[Tuple[str, RawConfigParser]] + + def load(self): + # type: () -> None + """Loads configuration from configuration files and environment + """ + self._load_config_files() + if not self.isolated: + self._load_environment_vars() + + def get_file_to_edit(self): + # type: () -> Optional[str] + """Returns the file with highest priority in configuration + """ + assert self.load_only is not None, \ + "Need to be specified a file to be editing" + + try: + return self._get_parser_to_modify()[0] + except IndexError: + return None + + def items(self): + # type: () -> Iterable[Tuple[str, Any]] + """Returns key-value pairs like dict.items() representing the loaded + configuration + """ + return self._dictionary.items() + + def get_value(self, key): + # type: (str) -> Any + """Get a value from the configuration. + """ + try: + return self._dictionary[key] + except KeyError: + raise ConfigurationError("No such key - {}".format(key)) + + def set_value(self, key, value): + # type: (str, Any) -> None + """Modify a value in the configuration. + """ + self._ensure_have_load_only() + + assert self.load_only + fname, parser = self._get_parser_to_modify() + + if parser is not None: + section, name = _disassemble_key(key) + + # Modify the parser and the configuration + if not parser.has_section(section): + parser.add_section(section) + parser.set(section, name, value) + + self._config[self.load_only][key] = value + self._mark_as_modified(fname, parser) + + def unset_value(self, key): + # type: (str) -> None + """Unset a value in the configuration.""" + self._ensure_have_load_only() + + assert self.load_only + if key not in self._config[self.load_only]: + raise ConfigurationError("No such key - {}".format(key)) + + fname, parser = self._get_parser_to_modify() + + if parser is not None: + section, name = _disassemble_key(key) + if not (parser.has_section(section) + and parser.remove_option(section, name)): + # The option was not removed. + raise ConfigurationError( + "Fatal Internal error [id=1]. Please report as a bug." + ) + + # The section may be empty after the option was removed. + if not parser.items(section): + parser.remove_section(section) + self._mark_as_modified(fname, parser) + + del self._config[self.load_only][key] + + def save(self): + # type: () -> None + """Save the current in-memory state. + """ + self._ensure_have_load_only() + + for fname, parser in self._modified_parsers: + logger.info("Writing to %s", fname) + + # Ensure directory exists. + ensure_dir(os.path.dirname(fname)) + + with open(fname, "w") as f: + parser.write(f) + + # + # Private routines + # + + def _ensure_have_load_only(self): + # type: () -> None + if self.load_only is None: + raise ConfigurationError("Needed a specific file to be modifying.") + logger.debug("Will be working with %s variant only", self.load_only) + + @property + def _dictionary(self): + # type: () -> Dict[str, Any] + """A dictionary representing the loaded configuration. + """ + # NOTE: Dictionaries are not populated if not loaded. So, conditionals + # are not needed here. + retval = {} + + for variant in OVERRIDE_ORDER: + retval.update(self._config[variant]) + + return retval + + def _load_config_files(self): + # type: () -> None + """Loads configuration from configuration files + """ + config_files = dict(self.iter_config_files()) + if config_files[kinds.ENV][0:1] == [os.devnull]: + logger.debug( + "Skipping loading configuration files due to " + "environment's PIP_CONFIG_FILE being os.devnull" + ) + return + + for variant, files in config_files.items(): + for fname in files: + # If there's specific variant set in `load_only`, load only + # that variant, not the others. + if self.load_only is not None and variant != self.load_only: + logger.debug( + "Skipping file '%s' (variant: %s)", fname, variant + ) + continue + + parser = self._load_file(variant, fname) + + # Keeping track of the parsers used + self._parsers[variant].append((fname, parser)) + + def _load_file(self, variant, fname): + # type: (Kind, str) -> RawConfigParser + logger.debug("For variant '%s', will try loading '%s'", variant, fname) + parser = self._construct_parser(fname) + + for section in parser.sections(): + items = parser.items(section) + self._config[variant].update(self._normalized_keys(section, items)) + + return parser + + def _construct_parser(self, fname): + # type: (str) -> RawConfigParser + parser = configparser.RawConfigParser() + # If there is no such file, don't bother reading it but create the + # parser anyway, to hold the data. + # Doing this is useful when modifying and saving files, where we don't + # need to construct a parser. + if os.path.exists(fname): + try: + parser.read(fname) + except UnicodeDecodeError: + # See https://github.com/pypa/pip/issues/4963 + raise ConfigurationFileCouldNotBeLoaded( + reason="contains invalid {} characters".format( + locale.getpreferredencoding(False) + ), + fname=fname, + ) + except configparser.Error as error: + # See https://github.com/pypa/pip/issues/4893 + raise ConfigurationFileCouldNotBeLoaded(error=error) + return parser + + def _load_environment_vars(self): + # type: () -> None + """Loads configuration from environment variables + """ + self._config[kinds.ENV_VAR].update( + self._normalized_keys(":env:", self.get_environ_vars()) + ) + + def _normalized_keys(self, section, items): + # type: (str, Iterable[Tuple[str, Any]]) -> Dict[str, Any] + """Normalizes items to construct a dictionary with normalized keys. + + This routine is where the names become keys and are made the same + regardless of source - configuration files or environment. + """ + normalized = {} + for name, val in items: + key = section + "." + _normalize_name(name) + normalized[key] = val + return normalized + + def get_environ_vars(self): + # type: () -> Iterable[Tuple[str, str]] + """Returns a generator with all environmental vars with prefix PIP_""" + for key, val in os.environ.items(): + if key.startswith("PIP_"): + name = key[4:].lower() + if name not in ENV_NAMES_IGNORED: + yield name, val + + # XXX: This is patched in the tests. + def iter_config_files(self): + # type: () -> Iterable[Tuple[Kind, List[str]]] + """Yields variant and configuration files associated with it. + + This should be treated like items of a dictionary. + """ + # SMELL: Move the conditions out of this function + + # environment variables have the lowest priority + config_file = os.environ.get('PIP_CONFIG_FILE', None) + if config_file is not None: + yield kinds.ENV, [config_file] + else: + yield kinds.ENV, [] + + config_files = get_configuration_files() + + # at the base we have any global configuration + yield kinds.GLOBAL, config_files[kinds.GLOBAL] + + # per-user configuration next + should_load_user_config = not self.isolated and not ( + config_file and os.path.exists(config_file) + ) + if should_load_user_config: + # The legacy config file is overridden by the new config file + yield kinds.USER, config_files[kinds.USER] + + # finally virtualenv configuration first trumping others + yield kinds.SITE, config_files[kinds.SITE] + + def get_values_in_config(self, variant): + # type: (Kind) -> Dict[str, Any] + """Get values present in a config file""" + return self._config[variant] + + def _get_parser_to_modify(self): + # type: () -> Tuple[str, RawConfigParser] + # Determine which parser to modify + assert self.load_only + parsers = self._parsers[self.load_only] + if not parsers: + # This should not happen if everything works correctly. + raise ConfigurationError( + "Fatal Internal error [id=2]. Please report as a bug." + ) + + # Use the highest priority parser. + return parsers[-1] + + # XXX: This is patched in the tests. + def _mark_as_modified(self, fname, parser): + # type: (str, RawConfigParser) -> None + file_parser_tuple = (fname, parser) + if file_parser_tuple not in self._modified_parsers: + self._modified_parsers.append(file_parser_tuple) + + def __repr__(self): + # type: () -> str + return "{}({!r})".format(self.__class__.__name__, self._dictionary) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/distributions/__init__.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/distributions/__init__.py new file mode 100644 index 0000000000..d5c1afc5bc --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/distributions/__init__.py @@ -0,0 +1,24 @@ +from pip._internal.distributions.sdist import SourceDistribution +from pip._internal.distributions.wheel import WheelDistribution +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from pip._internal.distributions.base import AbstractDistribution + from pip._internal.req.req_install import InstallRequirement + + +def make_distribution_for_install_requirement(install_req): + # type: (InstallRequirement) -> AbstractDistribution + """Returns a Distribution for the given InstallRequirement + """ + # Editable requirements will always be source distributions. They use the + # legacy logic until we create a modern standard for them. + if install_req.editable: + return SourceDistribution(install_req) + + # If it's a wheel, it's a WheelDistribution + if install_req.is_wheel: + return WheelDistribution(install_req) + + # Otherwise, a SourceDistribution + return SourceDistribution(install_req) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/distributions/base.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/distributions/base.py new file mode 100644 index 0000000000..3a789f8043 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/distributions/base.py @@ -0,0 +1,46 @@ +import abc + +from pip._vendor.six import add_metaclass + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Optional + + from pip._vendor.pkg_resources import Distribution + + from pip._internal.index.package_finder import PackageFinder + from pip._internal.req import InstallRequirement + + +@add_metaclass(abc.ABCMeta) +class AbstractDistribution(object): + """A base class for handling installable artifacts. + + The requirements for anything installable are as follows: + + - we must be able to determine the requirement name + (or we can't correctly handle the non-upgrade case). + + - for packages with setup requirements, we must also be able + to determine their requirements without installing additional + packages (for the same reason as run-time dependencies) + + - we must be able to create a Distribution object exposing the + above metadata. + """ + + def __init__(self, req): + # type: (InstallRequirement) -> None + super(AbstractDistribution, self).__init__() + self.req = req + + @abc.abstractmethod + def get_pkg_resources_distribution(self): + # type: () -> Optional[Distribution] + raise NotImplementedError() + + @abc.abstractmethod + def prepare_distribution_metadata(self, finder, build_isolation): + # type: (PackageFinder, bool) -> None + raise NotImplementedError() diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/distributions/installed.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/distributions/installed.py new file mode 100644 index 0000000000..a813b211fe --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/distributions/installed.py @@ -0,0 +1,25 @@ +from pip._internal.distributions.base import AbstractDistribution +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Optional + + from pip._vendor.pkg_resources import Distribution + + from pip._internal.index.package_finder import PackageFinder + + +class InstalledDistribution(AbstractDistribution): + """Represents an installed package. + + This does not need any preparation as the required information has already + been computed. + """ + + def get_pkg_resources_distribution(self): + # type: () -> Optional[Distribution] + return self.req.satisfied_by + + def prepare_distribution_metadata(self, finder, build_isolation): + # type: (PackageFinder, bool) -> None + pass diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/distributions/sdist.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/distributions/sdist.py new file mode 100644 index 0000000000..06b9df09cb --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/distributions/sdist.py @@ -0,0 +1,105 @@ +import logging + +from pip._internal.build_env import BuildEnvironment +from pip._internal.distributions.base import AbstractDistribution +from pip._internal.exceptions import InstallationError +from pip._internal.utils.subprocess import runner_with_spinner_message +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Set, Tuple + + from pip._vendor.pkg_resources import Distribution + + from pip._internal.index.package_finder import PackageFinder + + +logger = logging.getLogger(__name__) + + +class SourceDistribution(AbstractDistribution): + """Represents a source distribution. + + The preparation step for these needs metadata for the packages to be + generated, either using PEP 517 or using the legacy `setup.py egg_info`. + """ + + def get_pkg_resources_distribution(self): + # type: () -> Distribution + return self.req.get_dist() + + def prepare_distribution_metadata(self, finder, build_isolation): + # type: (PackageFinder, bool) -> None + # Load pyproject.toml, to determine whether PEP 517 is to be used + self.req.load_pyproject_toml() + + # Set up the build isolation, if this requirement should be isolated + should_isolate = self.req.use_pep517 and build_isolation + if should_isolate: + self._setup_isolation(finder) + + self.req.prepare_metadata() + + def _setup_isolation(self, finder): + # type: (PackageFinder) -> None + def _raise_conflicts(conflicting_with, conflicting_reqs): + # type: (str, Set[Tuple[str, str]]) -> None + format_string = ( + "Some build dependencies for {requirement} " + "conflict with {conflicting_with}: {description}." + ) + error_message = format_string.format( + requirement=self.req, + conflicting_with=conflicting_with, + description=', '.join( + '{} is incompatible with {}'.format(installed, wanted) + for installed, wanted in sorted(conflicting) + ) + ) + raise InstallationError(error_message) + + # Isolate in a BuildEnvironment and install the build-time + # requirements. + pyproject_requires = self.req.pyproject_requires + assert pyproject_requires is not None + + self.req.build_env = BuildEnvironment() + self.req.build_env.install_requirements( + finder, pyproject_requires, 'overlay', + "Installing build dependencies" + ) + conflicting, missing = self.req.build_env.check_requirements( + self.req.requirements_to_check + ) + if conflicting: + _raise_conflicts("PEP 517/518 supported requirements", + conflicting) + if missing: + logger.warning( + "Missing build requirements in pyproject.toml for %s.", + self.req, + ) + logger.warning( + "The project does not specify a build backend, and " + "pip cannot fall back to setuptools without %s.", + " and ".join(map(repr, sorted(missing))) + ) + # Install any extra build dependencies that the backend requests. + # This must be done in a second pass, as the pyproject.toml + # dependencies must be installed before we can call the backend. + with self.req.build_env: + runner = runner_with_spinner_message( + "Getting requirements to build wheel" + ) + backend = self.req.pep517_backend + assert backend is not None + with backend.subprocess_runner(runner): + reqs = backend.get_requires_for_build_wheel() + + conflicting, missing = self.req.build_env.check_requirements(reqs) + if conflicting: + _raise_conflicts("the backend dependencies", conflicting) + self.req.build_env.install_requirements( + finder, missing, 'normal', + "Installing backend dependencies" + ) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/distributions/wheel.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/distributions/wheel.py new file mode 100644 index 0000000000..2adc228627 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/distributions/wheel.py @@ -0,0 +1,37 @@ +from zipfile import ZipFile + +from pip._internal.distributions.base import AbstractDistribution +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.wheel import pkg_resources_distribution_for_wheel + +if MYPY_CHECK_RUNNING: + from pip._vendor.pkg_resources import Distribution + + from pip._internal.index.package_finder import PackageFinder + + +class WheelDistribution(AbstractDistribution): + """Represents a wheel distribution. + + This does not need any preparation as wheels can be directly unpacked. + """ + + def get_pkg_resources_distribution(self): + # type: () -> Distribution + """Loads the metadata from the wheel file into memory and returns a + Distribution that uses it, not relying on the wheel file or + requirement. + """ + # Set as part of preparation during download. + assert self.req.local_file_path + # Wheels are never unnamed. + assert self.req.name + + with ZipFile(self.req.local_file_path, allowZip64=True) as z: + return pkg_resources_distribution_for_wheel( + z, self.req.name, self.req.local_file_path + ) + + def prepare_distribution_metadata(self, finder, build_isolation): + # type: (PackageFinder, bool) -> None + pass diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/exceptions.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/exceptions.py new file mode 100644 index 0000000000..8d7f7fa39b --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/exceptions.py @@ -0,0 +1,391 @@ +"""Exceptions used throughout package""" + +from __future__ import absolute_import + +from itertools import chain, groupby, repeat + +from pip._vendor.six import iteritems + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Any, Dict, List, Optional, Text + + from pip._vendor.pkg_resources import Distribution + from pip._vendor.requests.models import Request, Response + from pip._vendor.six import PY3 + from pip._vendor.six.moves import configparser + + from pip._internal.req.req_install import InstallRequirement + + if PY3: + from hashlib import _Hash + else: + from hashlib import _hash as _Hash + + +class PipError(Exception): + """Base pip exception""" + + +class ConfigurationError(PipError): + """General exception in configuration""" + + +class InstallationError(PipError): + """General exception during installation""" + + +class UninstallationError(PipError): + """General exception during uninstallation""" + + +class NoneMetadataError(PipError): + """ + Raised when accessing "METADATA" or "PKG-INFO" metadata for a + pip._vendor.pkg_resources.Distribution object and + `dist.has_metadata('METADATA')` returns True but + `dist.get_metadata('METADATA')` returns None (and similarly for + "PKG-INFO"). + """ + + def __init__(self, dist, metadata_name): + # type: (Distribution, str) -> None + """ + :param dist: A Distribution object. + :param metadata_name: The name of the metadata being accessed + (can be "METADATA" or "PKG-INFO"). + """ + self.dist = dist + self.metadata_name = metadata_name + + def __str__(self): + # type: () -> str + # Use `dist` in the error message because its stringification + # includes more information, like the version and location. + return ( + 'None {} metadata found for distribution: {}'.format( + self.metadata_name, self.dist, + ) + ) + + +class DistributionNotFound(InstallationError): + """Raised when a distribution cannot be found to satisfy a requirement""" + + +class RequirementsFileParseError(InstallationError): + """Raised when a general error occurs parsing a requirements file line.""" + + +class BestVersionAlreadyInstalled(PipError): + """Raised when the most up-to-date version of a package is already + installed.""" + + +class BadCommand(PipError): + """Raised when virtualenv or a command is not found""" + + +class CommandError(PipError): + """Raised when there is an error in command-line arguments""" + + +class PreviousBuildDirError(PipError): + """Raised when there's a previous conflicting build directory""" + + +class NetworkConnectionError(PipError): + """HTTP connection error""" + + def __init__(self, error_msg, response=None, request=None): + # type: (Text, Response, Request) -> None + """ + Initialize NetworkConnectionError with `request` and `response` + objects. + """ + self.response = response + self.request = request + self.error_msg = error_msg + if (self.response is not None and not self.request and + hasattr(response, 'request')): + self.request = self.response.request + super(NetworkConnectionError, self).__init__( + error_msg, response, request) + + def __str__(self): + # type: () -> str + return str(self.error_msg) + + +class InvalidWheelFilename(InstallationError): + """Invalid wheel filename.""" + + +class UnsupportedWheel(InstallationError): + """Unsupported wheel.""" + + +class MetadataInconsistent(InstallationError): + """Built metadata contains inconsistent information. + + This is raised when the metadata contains values (e.g. name and version) + that do not match the information previously obtained from sdist filename + or user-supplied ``#egg=`` value. + """ + def __init__(self, ireq, field, built): + # type: (InstallRequirement, str, Any) -> None + self.ireq = ireq + self.field = field + self.built = built + + def __str__(self): + # type: () -> str + return "Requested {} has different {} in metadata: {!r}".format( + self.ireq, self.field, self.built, + ) + + +class InstallationSubprocessError(InstallationError): + """A subprocess call failed during installation.""" + def __init__(self, returncode, description): + # type: (int, str) -> None + self.returncode = returncode + self.description = description + + def __str__(self): + # type: () -> str + return ( + "Command errored out with exit status {}: {} " + "Check the logs for full command output." + ).format(self.returncode, self.description) + + +class HashErrors(InstallationError): + """Multiple HashError instances rolled into one for reporting""" + + def __init__(self): + # type: () -> None + self.errors = [] # type: List[HashError] + + def append(self, error): + # type: (HashError) -> None + self.errors.append(error) + + def __str__(self): + # type: () -> str + lines = [] + self.errors.sort(key=lambda e: e.order) + for cls, errors_of_cls in groupby(self.errors, lambda e: e.__class__): + lines.append(cls.head) + lines.extend(e.body() for e in errors_of_cls) + if lines: + return '\n'.join(lines) + return '' + + def __nonzero__(self): + # type: () -> bool + return bool(self.errors) + + def __bool__(self): + # type: () -> bool + return self.__nonzero__() + + +class HashError(InstallationError): + """ + A failure to verify a package against known-good hashes + + :cvar order: An int sorting hash exception classes by difficulty of + recovery (lower being harder), so the user doesn't bother fretting + about unpinned packages when he has deeper issues, like VCS + dependencies, to deal with. Also keeps error reports in a + deterministic order. + :cvar head: A section heading for display above potentially many + exceptions of this kind + :ivar req: The InstallRequirement that triggered this error. This is + pasted on after the exception is instantiated, because it's not + typically available earlier. + + """ + req = None # type: Optional[InstallRequirement] + head = '' + order = -1 # type: int + + def body(self): + # type: () -> str + """Return a summary of me for display under the heading. + + This default implementation simply prints a description of the + triggering requirement. + + :param req: The InstallRequirement that provoked this error, with + its link already populated by the resolver's _populate_link(). + + """ + return ' {}'.format(self._requirement_name()) + + def __str__(self): + # type: () -> str + return '{}\n{}'.format(self.head, self.body()) + + def _requirement_name(self): + # type: () -> str + """Return a description of the requirement that triggered me. + + This default implementation returns long description of the req, with + line numbers + + """ + return str(self.req) if self.req else 'unknown package' + + +class VcsHashUnsupported(HashError): + """A hash was provided for a version-control-system-based requirement, but + we don't have a method for hashing those.""" + + order = 0 + head = ("Can't verify hashes for these requirements because we don't " + "have a way to hash version control repositories:") + + +class DirectoryUrlHashUnsupported(HashError): + """A hash was provided for a version-control-system-based requirement, but + we don't have a method for hashing those.""" + + order = 1 + head = ("Can't verify hashes for these file:// requirements because they " + "point to directories:") + + +class HashMissing(HashError): + """A hash was needed for a requirement but is absent.""" + + order = 2 + head = ('Hashes are required in --require-hashes mode, but they are ' + 'missing from some requirements. Here is a list of those ' + 'requirements along with the hashes their downloaded archives ' + 'actually had. Add lines like these to your requirements files to ' + 'prevent tampering. (If you did not enable --require-hashes ' + 'manually, note that it turns on automatically when any package ' + 'has a hash.)') + + def __init__(self, gotten_hash): + # type: (str) -> None + """ + :param gotten_hash: The hash of the (possibly malicious) archive we + just downloaded + """ + self.gotten_hash = gotten_hash + + def body(self): + # type: () -> str + # Dodge circular import. + from pip._internal.utils.hashes import FAVORITE_HASH + + package = None + if self.req: + # In the case of URL-based requirements, display the original URL + # seen in the requirements file rather than the package name, + # so the output can be directly copied into the requirements file. + package = (self.req.original_link if self.req.original_link + # In case someone feeds something downright stupid + # to InstallRequirement's constructor. + else getattr(self.req, 'req', None)) + return ' {} --hash={}:{}'.format(package or 'unknown package', + FAVORITE_HASH, + self.gotten_hash) + + +class HashUnpinned(HashError): + """A requirement had a hash specified but was not pinned to a specific + version.""" + + order = 3 + head = ('In --require-hashes mode, all requirements must have their ' + 'versions pinned with ==. These do not:') + + +class HashMismatch(HashError): + """ + Distribution file hash values don't match. + + :ivar package_name: The name of the package that triggered the hash + mismatch. Feel free to write to this after the exception is raise to + improve its error message. + + """ + order = 4 + head = ('THESE PACKAGES DO NOT MATCH THE HASHES FROM THE REQUIREMENTS ' + 'FILE. If you have updated the package versions, please update ' + 'the hashes. Otherwise, examine the package contents carefully; ' + 'someone may have tampered with them.') + + def __init__(self, allowed, gots): + # type: (Dict[str, List[str]], Dict[str, _Hash]) -> None + """ + :param allowed: A dict of algorithm names pointing to lists of allowed + hex digests + :param gots: A dict of algorithm names pointing to hashes we + actually got from the files under suspicion + """ + self.allowed = allowed + self.gots = gots + + def body(self): + # type: () -> str + return ' {}:\n{}'.format(self._requirement_name(), + self._hash_comparison()) + + def _hash_comparison(self): + # type: () -> str + """ + Return a comparison of actual and expected hash values. + + Example:: + + Expected sha256 abcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcde + or 123451234512345123451234512345123451234512345 + Got bcdefbcdefbcdefbcdefbcdefbcdefbcdefbcdefbcdef + + """ + def hash_then_or(hash_name): + # type: (str) -> chain[str] + # For now, all the decent hashes have 6-char names, so we can get + # away with hard-coding space literals. + return chain([hash_name], repeat(' or')) + + lines = [] # type: List[str] + for hash_name, expecteds in iteritems(self.allowed): + prefix = hash_then_or(hash_name) + lines.extend((' Expected {} {}'.format(next(prefix), e)) + for e in expecteds) + lines.append(' Got {}\n'.format( + self.gots[hash_name].hexdigest())) + return '\n'.join(lines) + + +class UnsupportedPythonVersion(InstallationError): + """Unsupported python version according to Requires-Python package + metadata.""" + + +class ConfigurationFileCouldNotBeLoaded(ConfigurationError): + """When there are errors while loading a configuration file + """ + + def __init__(self, reason="could not be loaded", fname=None, error=None): + # type: (str, Optional[str], Optional[configparser.Error]) -> None + super(ConfigurationFileCouldNotBeLoaded, self).__init__(error) + self.reason = reason + self.fname = fname + self.error = error + + def __str__(self): + # type: () -> str + if self.fname is not None: + message_part = " in {}.".format(self.fname) + else: + assert self.error is not None + message_part = ".\n{}\n".format(self.error) + return "Configuration file {}{}".format(self.reason, message_part) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/index/__init__.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/index/__init__.py new file mode 100644 index 0000000000..7a17b7b3b6 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/index/__init__.py @@ -0,0 +1,2 @@ +"""Index interaction code +""" diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/index/collector.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/index/collector.py new file mode 100644 index 0000000000..b850b8cbed --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/index/collector.py @@ -0,0 +1,667 @@ +""" +The main purpose of this module is to expose LinkCollector.collect_links(). +""" + +import cgi +import functools +import itertools +import logging +import mimetypes +import os +import re +from collections import OrderedDict + +from pip._vendor import html5lib, requests +from pip._vendor.distlib.compat import unescape +from pip._vendor.requests.exceptions import RetryError, SSLError +from pip._vendor.six.moves.urllib import parse as urllib_parse +from pip._vendor.six.moves.urllib import request as urllib_request + +from pip._internal.exceptions import NetworkConnectionError +from pip._internal.models.link import Link +from pip._internal.models.search_scope import SearchScope +from pip._internal.network.utils import raise_for_status +from pip._internal.utils.compat import lru_cache +from pip._internal.utils.filetypes import is_archive_file +from pip._internal.utils.misc import pairwise, redact_auth_from_url +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.urls import path_to_url, url_to_path +from pip._internal.vcs import is_url, vcs + +if MYPY_CHECK_RUNNING: + import xml.etree.ElementTree + from optparse import Values + from typing import ( + Callable, + Iterable, + List, + MutableMapping, + Optional, + Sequence, + Tuple, + Union, + ) + + from pip._vendor.requests import Response + + from pip._internal.network.session import PipSession + + HTMLElement = xml.etree.ElementTree.Element + ResponseHeaders = MutableMapping[str, str] + + +logger = logging.getLogger(__name__) + + +def _match_vcs_scheme(url): + # type: (str) -> Optional[str] + """Look for VCS schemes in the URL. + + Returns the matched VCS scheme, or None if there's no match. + """ + for scheme in vcs.schemes: + if url.lower().startswith(scheme) and url[len(scheme)] in '+:': + return scheme + return None + + +class _NotHTML(Exception): + def __init__(self, content_type, request_desc): + # type: (str, str) -> None + super(_NotHTML, self).__init__(content_type, request_desc) + self.content_type = content_type + self.request_desc = request_desc + + +def _ensure_html_header(response): + # type: (Response) -> None + """Check the Content-Type header to ensure the response contains HTML. + + Raises `_NotHTML` if the content type is not text/html. + """ + content_type = response.headers.get("Content-Type", "") + if not content_type.lower().startswith("text/html"): + raise _NotHTML(content_type, response.request.method) + + +class _NotHTTP(Exception): + pass + + +def _ensure_html_response(url, session): + # type: (str, PipSession) -> None + """Send a HEAD request to the URL, and ensure the response contains HTML. + + Raises `_NotHTTP` if the URL is not available for a HEAD request, or + `_NotHTML` if the content type is not text/html. + """ + scheme, netloc, path, query, fragment = urllib_parse.urlsplit(url) + if scheme not in {'http', 'https'}: + raise _NotHTTP() + + resp = session.head(url, allow_redirects=True) + raise_for_status(resp) + + _ensure_html_header(resp) + + +def _get_html_response(url, session): + # type: (str, PipSession) -> Response + """Access an HTML page with GET, and return the response. + + This consists of three parts: + + 1. If the URL looks suspiciously like an archive, send a HEAD first to + check the Content-Type is HTML, to avoid downloading a large file. + Raise `_NotHTTP` if the content type cannot be determined, or + `_NotHTML` if it is not HTML. + 2. Actually perform the request. Raise HTTP exceptions on network failures. + 3. Check the Content-Type header to make sure we got HTML, and raise + `_NotHTML` otherwise. + """ + if is_archive_file(Link(url).filename): + _ensure_html_response(url, session=session) + + logger.debug('Getting page %s', redact_auth_from_url(url)) + + resp = session.get( + url, + headers={ + "Accept": "text/html", + # We don't want to blindly returned cached data for + # /simple/, because authors generally expecting that + # twine upload && pip install will function, but if + # they've done a pip install in the last ~10 minutes + # it won't. Thus by setting this to zero we will not + # blindly use any cached data, however the benefit of + # using max-age=0 instead of no-cache, is that we will + # still support conditional requests, so we will still + # minimize traffic sent in cases where the page hasn't + # changed at all, we will just always incur the round + # trip for the conditional GET now instead of only + # once per 10 minutes. + # For more information, please see pypa/pip#5670. + "Cache-Control": "max-age=0", + }, + ) + raise_for_status(resp) + + # The check for archives above only works if the url ends with + # something that looks like an archive. However that is not a + # requirement of an url. Unless we issue a HEAD request on every + # url we cannot know ahead of time for sure if something is HTML + # or not. However we can check after we've downloaded it. + _ensure_html_header(resp) + + return resp + + +def _get_encoding_from_headers(headers): + # type: (ResponseHeaders) -> Optional[str] + """Determine if we have any encoding information in our headers. + """ + if headers and "Content-Type" in headers: + content_type, params = cgi.parse_header(headers["Content-Type"]) + if "charset" in params: + return params['charset'] + return None + + +def _determine_base_url(document, page_url): + # type: (HTMLElement, str) -> str + """Determine the HTML document's base URL. + + This looks for a ```` tag in the HTML document. If present, its href + attribute denotes the base URL of anchor tags in the document. If there is + no such tag (or if it does not have a valid href attribute), the HTML + file's URL is used as the base URL. + + :param document: An HTML document representation. The current + implementation expects the result of ``html5lib.parse()``. + :param page_url: The URL of the HTML document. + """ + for base in document.findall(".//base"): + href = base.get("href") + if href is not None: + return href + return page_url + + +def _clean_url_path_part(part): + # type: (str) -> str + """ + Clean a "part" of a URL path (i.e. after splitting on "@" characters). + """ + # We unquote prior to quoting to make sure nothing is double quoted. + return urllib_parse.quote(urllib_parse.unquote(part)) + + +def _clean_file_url_path(part): + # type: (str) -> str + """ + Clean the first part of a URL path that corresponds to a local + filesystem path (i.e. the first part after splitting on "@" characters). + """ + # We unquote prior to quoting to make sure nothing is double quoted. + # Also, on Windows the path part might contain a drive letter which + # should not be quoted. On Linux where drive letters do not + # exist, the colon should be quoted. We rely on urllib.request + # to do the right thing here. + return urllib_request.pathname2url(urllib_request.url2pathname(part)) + + +# percent-encoded: / +_reserved_chars_re = re.compile('(@|%2F)', re.IGNORECASE) + + +def _clean_url_path(path, is_local_path): + # type: (str, bool) -> str + """ + Clean the path portion of a URL. + """ + if is_local_path: + clean_func = _clean_file_url_path + else: + clean_func = _clean_url_path_part + + # Split on the reserved characters prior to cleaning so that + # revision strings in VCS URLs are properly preserved. + parts = _reserved_chars_re.split(path) + + cleaned_parts = [] + for to_clean, reserved in pairwise(itertools.chain(parts, [''])): + cleaned_parts.append(clean_func(to_clean)) + # Normalize %xx escapes (e.g. %2f -> %2F) + cleaned_parts.append(reserved.upper()) + + return ''.join(cleaned_parts) + + +def _clean_link(url): + # type: (str) -> str + """ + Make sure a link is fully quoted. + For example, if ' ' occurs in the URL, it will be replaced with "%20", + and without double-quoting other characters. + """ + # Split the URL into parts according to the general structure + # `scheme://netloc/path;parameters?query#fragment`. + result = urllib_parse.urlparse(url) + # If the netloc is empty, then the URL refers to a local filesystem path. + is_local_path = not result.netloc + path = _clean_url_path(result.path, is_local_path=is_local_path) + return urllib_parse.urlunparse(result._replace(path=path)) + + +def _create_link_from_element( + anchor, # type: HTMLElement + page_url, # type: str + base_url, # type: str +): + # type: (...) -> Optional[Link] + """ + Convert an anchor element in a simple repository page to a Link. + """ + href = anchor.get("href") + if not href: + return None + + url = _clean_link(urllib_parse.urljoin(base_url, href)) + pyrequire = anchor.get('data-requires-python') + pyrequire = unescape(pyrequire) if pyrequire else None + + yanked_reason = anchor.get('data-yanked') + if yanked_reason: + # This is a unicode string in Python 2 (and 3). + yanked_reason = unescape(yanked_reason) + + link = Link( + url, + comes_from=page_url, + requires_python=pyrequire, + yanked_reason=yanked_reason, + ) + + return link + + +class CacheablePageContent(object): + def __init__(self, page): + # type: (HTMLPage) -> None + assert page.cache_link_parsing + self.page = page + + def __eq__(self, other): + # type: (object) -> bool + return (isinstance(other, type(self)) and + self.page.url == other.page.url) + + def __hash__(self): + # type: () -> int + return hash(self.page.url) + + +def with_cached_html_pages( + fn, # type: Callable[[HTMLPage], Iterable[Link]] +): + # type: (...) -> Callable[[HTMLPage], List[Link]] + """ + Given a function that parses an Iterable[Link] from an HTMLPage, cache the + function's result (keyed by CacheablePageContent), unless the HTMLPage + `page` has `page.cache_link_parsing == False`. + """ + + @lru_cache(maxsize=None) + def wrapper(cacheable_page): + # type: (CacheablePageContent) -> List[Link] + return list(fn(cacheable_page.page)) + + @functools.wraps(fn) + def wrapper_wrapper(page): + # type: (HTMLPage) -> List[Link] + if page.cache_link_parsing: + return wrapper(CacheablePageContent(page)) + return list(fn(page)) + + return wrapper_wrapper + + +@with_cached_html_pages +def parse_links(page): + # type: (HTMLPage) -> Iterable[Link] + """ + Parse an HTML document, and yield its anchor elements as Link objects. + """ + document = html5lib.parse( + page.content, + transport_encoding=page.encoding, + namespaceHTMLElements=False, + ) + + url = page.url + base_url = _determine_base_url(document, url) + for anchor in document.findall(".//a"): + link = _create_link_from_element( + anchor, + page_url=url, + base_url=base_url, + ) + if link is None: + continue + yield link + + +class HTMLPage(object): + """Represents one page, along with its URL""" + + def __init__( + self, + content, # type: bytes + encoding, # type: Optional[str] + url, # type: str + cache_link_parsing=True, # type: bool + ): + # type: (...) -> None + """ + :param encoding: the encoding to decode the given content. + :param url: the URL from which the HTML was downloaded. + :param cache_link_parsing: whether links parsed from this page's url + should be cached. PyPI index urls should + have this set to False, for example. + """ + self.content = content + self.encoding = encoding + self.url = url + self.cache_link_parsing = cache_link_parsing + + def __str__(self): + # type: () -> str + return redact_auth_from_url(self.url) + + +def _handle_get_page_fail( + link, # type: Link + reason, # type: Union[str, Exception] + meth=None # type: Optional[Callable[..., None]] +): + # type: (...) -> None + if meth is None: + meth = logger.debug + meth("Could not fetch URL %s: %s - skipping", link, reason) + + +def _make_html_page(response, cache_link_parsing=True): + # type: (Response, bool) -> HTMLPage + encoding = _get_encoding_from_headers(response.headers) + return HTMLPage( + response.content, + encoding=encoding, + url=response.url, + cache_link_parsing=cache_link_parsing) + + +def _get_html_page(link, session=None): + # type: (Link, Optional[PipSession]) -> Optional[HTMLPage] + if session is None: + raise TypeError( + "_get_html_page() missing 1 required keyword argument: 'session'" + ) + + url = link.url.split('#', 1)[0] + + # Check for VCS schemes that do not support lookup as web pages. + vcs_scheme = _match_vcs_scheme(url) + if vcs_scheme: + logger.warning('Cannot look at %s URL %s because it does not support ' + 'lookup as web pages.', vcs_scheme, link) + return None + + # Tack index.html onto file:// URLs that point to directories + scheme, _, path, _, _, _ = urllib_parse.urlparse(url) + if (scheme == 'file' and os.path.isdir(urllib_request.url2pathname(path))): + # add trailing slash if not present so urljoin doesn't trim + # final segment + if not url.endswith('/'): + url += '/' + url = urllib_parse.urljoin(url, 'index.html') + logger.debug(' file: URL is directory, getting %s', url) + + try: + resp = _get_html_response(url, session=session) + except _NotHTTP: + logger.warning( + 'Skipping page %s because it looks like an archive, and cannot ' + 'be checked by a HTTP HEAD request.', link, + ) + except _NotHTML as exc: + logger.warning( + 'Skipping page %s because the %s request got Content-Type: %s.' + 'The only supported Content-Type is text/html', + link, exc.request_desc, exc.content_type, + ) + except NetworkConnectionError as exc: + _handle_get_page_fail(link, exc) + except RetryError as exc: + _handle_get_page_fail(link, exc) + except SSLError as exc: + reason = "There was a problem confirming the ssl certificate: " + reason += str(exc) + _handle_get_page_fail(link, reason, meth=logger.info) + except requests.ConnectionError as exc: + _handle_get_page_fail(link, "connection error: {}".format(exc)) + except requests.Timeout: + _handle_get_page_fail(link, "timed out") + else: + return _make_html_page(resp, + cache_link_parsing=link.cache_link_parsing) + return None + + +def _remove_duplicate_links(links): + # type: (Iterable[Link]) -> List[Link] + """ + Return a list of links, with duplicates removed and ordering preserved. + """ + # We preserve the ordering when removing duplicates because we can. + return list(OrderedDict.fromkeys(links)) + + +def group_locations(locations, expand_dir=False): + # type: (Sequence[str], bool) -> Tuple[List[str], List[str]] + """ + Divide a list of locations into two groups: "files" (archives) and "urls." + + :return: A pair of lists (files, urls). + """ + files = [] + urls = [] + + # puts the url for the given file path into the appropriate list + def sort_path(path): + # type: (str) -> None + url = path_to_url(path) + if mimetypes.guess_type(url, strict=False)[0] == 'text/html': + urls.append(url) + else: + files.append(url) + + for url in locations: + + is_local_path = os.path.exists(url) + is_file_url = url.startswith('file:') + + if is_local_path or is_file_url: + if is_local_path: + path = url + else: + path = url_to_path(url) + if os.path.isdir(path): + if expand_dir: + path = os.path.realpath(path) + for item in os.listdir(path): + sort_path(os.path.join(path, item)) + elif is_file_url: + urls.append(url) + else: + logger.warning( + "Path '%s' is ignored: it is a directory.", path, + ) + elif os.path.isfile(path): + sort_path(path) + else: + logger.warning( + "Url '%s' is ignored: it is neither a file " + "nor a directory.", url, + ) + elif is_url(url): + # Only add url with clear scheme + urls.append(url) + else: + logger.warning( + "Url '%s' is ignored. It is either a non-existing " + "path or lacks a specific scheme.", url, + ) + + return files, urls + + +class CollectedLinks(object): + + """ + Encapsulates the return value of a call to LinkCollector.collect_links(). + + The return value includes both URLs to project pages containing package + links, as well as individual package Link objects collected from other + sources. + + This info is stored separately as: + + (1) links from the configured file locations, + (2) links from the configured find_links, and + (3) urls to HTML project pages, as described by the PEP 503 simple + repository API. + """ + + def __init__( + self, + files, # type: List[Link] + find_links, # type: List[Link] + project_urls, # type: List[Link] + ): + # type: (...) -> None + """ + :param files: Links from file locations. + :param find_links: Links from find_links. + :param project_urls: URLs to HTML project pages, as described by + the PEP 503 simple repository API. + """ + self.files = files + self.find_links = find_links + self.project_urls = project_urls + + +class LinkCollector(object): + + """ + Responsible for collecting Link objects from all configured locations, + making network requests as needed. + + The class's main method is its collect_links() method. + """ + + def __init__( + self, + session, # type: PipSession + search_scope, # type: SearchScope + ): + # type: (...) -> None + self.search_scope = search_scope + self.session = session + + @classmethod + def create(cls, session, options, suppress_no_index=False): + # type: (PipSession, Values, bool) -> LinkCollector + """ + :param session: The Session to use to make requests. + :param suppress_no_index: Whether to ignore the --no-index option + when constructing the SearchScope object. + """ + index_urls = [options.index_url] + options.extra_index_urls + if options.no_index and not suppress_no_index: + logger.debug( + 'Ignoring indexes: %s', + ','.join(redact_auth_from_url(url) for url in index_urls), + ) + index_urls = [] + + # Make sure find_links is a list before passing to create(). + find_links = options.find_links or [] + + search_scope = SearchScope.create( + find_links=find_links, index_urls=index_urls, + ) + link_collector = LinkCollector( + session=session, search_scope=search_scope, + ) + return link_collector + + @property + def find_links(self): + # type: () -> List[str] + return self.search_scope.find_links + + def fetch_page(self, location): + # type: (Link) -> Optional[HTMLPage] + """ + Fetch an HTML page containing package links. + """ + return _get_html_page(location, session=self.session) + + def collect_links(self, project_name): + # type: (str) -> CollectedLinks + """Find all available links for the given project name. + + :return: All the Link objects (unfiltered), as a CollectedLinks object. + """ + search_scope = self.search_scope + index_locations = search_scope.get_index_urls_locations(project_name) + index_file_loc, index_url_loc = group_locations(index_locations) + fl_file_loc, fl_url_loc = group_locations( + self.find_links, expand_dir=True, + ) + + file_links = [ + Link(url) for url in itertools.chain(index_file_loc, fl_file_loc) + ] + + # We trust every directly linked archive in find_links + find_link_links = [Link(url, '-f') for url in self.find_links] + + # We trust every url that the user has given us whether it was given + # via --index-url or --find-links. + # We want to filter out anything that does not have a secure origin. + url_locations = [ + link for link in itertools.chain( + # Mark PyPI indices as "cache_link_parsing == False" -- this + # will avoid caching the result of parsing the page for links. + (Link(url, cache_link_parsing=False) for url in index_url_loc), + (Link(url) for url in fl_url_loc), + ) + if self.session.is_secure_origin(link) + ] + + url_locations = _remove_duplicate_links(url_locations) + lines = [ + '{} location(s) to search for versions of {}:'.format( + len(url_locations), project_name, + ), + ] + for link in url_locations: + lines.append('* {}'.format(link)) + logger.debug('\n'.join(lines)) + + return CollectedLinks( + files=file_links, + find_links=find_link_links, + project_urls=url_locations, + ) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/index/package_finder.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/index/package_finder.py new file mode 100644 index 0000000000..9f39631dde --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/index/package_finder.py @@ -0,0 +1,1015 @@ +"""Routines related to PyPI, indexes""" + +# The following comment should be removed at some point in the future. +# mypy: strict-optional=False + +from __future__ import absolute_import + +import logging +import re + +from pip._vendor.packaging import specifiers +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.packaging.version import parse as parse_version + +from pip._internal.exceptions import ( + BestVersionAlreadyInstalled, + DistributionNotFound, + InvalidWheelFilename, + UnsupportedWheel, +) +from pip._internal.index.collector import parse_links +from pip._internal.models.candidate import InstallationCandidate +from pip._internal.models.format_control import FormatControl +from pip._internal.models.link import Link +from pip._internal.models.selection_prefs import SelectionPreferences +from pip._internal.models.target_python import TargetPython +from pip._internal.models.wheel import Wheel +from pip._internal.utils.compat import lru_cache +from pip._internal.utils.filetypes import WHEEL_EXTENSION +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import build_netloc +from pip._internal.utils.packaging import check_requires_python +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.unpacking import SUPPORTED_EXTENSIONS +from pip._internal.utils.urls import url_to_path + +if MYPY_CHECK_RUNNING: + from typing import FrozenSet, Iterable, List, Optional, Set, Text, Tuple, Union + + from pip._vendor.packaging.tags import Tag + from pip._vendor.packaging.version import _BaseVersion + + from pip._internal.index.collector import LinkCollector + from pip._internal.models.search_scope import SearchScope + from pip._internal.req import InstallRequirement + from pip._internal.utils.hashes import Hashes + + BuildTag = Union[Tuple[()], Tuple[int, str]] + CandidateSortingKey = ( + Tuple[int, int, int, _BaseVersion, BuildTag, Optional[int]] + ) + + +__all__ = ['FormatControl', 'BestCandidateResult', 'PackageFinder'] + + +logger = logging.getLogger(__name__) + + +def _check_link_requires_python( + link, # type: Link + version_info, # type: Tuple[int, int, int] + ignore_requires_python=False, # type: bool +): + # type: (...) -> bool + """ + Return whether the given Python version is compatible with a link's + "Requires-Python" value. + + :param version_info: A 3-tuple of ints representing the Python + major-minor-micro version to check. + :param ignore_requires_python: Whether to ignore the "Requires-Python" + value if the given Python version isn't compatible. + """ + try: + is_compatible = check_requires_python( + link.requires_python, version_info=version_info, + ) + except specifiers.InvalidSpecifier: + logger.debug( + "Ignoring invalid Requires-Python (%r) for link: %s", + link.requires_python, link, + ) + else: + if not is_compatible: + version = '.'.join(map(str, version_info)) + if not ignore_requires_python: + logger.debug( + 'Link requires a different Python (%s not in: %r): %s', + version, link.requires_python, link, + ) + return False + + logger.debug( + 'Ignoring failed Requires-Python check (%s not in: %r) ' + 'for link: %s', + version, link.requires_python, link, + ) + + return True + + +class LinkEvaluator(object): + + """ + Responsible for evaluating links for a particular project. + """ + + _py_version_re = re.compile(r'-py([123]\.?[0-9]?)$') + + # Don't include an allow_yanked default value to make sure each call + # site considers whether yanked releases are allowed. This also causes + # that decision to be made explicit in the calling code, which helps + # people when reading the code. + def __init__( + self, + project_name, # type: str + canonical_name, # type: str + formats, # type: FrozenSet[str] + target_python, # type: TargetPython + allow_yanked, # type: bool + ignore_requires_python=None, # type: Optional[bool] + ): + # type: (...) -> None + """ + :param project_name: The user supplied package name. + :param canonical_name: The canonical package name. + :param formats: The formats allowed for this package. Should be a set + with 'binary' or 'source' or both in it. + :param target_python: The target Python interpreter to use when + evaluating link compatibility. This is used, for example, to + check wheel compatibility, as well as when checking the Python + version, e.g. the Python version embedded in a link filename + (or egg fragment) and against an HTML link's optional PEP 503 + "data-requires-python" attribute. + :param allow_yanked: Whether files marked as yanked (in the sense + of PEP 592) are permitted to be candidates for install. + :param ignore_requires_python: Whether to ignore incompatible + PEP 503 "data-requires-python" values in HTML links. Defaults + to False. + """ + if ignore_requires_python is None: + ignore_requires_python = False + + self._allow_yanked = allow_yanked + self._canonical_name = canonical_name + self._ignore_requires_python = ignore_requires_python + self._formats = formats + self._target_python = target_python + + self.project_name = project_name + + def evaluate_link(self, link): + # type: (Link) -> Tuple[bool, Optional[Text]] + """ + Determine whether a link is a candidate for installation. + + :return: A tuple (is_candidate, result), where `result` is (1) a + version string if `is_candidate` is True, and (2) if + `is_candidate` is False, an optional string to log the reason + the link fails to qualify. + """ + version = None + if link.is_yanked and not self._allow_yanked: + reason = link.yanked_reason or '' + # Mark this as a unicode string to prevent "UnicodeEncodeError: + # 'ascii' codec can't encode character" in Python 2 when + # the reason contains non-ascii characters. + return (False, u'yanked for reason: {}'.format(reason)) + + if link.egg_fragment: + egg_info = link.egg_fragment + ext = link.ext + else: + egg_info, ext = link.splitext() + if not ext: + return (False, 'not a file') + if ext not in SUPPORTED_EXTENSIONS: + return (False, 'unsupported archive format: {}'.format(ext)) + if "binary" not in self._formats and ext == WHEEL_EXTENSION: + reason = 'No binaries permitted for {}'.format( + self.project_name) + return (False, reason) + if "macosx10" in link.path and ext == '.zip': + return (False, 'macosx10 one') + if ext == WHEEL_EXTENSION: + try: + wheel = Wheel(link.filename) + except InvalidWheelFilename: + return (False, 'invalid wheel filename') + if canonicalize_name(wheel.name) != self._canonical_name: + reason = 'wrong project name (not {})'.format( + self.project_name) + return (False, reason) + + supported_tags = self._target_python.get_tags() + if not wheel.supported(supported_tags): + # Include the wheel's tags in the reason string to + # simplify troubleshooting compatibility issues. + file_tags = wheel.get_formatted_file_tags() + reason = ( + "none of the wheel's tags match: {}".format( + ', '.join(file_tags) + ) + ) + return (False, reason) + + version = wheel.version + + # This should be up by the self.ok_binary check, but see issue 2700. + if "source" not in self._formats and ext != WHEEL_EXTENSION: + reason = 'No sources permitted for {}'.format(self.project_name) + return (False, reason) + + if not version: + version = _extract_version_from_fragment( + egg_info, self._canonical_name, + ) + if not version: + reason = 'Missing project version for {}'.format(self.project_name) + return (False, reason) + + match = self._py_version_re.search(version) + if match: + version = version[:match.start()] + py_version = match.group(1) + if py_version != self._target_python.py_version: + return (False, 'Python version is incorrect') + + supports_python = _check_link_requires_python( + link, version_info=self._target_python.py_version_info, + ignore_requires_python=self._ignore_requires_python, + ) + if not supports_python: + # Return None for the reason text to suppress calling + # _log_skipped_link(). + return (False, None) + + logger.debug('Found link %s, version: %s', link, version) + + return (True, version) + + +def filter_unallowed_hashes( + candidates, # type: List[InstallationCandidate] + hashes, # type: Hashes + project_name, # type: str +): + # type: (...) -> List[InstallationCandidate] + """ + Filter out candidates whose hashes aren't allowed, and return a new + list of candidates. + + If at least one candidate has an allowed hash, then all candidates with + either an allowed hash or no hash specified are returned. Otherwise, + the given candidates are returned. + + Including the candidates with no hash specified when there is a match + allows a warning to be logged if there is a more preferred candidate + with no hash specified. Returning all candidates in the case of no + matches lets pip report the hash of the candidate that would otherwise + have been installed (e.g. permitting the user to more easily update + their requirements file with the desired hash). + """ + if not hashes: + logger.debug( + 'Given no hashes to check %s links for project %r: ' + 'discarding no candidates', + len(candidates), + project_name, + ) + # Make sure we're not returning back the given value. + return list(candidates) + + matches_or_no_digest = [] + # Collect the non-matches for logging purposes. + non_matches = [] + match_count = 0 + for candidate in candidates: + link = candidate.link + if not link.has_hash: + pass + elif link.is_hash_allowed(hashes=hashes): + match_count += 1 + else: + non_matches.append(candidate) + continue + + matches_or_no_digest.append(candidate) + + if match_count: + filtered = matches_or_no_digest + else: + # Make sure we're not returning back the given value. + filtered = list(candidates) + + if len(filtered) == len(candidates): + discard_message = 'discarding no candidates' + else: + discard_message = 'discarding {} non-matches:\n {}'.format( + len(non_matches), + '\n '.join(str(candidate.link) for candidate in non_matches) + ) + + logger.debug( + 'Checked %s links for project %r against %s hashes ' + '(%s matches, %s no digest): %s', + len(candidates), + project_name, + hashes.digest_count, + match_count, + len(matches_or_no_digest) - match_count, + discard_message + ) + + return filtered + + +class CandidatePreferences(object): + + """ + Encapsulates some of the preferences for filtering and sorting + InstallationCandidate objects. + """ + + def __init__( + self, + prefer_binary=False, # type: bool + allow_all_prereleases=False, # type: bool + ): + # type: (...) -> None + """ + :param allow_all_prereleases: Whether to allow all pre-releases. + """ + self.allow_all_prereleases = allow_all_prereleases + self.prefer_binary = prefer_binary + + +class BestCandidateResult(object): + """A collection of candidates, returned by `PackageFinder.find_best_candidate`. + + This class is only intended to be instantiated by CandidateEvaluator's + `compute_best_candidate()` method. + """ + + def __init__( + self, + candidates, # type: List[InstallationCandidate] + applicable_candidates, # type: List[InstallationCandidate] + best_candidate, # type: Optional[InstallationCandidate] + ): + # type: (...) -> None + """ + :param candidates: A sequence of all available candidates found. + :param applicable_candidates: The applicable candidates. + :param best_candidate: The most preferred candidate found, or None + if no applicable candidates were found. + """ + assert set(applicable_candidates) <= set(candidates) + + if best_candidate is None: + assert not applicable_candidates + else: + assert best_candidate in applicable_candidates + + self._applicable_candidates = applicable_candidates + self._candidates = candidates + + self.best_candidate = best_candidate + + def iter_all(self): + # type: () -> Iterable[InstallationCandidate] + """Iterate through all candidates. + """ + return iter(self._candidates) + + def iter_applicable(self): + # type: () -> Iterable[InstallationCandidate] + """Iterate through the applicable candidates. + """ + return iter(self._applicable_candidates) + + +class CandidateEvaluator(object): + + """ + Responsible for filtering and sorting candidates for installation based + on what tags are valid. + """ + + @classmethod + def create( + cls, + project_name, # type: str + target_python=None, # type: Optional[TargetPython] + prefer_binary=False, # type: bool + allow_all_prereleases=False, # type: bool + specifier=None, # type: Optional[specifiers.BaseSpecifier] + hashes=None, # type: Optional[Hashes] + ): + # type: (...) -> CandidateEvaluator + """Create a CandidateEvaluator object. + + :param target_python: The target Python interpreter to use when + checking compatibility. If None (the default), a TargetPython + object will be constructed from the running Python. + :param specifier: An optional object implementing `filter` + (e.g. `packaging.specifiers.SpecifierSet`) to filter applicable + versions. + :param hashes: An optional collection of allowed hashes. + """ + if target_python is None: + target_python = TargetPython() + if specifier is None: + specifier = specifiers.SpecifierSet() + + supported_tags = target_python.get_tags() + + return cls( + project_name=project_name, + supported_tags=supported_tags, + specifier=specifier, + prefer_binary=prefer_binary, + allow_all_prereleases=allow_all_prereleases, + hashes=hashes, + ) + + def __init__( + self, + project_name, # type: str + supported_tags, # type: List[Tag] + specifier, # type: specifiers.BaseSpecifier + prefer_binary=False, # type: bool + allow_all_prereleases=False, # type: bool + hashes=None, # type: Optional[Hashes] + ): + # type: (...) -> None + """ + :param supported_tags: The PEP 425 tags supported by the target + Python in order of preference (most preferred first). + """ + self._allow_all_prereleases = allow_all_prereleases + self._hashes = hashes + self._prefer_binary = prefer_binary + self._project_name = project_name + self._specifier = specifier + self._supported_tags = supported_tags + + def get_applicable_candidates( + self, + candidates, # type: List[InstallationCandidate] + ): + # type: (...) -> List[InstallationCandidate] + """ + Return the applicable candidates from a list of candidates. + """ + # Using None infers from the specifier instead. + allow_prereleases = self._allow_all_prereleases or None + specifier = self._specifier + versions = { + str(v) for v in specifier.filter( + # We turn the version object into a str here because otherwise + # when we're debundled but setuptools isn't, Python will see + # packaging.version.Version and + # pkg_resources._vendor.packaging.version.Version as different + # types. This way we'll use a str as a common data interchange + # format. If we stop using the pkg_resources provided specifier + # and start using our own, we can drop the cast to str(). + (str(c.version) for c in candidates), + prereleases=allow_prereleases, + ) + } + + # Again, converting version to str to deal with debundling. + applicable_candidates = [ + c for c in candidates if str(c.version) in versions + ] + + filtered_applicable_candidates = filter_unallowed_hashes( + candidates=applicable_candidates, + hashes=self._hashes, + project_name=self._project_name, + ) + + return sorted(filtered_applicable_candidates, key=self._sort_key) + + def _sort_key(self, candidate): + # type: (InstallationCandidate) -> CandidateSortingKey + """ + Function to pass as the `key` argument to a call to sorted() to sort + InstallationCandidates by preference. + + Returns a tuple such that tuples sorting as greater using Python's + default comparison operator are more preferred. + + The preference is as follows: + + First and foremost, candidates with allowed (matching) hashes are + always preferred over candidates without matching hashes. This is + because e.g. if the only candidate with an allowed hash is yanked, + we still want to use that candidate. + + Second, excepting hash considerations, candidates that have been + yanked (in the sense of PEP 592) are always less preferred than + candidates that haven't been yanked. Then: + + If not finding wheels, they are sorted by version only. + If finding wheels, then the sort order is by version, then: + 1. existing installs + 2. wheels ordered via Wheel.support_index_min(self._supported_tags) + 3. source archives + If prefer_binary was set, then all wheels are sorted above sources. + + Note: it was considered to embed this logic into the Link + comparison operators, but then different sdist links + with the same version, would have to be considered equal + """ + valid_tags = self._supported_tags + support_num = len(valid_tags) + build_tag = () # type: BuildTag + binary_preference = 0 + link = candidate.link + if link.is_wheel: + # can raise InvalidWheelFilename + wheel = Wheel(link.filename) + if not wheel.supported(valid_tags): + raise UnsupportedWheel( + "{} is not a supported wheel for this platform. It " + "can't be sorted.".format(wheel.filename) + ) + if self._prefer_binary: + binary_preference = 1 + pri = -(wheel.support_index_min(valid_tags)) + if wheel.build_tag is not None: + match = re.match(r'^(\d+)(.*)$', wheel.build_tag) + build_tag_groups = match.groups() + build_tag = (int(build_tag_groups[0]), build_tag_groups[1]) + else: # sdist + pri = -(support_num) + has_allowed_hash = int(link.is_hash_allowed(self._hashes)) + yank_value = -1 * int(link.is_yanked) # -1 for yanked. + return ( + has_allowed_hash, yank_value, binary_preference, candidate.version, + build_tag, pri, + ) + + def sort_best_candidate( + self, + candidates, # type: List[InstallationCandidate] + ): + # type: (...) -> Optional[InstallationCandidate] + """ + Return the best candidate per the instance's sort order, or None if + no candidate is acceptable. + """ + if not candidates: + return None + best_candidate = max(candidates, key=self._sort_key) + return best_candidate + + def compute_best_candidate( + self, + candidates, # type: List[InstallationCandidate] + ): + # type: (...) -> BestCandidateResult + """ + Compute and return a `BestCandidateResult` instance. + """ + applicable_candidates = self.get_applicable_candidates(candidates) + + best_candidate = self.sort_best_candidate(applicable_candidates) + + return BestCandidateResult( + candidates, + applicable_candidates=applicable_candidates, + best_candidate=best_candidate, + ) + + +class PackageFinder(object): + """This finds packages. + + This is meant to match easy_install's technique for looking for + packages, by reading pages and looking for appropriate links. + """ + + def __init__( + self, + link_collector, # type: LinkCollector + target_python, # type: TargetPython + allow_yanked, # type: bool + format_control=None, # type: Optional[FormatControl] + candidate_prefs=None, # type: CandidatePreferences + ignore_requires_python=None, # type: Optional[bool] + ): + # type: (...) -> None + """ + This constructor is primarily meant to be used by the create() class + method and from tests. + + :param format_control: A FormatControl object, used to control + the selection of source packages / binary packages when consulting + the index and links. + :param candidate_prefs: Options to use when creating a + CandidateEvaluator object. + """ + if candidate_prefs is None: + candidate_prefs = CandidatePreferences() + + format_control = format_control or FormatControl(set(), set()) + + self._allow_yanked = allow_yanked + self._candidate_prefs = candidate_prefs + self._ignore_requires_python = ignore_requires_python + self._link_collector = link_collector + self._target_python = target_python + + self.format_control = format_control + + # These are boring links that have already been logged somehow. + self._logged_links = set() # type: Set[Link] + + # Don't include an allow_yanked default value to make sure each call + # site considers whether yanked releases are allowed. This also causes + # that decision to be made explicit in the calling code, which helps + # people when reading the code. + @classmethod + def create( + cls, + link_collector, # type: LinkCollector + selection_prefs, # type: SelectionPreferences + target_python=None, # type: Optional[TargetPython] + ): + # type: (...) -> PackageFinder + """Create a PackageFinder. + + :param selection_prefs: The candidate selection preferences, as a + SelectionPreferences object. + :param target_python: The target Python interpreter to use when + checking compatibility. If None (the default), a TargetPython + object will be constructed from the running Python. + """ + if target_python is None: + target_python = TargetPython() + + candidate_prefs = CandidatePreferences( + prefer_binary=selection_prefs.prefer_binary, + allow_all_prereleases=selection_prefs.allow_all_prereleases, + ) + + return cls( + candidate_prefs=candidate_prefs, + link_collector=link_collector, + target_python=target_python, + allow_yanked=selection_prefs.allow_yanked, + format_control=selection_prefs.format_control, + ignore_requires_python=selection_prefs.ignore_requires_python, + ) + + @property + def target_python(self): + # type: () -> TargetPython + return self._target_python + + @property + def search_scope(self): + # type: () -> SearchScope + return self._link_collector.search_scope + + @search_scope.setter + def search_scope(self, search_scope): + # type: (SearchScope) -> None + self._link_collector.search_scope = search_scope + + @property + def find_links(self): + # type: () -> List[str] + return self._link_collector.find_links + + @property + def index_urls(self): + # type: () -> List[str] + return self.search_scope.index_urls + + @property + def trusted_hosts(self): + # type: () -> Iterable[str] + for host_port in self._link_collector.session.pip_trusted_origins: + yield build_netloc(*host_port) + + @property + def allow_all_prereleases(self): + # type: () -> bool + return self._candidate_prefs.allow_all_prereleases + + def set_allow_all_prereleases(self): + # type: () -> None + self._candidate_prefs.allow_all_prereleases = True + + @property + def prefer_binary(self): + # type: () -> bool + return self._candidate_prefs.prefer_binary + + def set_prefer_binary(self): + # type: () -> None + self._candidate_prefs.prefer_binary = True + + def make_link_evaluator(self, project_name): + # type: (str) -> LinkEvaluator + canonical_name = canonicalize_name(project_name) + formats = self.format_control.get_allowed_formats(canonical_name) + + return LinkEvaluator( + project_name=project_name, + canonical_name=canonical_name, + formats=formats, + target_python=self._target_python, + allow_yanked=self._allow_yanked, + ignore_requires_python=self._ignore_requires_python, + ) + + def _sort_links(self, links): + # type: (Iterable[Link]) -> List[Link] + """ + Returns elements of links in order, non-egg links first, egg links + second, while eliminating duplicates + """ + eggs, no_eggs = [], [] + seen = set() # type: Set[Link] + for link in links: + if link not in seen: + seen.add(link) + if link.egg_fragment: + eggs.append(link) + else: + no_eggs.append(link) + return no_eggs + eggs + + def _log_skipped_link(self, link, reason): + # type: (Link, Text) -> None + if link not in self._logged_links: + # Mark this as a unicode string to prevent "UnicodeEncodeError: + # 'ascii' codec can't encode character" in Python 2 when + # the reason contains non-ascii characters. + # Also, put the link at the end so the reason is more visible + # and because the link string is usually very long. + logger.debug(u'Skipping link: %s: %s', reason, link) + self._logged_links.add(link) + + def get_install_candidate(self, link_evaluator, link): + # type: (LinkEvaluator, Link) -> Optional[InstallationCandidate] + """ + If the link is a candidate for install, convert it to an + InstallationCandidate and return it. Otherwise, return None. + """ + is_candidate, result = link_evaluator.evaluate_link(link) + if not is_candidate: + if result: + self._log_skipped_link(link, reason=result) + return None + + return InstallationCandidate( + name=link_evaluator.project_name, + link=link, + # Convert the Text result to str since InstallationCandidate + # accepts str. + version=str(result), + ) + + def evaluate_links(self, link_evaluator, links): + # type: (LinkEvaluator, Iterable[Link]) -> List[InstallationCandidate] + """ + Convert links that are candidates to InstallationCandidate objects. + """ + candidates = [] + for link in self._sort_links(links): + candidate = self.get_install_candidate(link_evaluator, link) + if candidate is not None: + candidates.append(candidate) + + return candidates + + def process_project_url(self, project_url, link_evaluator): + # type: (Link, LinkEvaluator) -> List[InstallationCandidate] + logger.debug( + 'Fetching project page and analyzing links: %s', project_url, + ) + html_page = self._link_collector.fetch_page(project_url) + if html_page is None: + return [] + + page_links = list(parse_links(html_page)) + + with indent_log(): + package_links = self.evaluate_links( + link_evaluator, + links=page_links, + ) + + return package_links + + @lru_cache(maxsize=None) + def find_all_candidates(self, project_name): + # type: (str) -> List[InstallationCandidate] + """Find all available InstallationCandidate for project_name + + This checks index_urls and find_links. + All versions found are returned as an InstallationCandidate list. + + See LinkEvaluator.evaluate_link() for details on which files + are accepted. + """ + collected_links = self._link_collector.collect_links(project_name) + + link_evaluator = self.make_link_evaluator(project_name) + + find_links_versions = self.evaluate_links( + link_evaluator, + links=collected_links.find_links, + ) + + page_versions = [] + for project_url in collected_links.project_urls: + package_links = self.process_project_url( + project_url, link_evaluator=link_evaluator, + ) + page_versions.extend(package_links) + + file_versions = self.evaluate_links( + link_evaluator, + links=collected_links.files, + ) + if file_versions: + file_versions.sort(reverse=True) + logger.debug( + 'Local files found: %s', + ', '.join([ + url_to_path(candidate.link.url) + for candidate in file_versions + ]) + ) + + # This is an intentional priority ordering + return file_versions + find_links_versions + page_versions + + def make_candidate_evaluator( + self, + project_name, # type: str + specifier=None, # type: Optional[specifiers.BaseSpecifier] + hashes=None, # type: Optional[Hashes] + ): + # type: (...) -> CandidateEvaluator + """Create a CandidateEvaluator object to use. + """ + candidate_prefs = self._candidate_prefs + return CandidateEvaluator.create( + project_name=project_name, + target_python=self._target_python, + prefer_binary=candidate_prefs.prefer_binary, + allow_all_prereleases=candidate_prefs.allow_all_prereleases, + specifier=specifier, + hashes=hashes, + ) + + @lru_cache(maxsize=None) + def find_best_candidate( + self, + project_name, # type: str + specifier=None, # type: Optional[specifiers.BaseSpecifier] + hashes=None, # type: Optional[Hashes] + ): + # type: (...) -> BestCandidateResult + """Find matches for the given project and specifier. + + :param specifier: An optional object implementing `filter` + (e.g. `packaging.specifiers.SpecifierSet`) to filter applicable + versions. + + :return: A `BestCandidateResult` instance. + """ + candidates = self.find_all_candidates(project_name) + candidate_evaluator = self.make_candidate_evaluator( + project_name=project_name, + specifier=specifier, + hashes=hashes, + ) + return candidate_evaluator.compute_best_candidate(candidates) + + def find_requirement(self, req, upgrade): + # type: (InstallRequirement, bool) -> Optional[InstallationCandidate] + """Try to find a Link matching req + + Expects req, an InstallRequirement and upgrade, a boolean + Returns a InstallationCandidate if found, + Raises DistributionNotFound or BestVersionAlreadyInstalled otherwise + """ + hashes = req.hashes(trust_internet=False) + best_candidate_result = self.find_best_candidate( + req.name, specifier=req.specifier, hashes=hashes, + ) + best_candidate = best_candidate_result.best_candidate + + installed_version = None # type: Optional[_BaseVersion] + if req.satisfied_by is not None: + installed_version = parse_version(req.satisfied_by.version) + + def _format_versions(cand_iter): + # type: (Iterable[InstallationCandidate]) -> str + # This repeated parse_version and str() conversion is needed to + # handle different vendoring sources from pip and pkg_resources. + # If we stop using the pkg_resources provided specifier and start + # using our own, we can drop the cast to str(). + return ", ".join(sorted( + {str(c.version) for c in cand_iter}, + key=parse_version, + )) or "none" + + if installed_version is None and best_candidate is None: + logger.critical( + 'Could not find a version that satisfies the requirement %s ' + '(from versions: %s)', + req, + _format_versions(best_candidate_result.iter_all()), + ) + + raise DistributionNotFound( + 'No matching distribution found for {}'.format( + req) + ) + + best_installed = False + if installed_version and ( + best_candidate is None or + best_candidate.version <= installed_version): + best_installed = True + + if not upgrade and installed_version is not None: + if best_installed: + logger.debug( + 'Existing installed version (%s) is most up-to-date and ' + 'satisfies requirement', + installed_version, + ) + else: + logger.debug( + 'Existing installed version (%s) satisfies requirement ' + '(most up-to-date version is %s)', + installed_version, + best_candidate.version, + ) + return None + + if best_installed: + # We have an existing version, and its the best version + logger.debug( + 'Installed version (%s) is most up-to-date (past versions: ' + '%s)', + installed_version, + _format_versions(best_candidate_result.iter_applicable()), + ) + raise BestVersionAlreadyInstalled + + logger.debug( + 'Using version %s (newest of versions: %s)', + best_candidate.version, + _format_versions(best_candidate_result.iter_applicable()), + ) + return best_candidate + + +def _find_name_version_sep(fragment, canonical_name): + # type: (str, str) -> int + """Find the separator's index based on the package's canonical name. + + :param fragment: A + filename "fragment" (stem) or + egg fragment. + :param canonical_name: The package's canonical name. + + This function is needed since the canonicalized name does not necessarily + have the same length as the egg info's name part. An example:: + + >>> fragment = 'foo__bar-1.0' + >>> canonical_name = 'foo-bar' + >>> _find_name_version_sep(fragment, canonical_name) + 8 + """ + # Project name and version must be separated by one single dash. Find all + # occurrences of dashes; if the string in front of it matches the canonical + # name, this is the one separating the name and version parts. + for i, c in enumerate(fragment): + if c != "-": + continue + if canonicalize_name(fragment[:i]) == canonical_name: + return i + raise ValueError("{} does not match {}".format(fragment, canonical_name)) + + +def _extract_version_from_fragment(fragment, canonical_name): + # type: (str, str) -> Optional[str] + """Parse the version string from a + filename + "fragment" (stem) or egg fragment. + + :param fragment: The string to parse. E.g. foo-2.1 + :param canonical_name: The canonicalized name of the package this + belongs to. + """ + try: + version_start = _find_name_version_sep(fragment, canonical_name) + 1 + except ValueError: + return None + version = fragment[version_start:] + if not version: + return None + return version diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/locations.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/locations.py new file mode 100644 index 0000000000..f521844525 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/locations.py @@ -0,0 +1,199 @@ +"""Locations where we look for configs, install stuff, etc""" + +# The following comment should be removed at some point in the future. +# mypy: strict-optional=False + +from __future__ import absolute_import + +import os +import os.path +import platform +import site +import sys +import sysconfig +from distutils import sysconfig as distutils_sysconfig +from distutils.command.install import SCHEME_KEYS # type: ignore +from distutils.command.install import install as distutils_install_command + +from pip._internal.models.scheme import Scheme +from pip._internal.utils import appdirs +from pip._internal.utils.compat import WINDOWS +from pip._internal.utils.typing import MYPY_CHECK_RUNNING, cast +from pip._internal.utils.virtualenv import running_under_virtualenv + +if MYPY_CHECK_RUNNING: + from distutils.cmd import Command as DistutilsCommand + from typing import Dict, List, Optional, Union + + +# Application Directories +USER_CACHE_DIR = appdirs.user_cache_dir("pip") + + +def get_major_minor_version(): + # type: () -> str + """ + Return the major-minor version of the current Python as a string, e.g. + "3.7" or "3.10". + """ + return '{}.{}'.format(*sys.version_info) + + +def get_src_prefix(): + # type: () -> str + if running_under_virtualenv(): + src_prefix = os.path.join(sys.prefix, 'src') + else: + # FIXME: keep src in cwd for now (it is not a temporary folder) + try: + src_prefix = os.path.join(os.getcwd(), 'src') + except OSError: + # In case the current working directory has been renamed or deleted + sys.exit( + "The folder you are executing pip from can no longer be found." + ) + + # under macOS + virtualenv sys.prefix is not properly resolved + # it is something like /path/to/python/bin/.. + return os.path.abspath(src_prefix) + + +# FIXME doesn't account for venv linked to global site-packages + +# The python2.7 part of this is Debian specific: +# https://github.com/pypa/pip/issues/5193 +# https://bitbucket.org/pypy/pypy/issues/2506/sysconfig-returns-incorrect-paths +can_not_depend_on_purelib = ( + sys.version_info[:2] == (2, 7) or + platform.python_implementation().lower() == "pypy" +) +site_packages = None # type: Optional[str] +if can_not_depend_on_purelib: + site_packages = distutils_sysconfig.get_python_lib() +else: + site_packages = sysconfig.get_path("purelib") + +try: + # Use getusersitepackages if this is present, as it ensures that the + # value is initialised properly. + user_site = site.getusersitepackages() +except AttributeError: + user_site = site.USER_SITE + +if WINDOWS: + bin_py = os.path.join(sys.prefix, 'Scripts') + bin_user = os.path.join(user_site, 'Scripts') + # buildout uses 'bin' on Windows too? + if not os.path.exists(bin_py): + bin_py = os.path.join(sys.prefix, 'bin') + bin_user = os.path.join(user_site, 'bin') +else: + bin_py = os.path.join(sys.prefix, 'bin') + bin_user = os.path.join(user_site, 'bin') + + # Forcing to use /usr/local/bin for standard macOS framework installs + # Also log to ~/Library/Logs/ for use with the Console.app log viewer + if sys.platform[:6] == 'darwin' and sys.prefix[:16] == '/System/Library/': + bin_py = '/usr/local/bin' + + +def distutils_scheme( + dist_name, user=False, home=None, root=None, isolated=False, prefix=None +): + # type:(str, bool, str, str, bool, str) -> Dict[str, str] + """ + Return a distutils install scheme + """ + from distutils.dist import Distribution + + dist_args = {'name': dist_name} # type: Dict[str, Union[str, List[str]]] + if isolated: + dist_args["script_args"] = ["--no-user-cfg"] + + d = Distribution(dist_args) + d.parse_config_files() + obj = None # type: Optional[DistutilsCommand] + obj = d.get_command_obj('install', create=True) + assert obj is not None + i = cast(distutils_install_command, obj) + # NOTE: setting user or home has the side-effect of creating the home dir + # or user base for installations during finalize_options() + # ideally, we'd prefer a scheme class that has no side-effects. + assert not (user and prefix), "user={} prefix={}".format(user, prefix) + assert not (home and prefix), "home={} prefix={}".format(home, prefix) + i.user = user or i.user + if user or home: + i.prefix = "" + i.prefix = prefix or i.prefix + i.home = home or i.home + i.root = root or i.root + i.finalize_options() + + scheme = {} + for key in SCHEME_KEYS: + scheme[key] = getattr(i, 'install_' + key) + + # install_lib specified in setup.cfg should install *everything* + # into there (i.e. it takes precedence over both purelib and + # platlib). Note, i.install_lib is *always* set after + # finalize_options(); we only want to override here if the user + # has explicitly requested it hence going back to the config + if 'install_lib' in d.get_option_dict('install'): + scheme.update(dict(purelib=i.install_lib, platlib=i.install_lib)) + + if running_under_virtualenv(): + scheme['headers'] = os.path.join( + i.prefix, + 'include', + 'site', + 'python{}'.format(get_major_minor_version()), + dist_name, + ) + + if root is not None: + path_no_drive = os.path.splitdrive( + os.path.abspath(scheme["headers"]))[1] + scheme["headers"] = os.path.join( + root, + path_no_drive[1:], + ) + + return scheme + + +def get_scheme( + dist_name, # type: str + user=False, # type: bool + home=None, # type: Optional[str] + root=None, # type: Optional[str] + isolated=False, # type: bool + prefix=None, # type: Optional[str] +): + # type: (...) -> Scheme + """ + Get the "scheme" corresponding to the input parameters. The distutils + documentation provides the context for the available schemes: + https://docs.python.org/3/install/index.html#alternate-installation + + :param dist_name: the name of the package to retrieve the scheme for, used + in the headers scheme path + :param user: indicates to use the "user" scheme + :param home: indicates to use the "home" scheme and provides the base + directory for the same + :param root: root under which other directories are re-based + :param isolated: equivalent to --no-user-cfg, i.e. do not consider + ~/.pydistutils.cfg (posix) or ~/pydistutils.cfg (non-posix) for + scheme paths + :param prefix: indicates to use the "prefix" scheme and provides the + base directory for the same + """ + scheme = distutils_scheme( + dist_name, user, home, root, isolated, prefix + ) + return Scheme( + platlib=scheme["platlib"], + purelib=scheme["purelib"], + headers=scheme["headers"], + scripts=scheme["scripts"], + data=scheme["data"], + ) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/main.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/main.py new file mode 100644 index 0000000000..1c99c49a1f --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/main.py @@ -0,0 +1,16 @@ +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List, Optional + + +def main(args=None): + # type: (Optional[List[str]]) -> int + """This is preserved for old console scripts that may still be referencing + it. + + For additional details, see https://github.com/pypa/pip/issues/7498. + """ + from pip._internal.utils.entrypoints import _wrapper + + return _wrapper(args) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/models/__init__.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/models/__init__.py new file mode 100644 index 0000000000..7855226e4b --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/models/__init__.py @@ -0,0 +1,2 @@ +"""A package that contains models that represent entities. +""" diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/models/candidate.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/models/candidate.py new file mode 100644 index 0000000000..0d89a8c07d --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/models/candidate.py @@ -0,0 +1,39 @@ +from pip._vendor.packaging.version import parse as parse_version + +from pip._internal.utils.models import KeyBasedCompareMixin +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from pip._vendor.packaging.version import _BaseVersion + + from pip._internal.models.link import Link + + +class InstallationCandidate(KeyBasedCompareMixin): + """Represents a potential "candidate" for installation. + """ + + __slots__ = ["name", "version", "link"] + + def __init__(self, name, version, link): + # type: (str, str, Link) -> None + self.name = name + self.version = parse_version(version) # type: _BaseVersion + self.link = link + + super(InstallationCandidate, self).__init__( + key=(self.name, self.version, self.link), + defining_class=InstallationCandidate + ) + + def __repr__(self): + # type: () -> str + return "".format( + self.name, self.version, self.link, + ) + + def __str__(self): + # type: () -> str + return '{!r} candidate (version {} at {})'.format( + self.name, self.version, self.link, + ) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/models/direct_url.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/models/direct_url.py new file mode 100644 index 0000000000..99aa68d121 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/models/direct_url.py @@ -0,0 +1,243 @@ +""" PEP 610 """ +import json +import re + +from pip._vendor import six +from pip._vendor.six.moves.urllib import parse as urllib_parse + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Any, Dict, Iterable, Optional, Type, TypeVar, Union + + T = TypeVar("T") + + +DIRECT_URL_METADATA_NAME = "direct_url.json" +ENV_VAR_RE = re.compile(r"^\$\{[A-Za-z0-9-_]+\}(:\$\{[A-Za-z0-9-_]+\})?$") + +__all__ = [ + "DirectUrl", + "DirectUrlValidationError", + "DirInfo", + "ArchiveInfo", + "VcsInfo", +] + + +class DirectUrlValidationError(Exception): + pass + + +def _get(d, expected_type, key, default=None): + # type: (Dict[str, Any], Type[T], str, Optional[T]) -> Optional[T] + """Get value from dictionary and verify expected type.""" + if key not in d: + return default + value = d[key] + if six.PY2 and expected_type is str: + expected_type = six.string_types # type: ignore + if not isinstance(value, expected_type): + raise DirectUrlValidationError( + "{!r} has unexpected type for {} (expected {})".format( + value, key, expected_type + ) + ) + return value + + +def _get_required(d, expected_type, key, default=None): + # type: (Dict[str, Any], Type[T], str, Optional[T]) -> T + value = _get(d, expected_type, key, default) + if value is None: + raise DirectUrlValidationError("{} must have a value".format(key)) + return value + + +def _exactly_one_of(infos): + # type: (Iterable[Optional[InfoType]]) -> InfoType + infos = [info for info in infos if info is not None] + if not infos: + raise DirectUrlValidationError( + "missing one of archive_info, dir_info, vcs_info" + ) + if len(infos) > 1: + raise DirectUrlValidationError( + "more than one of archive_info, dir_info, vcs_info" + ) + assert infos[0] is not None + return infos[0] + + +def _filter_none(**kwargs): + # type: (Any) -> Dict[str, Any] + """Make dict excluding None values.""" + return {k: v for k, v in kwargs.items() if v is not None} + + +class VcsInfo(object): + name = "vcs_info" + + def __init__( + self, + vcs, # type: str + commit_id, # type: str + requested_revision=None, # type: Optional[str] + resolved_revision=None, # type: Optional[str] + resolved_revision_type=None, # type: Optional[str] + ): + self.vcs = vcs + self.requested_revision = requested_revision + self.commit_id = commit_id + self.resolved_revision = resolved_revision + self.resolved_revision_type = resolved_revision_type + + @classmethod + def _from_dict(cls, d): + # type: (Optional[Dict[str, Any]]) -> Optional[VcsInfo] + if d is None: + return None + return cls( + vcs=_get_required(d, str, "vcs"), + commit_id=_get_required(d, str, "commit_id"), + requested_revision=_get(d, str, "requested_revision"), + resolved_revision=_get(d, str, "resolved_revision"), + resolved_revision_type=_get(d, str, "resolved_revision_type"), + ) + + def _to_dict(self): + # type: () -> Dict[str, Any] + return _filter_none( + vcs=self.vcs, + requested_revision=self.requested_revision, + commit_id=self.commit_id, + resolved_revision=self.resolved_revision, + resolved_revision_type=self.resolved_revision_type, + ) + + +class ArchiveInfo(object): + name = "archive_info" + + def __init__( + self, + hash=None, # type: Optional[str] + ): + self.hash = hash + + @classmethod + def _from_dict(cls, d): + # type: (Optional[Dict[str, Any]]) -> Optional[ArchiveInfo] + if d is None: + return None + return cls(hash=_get(d, str, "hash")) + + def _to_dict(self): + # type: () -> Dict[str, Any] + return _filter_none(hash=self.hash) + + +class DirInfo(object): + name = "dir_info" + + def __init__( + self, + editable=False, # type: bool + ): + self.editable = editable + + @classmethod + def _from_dict(cls, d): + # type: (Optional[Dict[str, Any]]) -> Optional[DirInfo] + if d is None: + return None + return cls( + editable=_get_required(d, bool, "editable", default=False) + ) + + def _to_dict(self): + # type: () -> Dict[str, Any] + return _filter_none(editable=self.editable or None) + + +if MYPY_CHECK_RUNNING: + InfoType = Union[ArchiveInfo, DirInfo, VcsInfo] + + +class DirectUrl(object): + + def __init__( + self, + url, # type: str + info, # type: InfoType + subdirectory=None, # type: Optional[str] + ): + self.url = url + self.info = info + self.subdirectory = subdirectory + + def _remove_auth_from_netloc(self, netloc): + # type: (str) -> str + if "@" not in netloc: + return netloc + user_pass, netloc_no_user_pass = netloc.split("@", 1) + if ( + isinstance(self.info, VcsInfo) and + self.info.vcs == "git" and + user_pass == "git" + ): + return netloc + if ENV_VAR_RE.match(user_pass): + return netloc + return netloc_no_user_pass + + @property + def redacted_url(self): + # type: () -> str + """url with user:password part removed unless it is formed with + environment variables as specified in PEP 610, or it is ``git`` + in the case of a git URL. + """ + purl = urllib_parse.urlsplit(self.url) + netloc = self._remove_auth_from_netloc(purl.netloc) + surl = urllib_parse.urlunsplit( + (purl.scheme, netloc, purl.path, purl.query, purl.fragment) + ) + return surl + + def validate(self): + # type: () -> None + self.from_dict(self.to_dict()) + + @classmethod + def from_dict(cls, d): + # type: (Dict[str, Any]) -> DirectUrl + return DirectUrl( + url=_get_required(d, str, "url"), + subdirectory=_get(d, str, "subdirectory"), + info=_exactly_one_of( + [ + ArchiveInfo._from_dict(_get(d, dict, "archive_info")), + DirInfo._from_dict(_get(d, dict, "dir_info")), + VcsInfo._from_dict(_get(d, dict, "vcs_info")), + ] + ), + ) + + def to_dict(self): + # type: () -> Dict[str, Any] + res = _filter_none( + url=self.redacted_url, + subdirectory=self.subdirectory, + ) + res[self.info.name] = self.info._to_dict() + return res + + @classmethod + def from_json(cls, s): + # type: (str) -> DirectUrl + return cls.from_dict(json.loads(s)) + + def to_json(self): + # type: () -> str + return json.dumps(self.to_dict(), sort_keys=True) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/models/format_control.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/models/format_control.py new file mode 100644 index 0000000000..adcf61e285 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/models/format_control.py @@ -0,0 +1,92 @@ +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.exceptions import CommandError +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import FrozenSet, Optional, Set + + +class FormatControl(object): + """Helper for managing formats from which a package can be installed. + """ + + __slots__ = ["no_binary", "only_binary"] + + def __init__(self, no_binary=None, only_binary=None): + # type: (Optional[Set[str]], Optional[Set[str]]) -> None + if no_binary is None: + no_binary = set() + if only_binary is None: + only_binary = set() + + self.no_binary = no_binary + self.only_binary = only_binary + + def __eq__(self, other): + # type: (object) -> bool + if not isinstance(other, self.__class__): + return NotImplemented + + if self.__slots__ != other.__slots__: + return False + + return all( + getattr(self, k) == getattr(other, k) + for k in self.__slots__ + ) + + def __ne__(self, other): + # type: (object) -> bool + return not self.__eq__(other) + + def __repr__(self): + # type: () -> str + return "{}({}, {})".format( + self.__class__.__name__, + self.no_binary, + self.only_binary + ) + + @staticmethod + def handle_mutual_excludes(value, target, other): + # type: (str, Set[str], Set[str]) -> None + if value.startswith('-'): + raise CommandError( + "--no-binary / --only-binary option requires 1 argument." + ) + new = value.split(',') + while ':all:' in new: + other.clear() + target.clear() + target.add(':all:') + del new[:new.index(':all:') + 1] + # Without a none, we want to discard everything as :all: covers it + if ':none:' not in new: + return + for name in new: + if name == ':none:': + target.clear() + continue + name = canonicalize_name(name) + other.discard(name) + target.add(name) + + def get_allowed_formats(self, canonical_name): + # type: (str) -> FrozenSet[str] + result = {"binary", "source"} + if canonical_name in self.only_binary: + result.discard('source') + elif canonical_name in self.no_binary: + result.discard('binary') + elif ':all:' in self.only_binary: + result.discard('source') + elif ':all:' in self.no_binary: + result.discard('binary') + return frozenset(result) + + def disallow_binaries(self): + # type: () -> None + self.handle_mutual_excludes( + ':all:', self.no_binary, self.only_binary, + ) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/models/index.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/models/index.py new file mode 100644 index 0000000000..5b4a1fe227 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/models/index.py @@ -0,0 +1,34 @@ +from pip._vendor.six.moves.urllib import parse as urllib_parse + + +class PackageIndex(object): + """Represents a Package Index and provides easier access to endpoints + """ + + __slots__ = ['url', 'netloc', 'simple_url', 'pypi_url', + 'file_storage_domain'] + + def __init__(self, url, file_storage_domain): + # type: (str, str) -> None + super(PackageIndex, self).__init__() + self.url = url + self.netloc = urllib_parse.urlsplit(url).netloc + self.simple_url = self._url_for_path('simple') + self.pypi_url = self._url_for_path('pypi') + + # This is part of a temporary hack used to block installs of PyPI + # packages which depend on external urls only necessary until PyPI can + # block such packages themselves + self.file_storage_domain = file_storage_domain + + def _url_for_path(self, path): + # type: (str) -> str + return urllib_parse.urljoin(self.url, path) + + +PyPI = PackageIndex( + 'https://pypi.org/', file_storage_domain='files.pythonhosted.org' +) +TestPyPI = PackageIndex( + 'https://test.pypi.org/', file_storage_domain='test-files.pythonhosted.org' +) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/models/link.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/models/link.py new file mode 100644 index 0000000000..29ef402bee --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/models/link.py @@ -0,0 +1,246 @@ +import os +import posixpath +import re + +from pip._vendor.six.moves.urllib import parse as urllib_parse + +from pip._internal.utils.filetypes import WHEEL_EXTENSION +from pip._internal.utils.misc import ( + redact_auth_from_url, + split_auth_from_netloc, + splitext, +) +from pip._internal.utils.models import KeyBasedCompareMixin +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.urls import path_to_url, url_to_path + +if MYPY_CHECK_RUNNING: + from typing import Optional, Text, Tuple, Union + + from pip._internal.index.collector import HTMLPage + from pip._internal.utils.hashes import Hashes + + +class Link(KeyBasedCompareMixin): + """Represents a parsed link from a Package Index's simple URL + """ + + __slots__ = [ + "_parsed_url", + "_url", + "comes_from", + "requires_python", + "yanked_reason", + "cache_link_parsing", + ] + + def __init__( + self, + url, # type: str + comes_from=None, # type: Optional[Union[str, HTMLPage]] + requires_python=None, # type: Optional[str] + yanked_reason=None, # type: Optional[Text] + cache_link_parsing=True, # type: bool + ): + # type: (...) -> None + """ + :param url: url of the resource pointed to (href of the link) + :param comes_from: instance of HTMLPage where the link was found, + or string. + :param requires_python: String containing the `Requires-Python` + metadata field, specified in PEP 345. This may be specified by + a data-requires-python attribute in the HTML link tag, as + described in PEP 503. + :param yanked_reason: the reason the file has been yanked, if the + file has been yanked, or None if the file hasn't been yanked. + This is the value of the "data-yanked" attribute, if present, in + a simple repository HTML link. If the file has been yanked but + no reason was provided, this should be the empty string. See + PEP 592 for more information and the specification. + :param cache_link_parsing: A flag that is used elsewhere to determine + whether resources retrieved from this link + should be cached. PyPI index urls should + generally have this set to False, for + example. + """ + + # url can be a UNC windows share + if url.startswith('\\\\'): + url = path_to_url(url) + + self._parsed_url = urllib_parse.urlsplit(url) + # Store the url as a private attribute to prevent accidentally + # trying to set a new value. + self._url = url + + self.comes_from = comes_from + self.requires_python = requires_python if requires_python else None + self.yanked_reason = yanked_reason + + super(Link, self).__init__(key=url, defining_class=Link) + + self.cache_link_parsing = cache_link_parsing + + def __str__(self): + # type: () -> str + if self.requires_python: + rp = ' (requires-python:{})'.format(self.requires_python) + else: + rp = '' + if self.comes_from: + return '{} (from {}){}'.format( + redact_auth_from_url(self._url), self.comes_from, rp) + else: + return redact_auth_from_url(str(self._url)) + + def __repr__(self): + # type: () -> str + return ''.format(self) + + @property + def url(self): + # type: () -> str + return self._url + + @property + def filename(self): + # type: () -> str + path = self.path.rstrip('/') + name = posixpath.basename(path) + if not name: + # Make sure we don't leak auth information if the netloc + # includes a username and password. + netloc, user_pass = split_auth_from_netloc(self.netloc) + return netloc + + name = urllib_parse.unquote(name) + assert name, ( + 'URL {self._url!r} produced no filename'.format(**locals())) + return name + + @property + def file_path(self): + # type: () -> str + return url_to_path(self.url) + + @property + def scheme(self): + # type: () -> str + return self._parsed_url.scheme + + @property + def netloc(self): + # type: () -> str + """ + This can contain auth information. + """ + return self._parsed_url.netloc + + @property + def path(self): + # type: () -> str + return urllib_parse.unquote(self._parsed_url.path) + + def splitext(self): + # type: () -> Tuple[str, str] + return splitext(posixpath.basename(self.path.rstrip('/'))) + + @property + def ext(self): + # type: () -> str + return self.splitext()[1] + + @property + def url_without_fragment(self): + # type: () -> str + scheme, netloc, path, query, fragment = self._parsed_url + return urllib_parse.urlunsplit((scheme, netloc, path, query, None)) + + _egg_fragment_re = re.compile(r'[#&]egg=([^&]*)') + + @property + def egg_fragment(self): + # type: () -> Optional[str] + match = self._egg_fragment_re.search(self._url) + if not match: + return None + return match.group(1) + + _subdirectory_fragment_re = re.compile(r'[#&]subdirectory=([^&]*)') + + @property + def subdirectory_fragment(self): + # type: () -> Optional[str] + match = self._subdirectory_fragment_re.search(self._url) + if not match: + return None + return match.group(1) + + _hash_re = re.compile( + r'(sha1|sha224|sha384|sha256|sha512|md5)=([a-f0-9]+)' + ) + + @property + def hash(self): + # type: () -> Optional[str] + match = self._hash_re.search(self._url) + if match: + return match.group(2) + return None + + @property + def hash_name(self): + # type: () -> Optional[str] + match = self._hash_re.search(self._url) + if match: + return match.group(1) + return None + + @property + def show_url(self): + # type: () -> str + return posixpath.basename(self._url.split('#', 1)[0].split('?', 1)[0]) + + @property + def is_file(self): + # type: () -> bool + return self.scheme == 'file' + + def is_existing_dir(self): + # type: () -> bool + return self.is_file and os.path.isdir(self.file_path) + + @property + def is_wheel(self): + # type: () -> bool + return self.ext == WHEEL_EXTENSION + + @property + def is_vcs(self): + # type: () -> bool + from pip._internal.vcs import vcs + + return self.scheme in vcs.all_schemes + + @property + def is_yanked(self): + # type: () -> bool + return self.yanked_reason is not None + + @property + def has_hash(self): + # type: () -> bool + return self.hash_name is not None + + def is_hash_allowed(self, hashes): + # type: (Optional[Hashes]) -> bool + """ + Return True if the link has a hash and it is allowed. + """ + if hashes is None or not self.has_hash: + return False + # Assert non-None so mypy knows self.hash_name and self.hash are str. + assert self.hash_name is not None + assert self.hash is not None + + return hashes.is_hash_allowed(self.hash_name, hex_digest=self.hash) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/models/scheme.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/models/scheme.py new file mode 100644 index 0000000000..5040551eb0 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/models/scheme.py @@ -0,0 +1,31 @@ +""" +For types associated with installation schemes. + +For a general overview of available schemes and their context, see +https://docs.python.org/3/install/index.html#alternate-installation. +""" + + +SCHEME_KEYS = ['platlib', 'purelib', 'headers', 'scripts', 'data'] + + +class Scheme(object): + """A Scheme holds paths which are used as the base directories for + artifacts associated with a Python package. + """ + + __slots__ = SCHEME_KEYS + + def __init__( + self, + platlib, # type: str + purelib, # type: str + headers, # type: str + scripts, # type: str + data, # type: str + ): + self.platlib = platlib + self.purelib = purelib + self.headers = headers + self.scripts = scripts + self.data = data diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/models/search_scope.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/models/search_scope.py new file mode 100644 index 0000000000..d732504e6f --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/models/search_scope.py @@ -0,0 +1,135 @@ +import itertools +import logging +import os +import posixpath + +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.six.moves.urllib import parse as urllib_parse + +from pip._internal.models.index import PyPI +from pip._internal.utils.compat import has_tls +from pip._internal.utils.misc import normalize_path, redact_auth_from_url +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List + + +logger = logging.getLogger(__name__) + + +class SearchScope(object): + + """ + Encapsulates the locations that pip is configured to search. + """ + + __slots__ = ["find_links", "index_urls"] + + @classmethod + def create( + cls, + find_links, # type: List[str] + index_urls, # type: List[str] + ): + # type: (...) -> SearchScope + """ + Create a SearchScope object after normalizing the `find_links`. + """ + # Build find_links. If an argument starts with ~, it may be + # a local file relative to a home directory. So try normalizing + # it and if it exists, use the normalized version. + # This is deliberately conservative - it might be fine just to + # blindly normalize anything starting with a ~... + built_find_links = [] # type: List[str] + for link in find_links: + if link.startswith('~'): + new_link = normalize_path(link) + if os.path.exists(new_link): + link = new_link + built_find_links.append(link) + + # If we don't have TLS enabled, then WARN if anyplace we're looking + # relies on TLS. + if not has_tls(): + for link in itertools.chain(index_urls, built_find_links): + parsed = urllib_parse.urlparse(link) + if parsed.scheme == 'https': + logger.warning( + 'pip is configured with locations that require ' + 'TLS/SSL, however the ssl module in Python is not ' + 'available.' + ) + break + + return cls( + find_links=built_find_links, + index_urls=index_urls, + ) + + def __init__( + self, + find_links, # type: List[str] + index_urls, # type: List[str] + ): + # type: (...) -> None + self.find_links = find_links + self.index_urls = index_urls + + def get_formatted_locations(self): + # type: () -> str + lines = [] + redacted_index_urls = [] + if self.index_urls and self.index_urls != [PyPI.simple_url]: + for url in self.index_urls: + + redacted_index_url = redact_auth_from_url(url) + + # Parse the URL + purl = urllib_parse.urlsplit(redacted_index_url) + + # URL is generally invalid if scheme and netloc is missing + # there are issues with Python and URL parsing, so this test + # is a bit crude. See bpo-20271, bpo-23505. Python doesn't + # always parse invalid URLs correctly - it should raise + # exceptions for malformed URLs + if not purl.scheme and not purl.netloc: + logger.warning( + 'The index url "%s" seems invalid, ' + 'please provide a scheme.', redacted_index_url) + + redacted_index_urls.append(redacted_index_url) + + lines.append('Looking in indexes: {}'.format( + ', '.join(redacted_index_urls))) + + if self.find_links: + lines.append( + 'Looking in links: {}'.format(', '.join( + redact_auth_from_url(url) for url in self.find_links)) + ) + return '\n'.join(lines) + + def get_index_urls_locations(self, project_name): + # type: (str) -> List[str] + """Returns the locations found via self.index_urls + + Checks the url_name on the main (first in the list) index and + use this url_name to produce all locations + """ + + def mkurl_pypi_url(url): + # type: (str) -> str + loc = posixpath.join( + url, + urllib_parse.quote(canonicalize_name(project_name))) + # For maximum compatibility with easy_install, ensure the path + # ends in a trailing slash. Although this isn't in the spec + # (and PyPI can handle it without the slash) some other index + # implementations might break if they relied on easy_install's + # behavior. + if not loc.endswith('/'): + loc = loc + '/' + return loc + + return [mkurl_pypi_url(url) for url in self.index_urls] diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/models/selection_prefs.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/models/selection_prefs.py new file mode 100644 index 0000000000..83110dd8f9 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/models/selection_prefs.py @@ -0,0 +1,50 @@ +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Optional + + from pip._internal.models.format_control import FormatControl + + +class SelectionPreferences(object): + """ + Encapsulates the candidate selection preferences for downloading + and installing files. + """ + + __slots__ = ['allow_yanked', 'allow_all_prereleases', 'format_control', + 'prefer_binary', 'ignore_requires_python'] + + # Don't include an allow_yanked default value to make sure each call + # site considers whether yanked releases are allowed. This also causes + # that decision to be made explicit in the calling code, which helps + # people when reading the code. + def __init__( + self, + allow_yanked, # type: bool + allow_all_prereleases=False, # type: bool + format_control=None, # type: Optional[FormatControl] + prefer_binary=False, # type: bool + ignore_requires_python=None, # type: Optional[bool] + ): + # type: (...) -> None + """Create a SelectionPreferences object. + + :param allow_yanked: Whether files marked as yanked (in the sense + of PEP 592) are permitted to be candidates for install. + :param format_control: A FormatControl object or None. Used to control + the selection of source packages / binary packages when consulting + the index and links. + :param prefer_binary: Whether to prefer an old, but valid, binary + dist over a new source dist. + :param ignore_requires_python: Whether to ignore incompatible + "Requires-Python" values in links. Defaults to False. + """ + if ignore_requires_python is None: + ignore_requires_python = False + + self.allow_yanked = allow_yanked + self.allow_all_prereleases = allow_all_prereleases + self.format_control = format_control + self.prefer_binary = prefer_binary + self.ignore_requires_python = ignore_requires_python diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/models/target_python.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/models/target_python.py new file mode 100644 index 0000000000..4593dc854f --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/models/target_python.py @@ -0,0 +1,117 @@ +import sys + +from pip._internal.utils.compatibility_tags import get_supported, version_info_to_nodot +from pip._internal.utils.misc import normalize_version_info +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List, Optional, Tuple + + from pip._vendor.packaging.tags import Tag + + +class TargetPython(object): + + """ + Encapsulates the properties of a Python interpreter one is targeting + for a package install, download, etc. + """ + + __slots__ = [ + "_given_py_version_info", + "abis", + "implementation", + "platforms", + "py_version", + "py_version_info", + "_valid_tags", + ] + + def __init__( + self, + platforms=None, # type: Optional[List[str]] + py_version_info=None, # type: Optional[Tuple[int, ...]] + abis=None, # type: Optional[List[str]] + implementation=None, # type: Optional[str] + ): + # type: (...) -> None + """ + :param platforms: A list of strings or None. If None, searches for + packages that are supported by the current system. Otherwise, will + find packages that can be built on the platforms passed in. These + packages will only be downloaded for distribution: they will + not be built locally. + :param py_version_info: An optional tuple of ints representing the + Python version information to use (e.g. `sys.version_info[:3]`). + This can have length 1, 2, or 3 when provided. + :param abis: A list of strings or None. This is passed to + compatibility_tags.py's get_supported() function as is. + :param implementation: A string or None. This is passed to + compatibility_tags.py's get_supported() function as is. + """ + # Store the given py_version_info for when we call get_supported(). + self._given_py_version_info = py_version_info + + if py_version_info is None: + py_version_info = sys.version_info[:3] + else: + py_version_info = normalize_version_info(py_version_info) + + py_version = '.'.join(map(str, py_version_info[:2])) + + self.abis = abis + self.implementation = implementation + self.platforms = platforms + self.py_version = py_version + self.py_version_info = py_version_info + + # This is used to cache the return value of get_tags(). + self._valid_tags = None # type: Optional[List[Tag]] + + def format_given(self): + # type: () -> str + """ + Format the given, non-None attributes for display. + """ + display_version = None + if self._given_py_version_info is not None: + display_version = '.'.join( + str(part) for part in self._given_py_version_info + ) + + key_values = [ + ('platforms', self.platforms), + ('version_info', display_version), + ('abis', self.abis), + ('implementation', self.implementation), + ] + return ' '.join( + '{}={!r}'.format(key, value) for key, value in key_values + if value is not None + ) + + def get_tags(self): + # type: () -> List[Tag] + """ + Return the supported PEP 425 tags to check wheel candidates against. + + The tags are returned in order of preference (most preferred first). + """ + if self._valid_tags is None: + # Pass versions=None if no py_version_info was given since + # versions=None uses special default logic. + py_version_info = self._given_py_version_info + if py_version_info is None: + version = None + else: + version = version_info_to_nodot(py_version_info) + + tags = get_supported( + version=version, + platforms=self.platforms, + abis=self.abis, + impl=self.implementation, + ) + self._valid_tags = tags + + return self._valid_tags diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/models/wheel.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/models/wheel.py new file mode 100644 index 0000000000..4d4068f3b7 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/models/wheel.py @@ -0,0 +1,78 @@ +"""Represents a wheel file and provides access to the various parts of the +name that have meaning. +""" +import re + +from pip._vendor.packaging.tags import Tag + +from pip._internal.exceptions import InvalidWheelFilename +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List + + +class Wheel(object): + """A wheel file""" + + wheel_file_re = re.compile( + r"""^(?P(?P.+?)-(?P.*?)) + ((-(?P\d[^-]*?))?-(?P.+?)-(?P.+?)-(?P.+?) + \.whl|\.dist-info)$""", + re.VERBOSE + ) + + def __init__(self, filename): + # type: (str) -> None + """ + :raises InvalidWheelFilename: when the filename is invalid for a wheel + """ + wheel_info = self.wheel_file_re.match(filename) + if not wheel_info: + raise InvalidWheelFilename( + "{} is not a valid wheel filename.".format(filename) + ) + self.filename = filename + self.name = wheel_info.group('name').replace('_', '-') + # we'll assume "_" means "-" due to wheel naming scheme + # (https://github.com/pypa/pip/issues/1150) + self.version = wheel_info.group('ver').replace('_', '-') + self.build_tag = wheel_info.group('build') + self.pyversions = wheel_info.group('pyver').split('.') + self.abis = wheel_info.group('abi').split('.') + self.plats = wheel_info.group('plat').split('.') + + # All the tag combinations from this file + self.file_tags = { + Tag(x, y, z) for x in self.pyversions + for y in self.abis for z in self.plats + } + + def get_formatted_file_tags(self): + # type: () -> List[str] + """Return the wheel's tags as a sorted list of strings.""" + return sorted(str(tag) for tag in self.file_tags) + + def support_index_min(self, tags): + # type: (List[Tag]) -> int + """Return the lowest index that one of the wheel's file_tag combinations + achieves in the given list of supported tags. + + For example, if there are 8 supported tags and one of the file tags + is first in the list, then return 0. + + :param tags: the PEP 425 tags to check the wheel against, in order + with most preferred first. + + :raises ValueError: If none of the wheel's file tags match one of + the supported tags. + """ + return min(tags.index(tag) for tag in self.file_tags if tag in tags) + + def supported(self, tags): + # type: (List[Tag]) -> bool + """Return whether the wheel is compatible with one of the given tags. + + :param tags: the PEP 425 tags to check the wheel against. + """ + return not self.file_tags.isdisjoint(tags) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/network/__init__.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/network/__init__.py new file mode 100644 index 0000000000..b51bde91b2 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/network/__init__.py @@ -0,0 +1,2 @@ +"""Contains purely network-related utilities. +""" diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/network/auth.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/network/auth.py new file mode 100644 index 0000000000..357811a16f --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/network/auth.py @@ -0,0 +1,310 @@ +"""Network Authentication Helpers + +Contains interface (MultiDomainBasicAuth) and associated glue code for +providing credentials in the context of network requests. +""" + +import logging + +from pip._vendor.requests.auth import AuthBase, HTTPBasicAuth +from pip._vendor.requests.utils import get_netrc_auth +from pip._vendor.six.moves.urllib import parse as urllib_parse + +from pip._internal.utils.misc import ( + ask, + ask_input, + ask_password, + remove_auth_from_url, + split_auth_netloc_from_url, +) +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Any, Dict, List, Optional, Tuple + + from pip._vendor.requests.models import Request, Response + + from pip._internal.vcs.versioncontrol import AuthInfo + + Credentials = Tuple[str, str, str] + +logger = logging.getLogger(__name__) + +try: + import keyring # noqa +except ImportError: + keyring = None +except Exception as exc: + logger.warning( + "Keyring is skipped due to an exception: %s", str(exc), + ) + keyring = None + + +def get_keyring_auth(url, username): + # type: (str, str) -> Optional[AuthInfo] + """Return the tuple auth for a given url from keyring.""" + global keyring + if not url or not keyring: + return None + + try: + try: + get_credential = keyring.get_credential + except AttributeError: + pass + else: + logger.debug("Getting credentials from keyring for %s", url) + cred = get_credential(url, username) + if cred is not None: + return cred.username, cred.password + return None + + if username: + logger.debug("Getting password from keyring for %s", url) + password = keyring.get_password(url, username) + if password: + return username, password + + except Exception as exc: + logger.warning( + "Keyring is skipped due to an exception: %s", str(exc), + ) + keyring = None + return None + + +class MultiDomainBasicAuth(AuthBase): + + def __init__(self, prompting=True, index_urls=None): + # type: (bool, Optional[List[str]]) -> None + self.prompting = prompting + self.index_urls = index_urls + self.passwords = {} # type: Dict[str, AuthInfo] + # When the user is prompted to enter credentials and keyring is + # available, we will offer to save them. If the user accepts, + # this value is set to the credentials they entered. After the + # request authenticates, the caller should call + # ``save_credentials`` to save these. + self._credentials_to_save = None # type: Optional[Credentials] + + def _get_index_url(self, url): + # type: (str) -> Optional[str] + """Return the original index URL matching the requested URL. + + Cached or dynamically generated credentials may work against + the original index URL rather than just the netloc. + + The provided url should have had its username and password + removed already. If the original index url had credentials then + they will be included in the return value. + + Returns None if no matching index was found, or if --no-index + was specified by the user. + """ + if not url or not self.index_urls: + return None + + for u in self.index_urls: + prefix = remove_auth_from_url(u).rstrip("/") + "/" + if url.startswith(prefix): + return u + return None + + def _get_new_credentials(self, original_url, allow_netrc=True, + allow_keyring=True): + # type: (str, bool, bool) -> AuthInfo + """Find and return credentials for the specified URL.""" + # Split the credentials and netloc from the url. + url, netloc, url_user_password = split_auth_netloc_from_url( + original_url, + ) + + # Start with the credentials embedded in the url + username, password = url_user_password + if username is not None and password is not None: + logger.debug("Found credentials in url for %s", netloc) + return url_user_password + + # Find a matching index url for this request + index_url = self._get_index_url(url) + if index_url: + # Split the credentials from the url. + index_info = split_auth_netloc_from_url(index_url) + if index_info: + index_url, _, index_url_user_password = index_info + logger.debug("Found index url %s", index_url) + + # If an index URL was found, try its embedded credentials + if index_url and index_url_user_password[0] is not None: + username, password = index_url_user_password + if username is not None and password is not None: + logger.debug("Found credentials in index url for %s", netloc) + return index_url_user_password + + # Get creds from netrc if we still don't have them + if allow_netrc: + netrc_auth = get_netrc_auth(original_url) + if netrc_auth: + logger.debug("Found credentials in netrc for %s", netloc) + return netrc_auth + + # If we don't have a password and keyring is available, use it. + if allow_keyring: + # The index url is more specific than the netloc, so try it first + kr_auth = ( + get_keyring_auth(index_url, username) or + get_keyring_auth(netloc, username) + ) + if kr_auth: + logger.debug("Found credentials in keyring for %s", netloc) + return kr_auth + + return username, password + + def _get_url_and_credentials(self, original_url): + # type: (str) -> Tuple[str, Optional[str], Optional[str]] + """Return the credentials to use for the provided URL. + + If allowed, netrc and keyring may be used to obtain the + correct credentials. + + Returns (url_without_credentials, username, password). Note + that even if the original URL contains credentials, this + function may return a different username and password. + """ + url, netloc, _ = split_auth_netloc_from_url(original_url) + + # Use any stored credentials that we have for this netloc + username, password = self.passwords.get(netloc, (None, None)) + + if username is None and password is None: + # No stored credentials. Acquire new credentials without prompting + # the user. (e.g. from netrc, keyring, or the URL itself) + username, password = self._get_new_credentials(original_url) + + if username is not None or password is not None: + # Convert the username and password if they're None, so that + # this netloc will show up as "cached" in the conditional above. + # Further, HTTPBasicAuth doesn't accept None, so it makes sense to + # cache the value that is going to be used. + username = username or "" + password = password or "" + + # Store any acquired credentials. + self.passwords[netloc] = (username, password) + + assert ( + # Credentials were found + (username is not None and password is not None) or + # Credentials were not found + (username is None and password is None) + ), "Could not load credentials from url: {}".format(original_url) + + return url, username, password + + def __call__(self, req): + # type: (Request) -> Request + # Get credentials for this request + url, username, password = self._get_url_and_credentials(req.url) + + # Set the url of the request to the url without any credentials + req.url = url + + if username is not None and password is not None: + # Send the basic auth with this request + req = HTTPBasicAuth(username, password)(req) + + # Attach a hook to handle 401 responses + req.register_hook("response", self.handle_401) + + return req + + # Factored out to allow for easy patching in tests + def _prompt_for_password(self, netloc): + # type: (str) -> Tuple[Optional[str], Optional[str], bool] + username = ask_input("User for {}: ".format(netloc)) + if not username: + return None, None, False + auth = get_keyring_auth(netloc, username) + if auth and auth[0] is not None and auth[1] is not None: + return auth[0], auth[1], False + password = ask_password("Password: ") + return username, password, True + + # Factored out to allow for easy patching in tests + def _should_save_password_to_keyring(self): + # type: () -> bool + if not keyring: + return False + return ask("Save credentials to keyring [y/N]: ", ["y", "n"]) == "y" + + def handle_401(self, resp, **kwargs): + # type: (Response, **Any) -> Response + # We only care about 401 responses, anything else we want to just + # pass through the actual response + if resp.status_code != 401: + return resp + + # We are not able to prompt the user so simply return the response + if not self.prompting: + return resp + + parsed = urllib_parse.urlparse(resp.url) + + # Prompt the user for a new username and password + username, password, save = self._prompt_for_password(parsed.netloc) + + # Store the new username and password to use for future requests + self._credentials_to_save = None + if username is not None and password is not None: + self.passwords[parsed.netloc] = (username, password) + + # Prompt to save the password to keyring + if save and self._should_save_password_to_keyring(): + self._credentials_to_save = (parsed.netloc, username, password) + + # Consume content and release the original connection to allow our new + # request to reuse the same one. + resp.content + resp.raw.release_conn() + + # Add our new username and password to the request + req = HTTPBasicAuth(username or "", password or "")(resp.request) + req.register_hook("response", self.warn_on_401) + + # On successful request, save the credentials that were used to + # keyring. (Note that if the user responded "no" above, this member + # is not set and nothing will be saved.) + if self._credentials_to_save: + req.register_hook("response", self.save_credentials) + + # Send our new request + new_resp = resp.connection.send(req, **kwargs) + new_resp.history.append(resp) + + return new_resp + + def warn_on_401(self, resp, **kwargs): + # type: (Response, **Any) -> None + """Response callback to warn about incorrect credentials.""" + if resp.status_code == 401: + logger.warning( + '401 Error, Credentials not correct for %s', resp.request.url, + ) + + def save_credentials(self, resp, **kwargs): + # type: (Response, **Any) -> None + """Response callback to save credentials on success.""" + assert keyring is not None, "should never reach here without keyring" + if not keyring: + return + + creds = self._credentials_to_save + self._credentials_to_save = None + if creds and resp.status_code < 400: + try: + logger.info('Saving credentials to keyring') + keyring.set_password(*creds) + except Exception: + logger.exception('Failed to save credentials') diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/network/cache.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/network/cache.py new file mode 100644 index 0000000000..d2a1b7313f --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/network/cache.py @@ -0,0 +1,79 @@ +"""HTTP cache implementation. +""" + +import os +from contextlib import contextmanager + +from pip._vendor.cachecontrol.cache import BaseCache +from pip._vendor.cachecontrol.caches import FileCache +from pip._vendor.requests.models import Response + +from pip._internal.utils.filesystem import adjacent_tmp_file, replace +from pip._internal.utils.misc import ensure_dir +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Iterator, Optional + + +def is_from_cache(response): + # type: (Response) -> bool + return getattr(response, "from_cache", False) + + +@contextmanager +def suppressed_cache_errors(): + # type: () -> Iterator[None] + """If we can't access the cache then we can just skip caching and process + requests as if caching wasn't enabled. + """ + try: + yield + except (OSError, IOError): + pass + + +class SafeFileCache(BaseCache): + """ + A file based cache which is safe to use even when the target directory may + not be accessible or writable. + """ + + def __init__(self, directory): + # type: (str) -> None + assert directory is not None, "Cache directory must not be None." + super(SafeFileCache, self).__init__() + self.directory = directory + + def _get_cache_path(self, name): + # type: (str) -> str + # From cachecontrol.caches.file_cache.FileCache._fn, brought into our + # class for backwards-compatibility and to avoid using a non-public + # method. + hashed = FileCache.encode(name) + parts = list(hashed[:5]) + [hashed] + return os.path.join(self.directory, *parts) + + def get(self, key): + # type: (str) -> Optional[bytes] + path = self._get_cache_path(key) + with suppressed_cache_errors(): + with open(path, 'rb') as f: + return f.read() + + def set(self, key, value): + # type: (str, bytes) -> None + path = self._get_cache_path(key) + with suppressed_cache_errors(): + ensure_dir(os.path.dirname(path)) + + with adjacent_tmp_file(path) as f: + f.write(value) + + replace(f.name, path) + + def delete(self, key): + # type: (str) -> None + path = self._get_cache_path(key) + with suppressed_cache_errors(): + os.remove(path) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/network/download.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/network/download.py new file mode 100644 index 0000000000..76896e8997 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/network/download.py @@ -0,0 +1,202 @@ +"""Download files with progress indicators. +""" +import cgi +import logging +import mimetypes +import os + +from pip._vendor.requests.models import CONTENT_CHUNK_SIZE + +from pip._internal.cli.progress_bars import DownloadProgressProvider +from pip._internal.exceptions import NetworkConnectionError +from pip._internal.models.index import PyPI +from pip._internal.network.cache import is_from_cache +from pip._internal.network.utils import HEADERS, raise_for_status, response_chunks +from pip._internal.utils.misc import format_size, redact_auth_from_url, splitext +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Iterable, Optional, Tuple + + from pip._vendor.requests.models import Response + + from pip._internal.models.link import Link + from pip._internal.network.session import PipSession + +logger = logging.getLogger(__name__) + + +def _get_http_response_size(resp): + # type: (Response) -> Optional[int] + try: + return int(resp.headers['content-length']) + except (ValueError, KeyError, TypeError): + return None + + +def _prepare_download( + resp, # type: Response + link, # type: Link + progress_bar # type: str +): + # type: (...) -> Iterable[bytes] + total_length = _get_http_response_size(resp) + + if link.netloc == PyPI.file_storage_domain: + url = link.show_url + else: + url = link.url_without_fragment + + logged_url = redact_auth_from_url(url) + + if total_length: + logged_url = '{} ({})'.format(logged_url, format_size(total_length)) + + if is_from_cache(resp): + logger.info("Using cached %s", logged_url) + else: + logger.info("Downloading %s", logged_url) + + if logger.getEffectiveLevel() > logging.INFO: + show_progress = False + elif is_from_cache(resp): + show_progress = False + elif not total_length: + show_progress = True + elif total_length > (40 * 1000): + show_progress = True + else: + show_progress = False + + chunks = response_chunks(resp, CONTENT_CHUNK_SIZE) + + if not show_progress: + return chunks + + return DownloadProgressProvider( + progress_bar, max=total_length + )(chunks) + + +def sanitize_content_filename(filename): + # type: (str) -> str + """ + Sanitize the "filename" value from a Content-Disposition header. + """ + return os.path.basename(filename) + + +def parse_content_disposition(content_disposition, default_filename): + # type: (str, str) -> str + """ + Parse the "filename" value from a Content-Disposition header, and + return the default filename if the result is empty. + """ + _type, params = cgi.parse_header(content_disposition) + filename = params.get('filename') + if filename: + # We need to sanitize the filename to prevent directory traversal + # in case the filename contains ".." path parts. + filename = sanitize_content_filename(filename) + return filename or default_filename + + +def _get_http_response_filename(resp, link): + # type: (Response, Link) -> str + """Get an ideal filename from the given HTTP response, falling back to + the link filename if not provided. + """ + filename = link.filename # fallback + # Have a look at the Content-Disposition header for a better guess + content_disposition = resp.headers.get('content-disposition') + if content_disposition: + filename = parse_content_disposition(content_disposition, filename) + ext = splitext(filename)[1] # type: Optional[str] + if not ext: + ext = mimetypes.guess_extension( + resp.headers.get('content-type', '') + ) + if ext: + filename += ext + if not ext and link.url != resp.url: + ext = os.path.splitext(resp.url)[1] + if ext: + filename += ext + return filename + + +def _http_get_download(session, link): + # type: (PipSession, Link) -> Response + target_url = link.url.split('#', 1)[0] + resp = session.get(target_url, headers=HEADERS, stream=True) + raise_for_status(resp) + return resp + + +class Downloader(object): + def __init__( + self, + session, # type: PipSession + progress_bar, # type: str + ): + # type: (...) -> None + self._session = session + self._progress_bar = progress_bar + + def __call__(self, link, location): + # type: (Link, str) -> Tuple[str, str] + """Download the file given by link into location.""" + try: + resp = _http_get_download(self._session, link) + except NetworkConnectionError as e: + assert e.response is not None + logger.critical( + "HTTP error %s while getting %s", e.response.status_code, link + ) + raise + + filename = _get_http_response_filename(resp, link) + filepath = os.path.join(location, filename) + + chunks = _prepare_download(resp, link, self._progress_bar) + with open(filepath, 'wb') as content_file: + for chunk in chunks: + content_file.write(chunk) + content_type = resp.headers.get('Content-Type', '') + return filepath, content_type + + +class BatchDownloader(object): + + def __init__( + self, + session, # type: PipSession + progress_bar, # type: str + ): + # type: (...) -> None + self._session = session + self._progress_bar = progress_bar + + def __call__(self, links, location): + # type: (Iterable[Link], str) -> Iterable[Tuple[str, Tuple[str, str]]] + """Download the files given by links into location.""" + for link in links: + try: + resp = _http_get_download(self._session, link) + except NetworkConnectionError as e: + assert e.response is not None + logger.critical( + "HTTP error %s while getting %s", + e.response.status_code, link, + ) + raise + + filename = _get_http_response_filename(resp, link) + filepath = os.path.join(location, filename) + + chunks = _prepare_download(resp, link, self._progress_bar) + with open(filepath, 'wb') as content_file: + for chunk in chunks: + content_file.write(chunk) + content_type = resp.headers.get('Content-Type', '') + yield link.url, (filepath, content_type) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/network/lazy_wheel.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/network/lazy_wheel.py new file mode 100644 index 0000000000..608475abab --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/network/lazy_wheel.py @@ -0,0 +1,231 @@ +"""Lazy ZIP over HTTP""" + +__all__ = ['HTTPRangeRequestUnsupported', 'dist_from_wheel_url'] + +from bisect import bisect_left, bisect_right +from contextlib import contextmanager +from tempfile import NamedTemporaryFile +from zipfile import BadZipfile, ZipFile + +from pip._vendor.requests.models import CONTENT_CHUNK_SIZE +from pip._vendor.six.moves import range + +from pip._internal.network.utils import HEADERS, raise_for_status, response_chunks +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.wheel import pkg_resources_distribution_for_wheel + +if MYPY_CHECK_RUNNING: + from typing import Any, Dict, Iterator, List, Optional, Tuple + + from pip._vendor.pkg_resources import Distribution + from pip._vendor.requests.models import Response + + from pip._internal.network.session import PipSession + + +class HTTPRangeRequestUnsupported(Exception): + pass + + +def dist_from_wheel_url(name, url, session): + # type: (str, str, PipSession) -> Distribution + """Return a pkg_resources.Distribution from the given wheel URL. + + This uses HTTP range requests to only fetch the potion of the wheel + containing metadata, just enough for the object to be constructed. + If such requests are not supported, HTTPRangeRequestUnsupported + is raised. + """ + with LazyZipOverHTTP(url, session) as wheel: + # For read-only ZIP files, ZipFile only needs methods read, + # seek, seekable and tell, not the whole IO protocol. + zip_file = ZipFile(wheel) # type: ignore + # After context manager exit, wheel.name + # is an invalid file by intention. + return pkg_resources_distribution_for_wheel(zip_file, name, wheel.name) + + +class LazyZipOverHTTP(object): + """File-like object mapped to a ZIP file over HTTP. + + This uses HTTP range requests to lazily fetch the file's content, + which is supposed to be fed to ZipFile. If such requests are not + supported by the server, raise HTTPRangeRequestUnsupported + during initialization. + """ + + def __init__(self, url, session, chunk_size=CONTENT_CHUNK_SIZE): + # type: (str, PipSession, int) -> None + head = session.head(url, headers=HEADERS) + raise_for_status(head) + assert head.status_code == 200 + self._session, self._url, self._chunk_size = session, url, chunk_size + self._length = int(head.headers['Content-Length']) + self._file = NamedTemporaryFile() + self.truncate(self._length) + self._left = [] # type: List[int] + self._right = [] # type: List[int] + if 'bytes' not in head.headers.get('Accept-Ranges', 'none'): + raise HTTPRangeRequestUnsupported('range request is not supported') + self._check_zip() + + @property + def mode(self): + # type: () -> str + """Opening mode, which is always rb.""" + return 'rb' + + @property + def name(self): + # type: () -> str + """Path to the underlying file.""" + return self._file.name + + def seekable(self): + # type: () -> bool + """Return whether random access is supported, which is True.""" + return True + + def close(self): + # type: () -> None + """Close the file.""" + self._file.close() + + @property + def closed(self): + # type: () -> bool + """Whether the file is closed.""" + return self._file.closed + + def read(self, size=-1): + # type: (int) -> bytes + """Read up to size bytes from the object and return them. + + As a convenience, if size is unspecified or -1, + all bytes until EOF are returned. Fewer than + size bytes may be returned if EOF is reached. + """ + download_size = max(size, self._chunk_size) + start, length = self.tell(), self._length + stop = length if size < 0 else min(start+download_size, length) + start = max(0, stop-download_size) + self._download(start, stop-1) + return self._file.read(size) + + def readable(self): + # type: () -> bool + """Return whether the file is readable, which is True.""" + return True + + def seek(self, offset, whence=0): + # type: (int, int) -> int + """Change stream position and return the new absolute position. + + Seek to offset relative position indicated by whence: + * 0: Start of stream (the default). pos should be >= 0; + * 1: Current position - pos may be negative; + * 2: End of stream - pos usually negative. + """ + return self._file.seek(offset, whence) + + def tell(self): + # type: () -> int + """Return the current possition.""" + return self._file.tell() + + def truncate(self, size=None): + # type: (Optional[int]) -> int + """Resize the stream to the given size in bytes. + + If size is unspecified resize to the current position. + The current stream position isn't changed. + + Return the new file size. + """ + return self._file.truncate(size) + + def writable(self): + # type: () -> bool + """Return False.""" + return False + + def __enter__(self): + # type: () -> LazyZipOverHTTP + self._file.__enter__() + return self + + def __exit__(self, *exc): + # type: (*Any) -> Optional[bool] + return self._file.__exit__(*exc) + + @contextmanager + def _stay(self): + # type: ()-> Iterator[None] + """Return a context manager keeping the position. + + At the end of the block, seek back to original position. + """ + pos = self.tell() + try: + yield + finally: + self.seek(pos) + + def _check_zip(self): + # type: () -> None + """Check and download until the file is a valid ZIP.""" + end = self._length - 1 + for start in reversed(range(0, end, self._chunk_size)): + self._download(start, end) + with self._stay(): + try: + # For read-only ZIP files, ZipFile only needs + # methods read, seek, seekable and tell. + ZipFile(self) # type: ignore + except BadZipfile: + pass + else: + break + + def _stream_response(self, start, end, base_headers=HEADERS): + # type: (int, int, Dict[str, str]) -> Response + """Return HTTP response to a range request from start to end.""" + headers = base_headers.copy() + headers['Range'] = 'bytes={}-{}'.format(start, end) + # TODO: Get range requests to be correctly cached + headers['Cache-Control'] = 'no-cache' + return self._session.get(self._url, headers=headers, stream=True) + + def _merge(self, start, end, left, right): + # type: (int, int, int, int) -> Iterator[Tuple[int, int]] + """Return an iterator of intervals to be fetched. + + Args: + start (int): Start of needed interval + end (int): End of needed interval + left (int): Index of first overlapping downloaded data + right (int): Index after last overlapping downloaded data + """ + lslice, rslice = self._left[left:right], self._right[left:right] + i = start = min([start]+lslice[:1]) + end = max([end]+rslice[-1:]) + for j, k in zip(lslice, rslice): + if j > i: + yield i, j-1 + i = k + 1 + if i <= end: + yield i, end + self._left[left:right], self._right[left:right] = [start], [end] + + def _download(self, start, end): + # type: (int, int) -> None + """Download bytes from start to end inclusively.""" + with self._stay(): + left = bisect_left(self._right, start) + right = bisect_right(self._left, end) + for start, end in self._merge(start, end, left, right): + response = self._stream_response(start, end) + response.raise_for_status() + self.seek(start) + for chunk in response_chunks(response, self._chunk_size): + self._file.write(chunk) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/network/session.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/network/session.py new file mode 100644 index 0000000000..454945d9ae --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/network/session.py @@ -0,0 +1,428 @@ +"""PipSession and supporting code, containing all pip-specific +network request configuration and behavior. +""" + +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False + +import email.utils +import json +import logging +import mimetypes +import os +import platform +import sys +import warnings + +from pip._vendor import requests, six, urllib3 +from pip._vendor.cachecontrol import CacheControlAdapter +from pip._vendor.requests.adapters import BaseAdapter, HTTPAdapter +from pip._vendor.requests.models import Response +from pip._vendor.requests.structures import CaseInsensitiveDict +from pip._vendor.six.moves.urllib import parse as urllib_parse +from pip._vendor.urllib3.exceptions import InsecureRequestWarning + +from pip import __version__ +from pip._internal.network.auth import MultiDomainBasicAuth +from pip._internal.network.cache import SafeFileCache + +# Import ssl from compat so the initial import occurs in only one place. +from pip._internal.utils.compat import has_tls, ipaddress +from pip._internal.utils.glibc import libc_ver +from pip._internal.utils.misc import ( + build_url_from_netloc, + get_installed_version, + parse_netloc, +) +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.urls import url_to_path + +if MYPY_CHECK_RUNNING: + from typing import Iterator, List, Optional, Tuple, Union + + from pip._internal.models.link import Link + + SecureOrigin = Tuple[str, str, Optional[Union[int, str]]] + + +logger = logging.getLogger(__name__) + + +# Ignore warning raised when using --trusted-host. +warnings.filterwarnings("ignore", category=InsecureRequestWarning) + + +SECURE_ORIGINS = [ + # protocol, hostname, port + # Taken from Chrome's list of secure origins (See: http://bit.ly/1qrySKC) + ("https", "*", "*"), + ("*", "localhost", "*"), + ("*", "127.0.0.0/8", "*"), + ("*", "::1/128", "*"), + ("file", "*", None), + # ssh is always secure. + ("ssh", "*", "*"), +] # type: List[SecureOrigin] + + +# These are environment variables present when running under various +# CI systems. For each variable, some CI systems that use the variable +# are indicated. The collection was chosen so that for each of a number +# of popular systems, at least one of the environment variables is used. +# This list is used to provide some indication of and lower bound for +# CI traffic to PyPI. Thus, it is okay if the list is not comprehensive. +# For more background, see: https://github.com/pypa/pip/issues/5499 +CI_ENVIRONMENT_VARIABLES = ( + # Azure Pipelines + 'BUILD_BUILDID', + # Jenkins + 'BUILD_ID', + # AppVeyor, CircleCI, Codeship, Gitlab CI, Shippable, Travis CI + 'CI', + # Explicit environment variable. + 'PIP_IS_CI', +) + + +def looks_like_ci(): + # type: () -> bool + """ + Return whether it looks like pip is running under CI. + """ + # We don't use the method of checking for a tty (e.g. using isatty()) + # because some CI systems mimic a tty (e.g. Travis CI). Thus that + # method doesn't provide definitive information in either direction. + return any(name in os.environ for name in CI_ENVIRONMENT_VARIABLES) + + +def user_agent(): + """ + Return a string representing the user agent. + """ + data = { + "installer": {"name": "pip", "version": __version__}, + "python": platform.python_version(), + "implementation": { + "name": platform.python_implementation(), + }, + } + + if data["implementation"]["name"] == 'CPython': + data["implementation"]["version"] = platform.python_version() + elif data["implementation"]["name"] == 'PyPy': + if sys.pypy_version_info.releaselevel == 'final': + pypy_version_info = sys.pypy_version_info[:3] + else: + pypy_version_info = sys.pypy_version_info + data["implementation"]["version"] = ".".join( + [str(x) for x in pypy_version_info] + ) + elif data["implementation"]["name"] == 'Jython': + # Complete Guess + data["implementation"]["version"] = platform.python_version() + elif data["implementation"]["name"] == 'IronPython': + # Complete Guess + data["implementation"]["version"] = platform.python_version() + + if sys.platform.startswith("linux"): + from pip._vendor import distro + distro_infos = dict(filter( + lambda x: x[1], + zip(["name", "version", "id"], distro.linux_distribution()), + )) + libc = dict(filter( + lambda x: x[1], + zip(["lib", "version"], libc_ver()), + )) + if libc: + distro_infos["libc"] = libc + if distro_infos: + data["distro"] = distro_infos + + if sys.platform.startswith("darwin") and platform.mac_ver()[0]: + data["distro"] = {"name": "macOS", "version": platform.mac_ver()[0]} + + if platform.system(): + data.setdefault("system", {})["name"] = platform.system() + + if platform.release(): + data.setdefault("system", {})["release"] = platform.release() + + if platform.machine(): + data["cpu"] = platform.machine() + + if has_tls(): + import _ssl as ssl + data["openssl_version"] = ssl.OPENSSL_VERSION + + setuptools_version = get_installed_version("setuptools") + if setuptools_version is not None: + data["setuptools_version"] = setuptools_version + + # Use None rather than False so as not to give the impression that + # pip knows it is not being run under CI. Rather, it is a null or + # inconclusive result. Also, we include some value rather than no + # value to make it easier to know that the check has been run. + data["ci"] = True if looks_like_ci() else None + + user_data = os.environ.get("PIP_USER_AGENT_USER_DATA") + if user_data is not None: + data["user_data"] = user_data + + return "{data[installer][name]}/{data[installer][version]} {json}".format( + data=data, + json=json.dumps(data, separators=(",", ":"), sort_keys=True), + ) + + +class LocalFSAdapter(BaseAdapter): + + def send(self, request, stream=None, timeout=None, verify=None, cert=None, + proxies=None): + pathname = url_to_path(request.url) + + resp = Response() + resp.status_code = 200 + resp.url = request.url + + try: + stats = os.stat(pathname) + except OSError as exc: + resp.status_code = 404 + resp.raw = exc + else: + modified = email.utils.formatdate(stats.st_mtime, usegmt=True) + content_type = mimetypes.guess_type(pathname)[0] or "text/plain" + resp.headers = CaseInsensitiveDict({ + "Content-Type": content_type, + "Content-Length": stats.st_size, + "Last-Modified": modified, + }) + + resp.raw = open(pathname, "rb") + resp.close = resp.raw.close + + return resp + + def close(self): + pass + + +class InsecureHTTPAdapter(HTTPAdapter): + + def cert_verify(self, conn, url, verify, cert): + super(InsecureHTTPAdapter, self).cert_verify( + conn=conn, url=url, verify=False, cert=cert + ) + + +class InsecureCacheControlAdapter(CacheControlAdapter): + + def cert_verify(self, conn, url, verify, cert): + super(InsecureCacheControlAdapter, self).cert_verify( + conn=conn, url=url, verify=False, cert=cert + ) + + +class PipSession(requests.Session): + + timeout = None # type: Optional[int] + + def __init__(self, *args, **kwargs): + """ + :param trusted_hosts: Domains not to emit warnings for when not using + HTTPS. + """ + retries = kwargs.pop("retries", 0) + cache = kwargs.pop("cache", None) + trusted_hosts = kwargs.pop("trusted_hosts", []) # type: List[str] + index_urls = kwargs.pop("index_urls", None) + + super(PipSession, self).__init__(*args, **kwargs) + + # Namespace the attribute with "pip_" just in case to prevent + # possible conflicts with the base class. + self.pip_trusted_origins = [] # type: List[Tuple[str, Optional[int]]] + + # Attach our User Agent to the request + self.headers["User-Agent"] = user_agent() + + # Attach our Authentication handler to the session + self.auth = MultiDomainBasicAuth(index_urls=index_urls) + + # Create our urllib3.Retry instance which will allow us to customize + # how we handle retries. + retries = urllib3.Retry( + # Set the total number of retries that a particular request can + # have. + total=retries, + + # A 503 error from PyPI typically means that the Fastly -> Origin + # connection got interrupted in some way. A 503 error in general + # is typically considered a transient error so we'll go ahead and + # retry it. + # A 500 may indicate transient error in Amazon S3 + # A 520 or 527 - may indicate transient error in CloudFlare + status_forcelist=[500, 503, 520, 527], + + # Add a small amount of back off between failed requests in + # order to prevent hammering the service. + backoff_factor=0.25, + ) + + # Our Insecure HTTPAdapter disables HTTPS validation. It does not + # support caching so we'll use it for all http:// URLs. + # If caching is disabled, we will also use it for + # https:// hosts that we've marked as ignoring + # TLS errors for (trusted-hosts). + insecure_adapter = InsecureHTTPAdapter(max_retries=retries) + + # We want to _only_ cache responses on securely fetched origins or when + # the host is specified as trusted. We do this because + # we can't validate the response of an insecurely/untrusted fetched + # origin, and we don't want someone to be able to poison the cache and + # require manual eviction from the cache to fix it. + if cache: + secure_adapter = CacheControlAdapter( + cache=SafeFileCache(cache), + max_retries=retries, + ) + self._trusted_host_adapter = InsecureCacheControlAdapter( + cache=SafeFileCache(cache), + max_retries=retries, + ) + else: + secure_adapter = HTTPAdapter(max_retries=retries) + self._trusted_host_adapter = insecure_adapter + + self.mount("https://", secure_adapter) + self.mount("http://", insecure_adapter) + + # Enable file:// urls + self.mount("file://", LocalFSAdapter()) + + for host in trusted_hosts: + self.add_trusted_host(host, suppress_logging=True) + + def update_index_urls(self, new_index_urls): + # type: (List[str]) -> None + """ + :param new_index_urls: New index urls to update the authentication + handler with. + """ + self.auth.index_urls = new_index_urls + + def add_trusted_host(self, host, source=None, suppress_logging=False): + # type: (str, Optional[str], bool) -> None + """ + :param host: It is okay to provide a host that has previously been + added. + :param source: An optional source string, for logging where the host + string came from. + """ + if not suppress_logging: + msg = 'adding trusted host: {!r}'.format(host) + if source is not None: + msg += ' (from {})'.format(source) + logger.info(msg) + + host_port = parse_netloc(host) + if host_port not in self.pip_trusted_origins: + self.pip_trusted_origins.append(host_port) + + self.mount( + build_url_from_netloc(host) + '/', + self._trusted_host_adapter + ) + if not host_port[1]: + # Mount wildcard ports for the same host. + self.mount( + build_url_from_netloc(host) + ':', + self._trusted_host_adapter + ) + + def iter_secure_origins(self): + # type: () -> Iterator[SecureOrigin] + for secure_origin in SECURE_ORIGINS: + yield secure_origin + for host, port in self.pip_trusted_origins: + yield ('*', host, '*' if port is None else port) + + def is_secure_origin(self, location): + # type: (Link) -> bool + # Determine if this url used a secure transport mechanism + parsed = urllib_parse.urlparse(str(location)) + origin_protocol, origin_host, origin_port = ( + parsed.scheme, parsed.hostname, parsed.port, + ) + + # The protocol to use to see if the protocol matches. + # Don't count the repository type as part of the protocol: in + # cases such as "git+ssh", only use "ssh". (I.e., Only verify against + # the last scheme.) + origin_protocol = origin_protocol.rsplit('+', 1)[-1] + + # Determine if our origin is a secure origin by looking through our + # hardcoded list of secure origins, as well as any additional ones + # configured on this PackageFinder instance. + for secure_origin in self.iter_secure_origins(): + secure_protocol, secure_host, secure_port = secure_origin + if origin_protocol != secure_protocol and secure_protocol != "*": + continue + + try: + addr = ipaddress.ip_address( + None + if origin_host is None + else six.ensure_text(origin_host) + ) + network = ipaddress.ip_network( + six.ensure_text(secure_host) + ) + except ValueError: + # We don't have both a valid address or a valid network, so + # we'll check this origin against hostnames. + if ( + origin_host and + origin_host.lower() != secure_host.lower() and + secure_host != "*" + ): + continue + else: + # We have a valid address and network, so see if the address + # is contained within the network. + if addr not in network: + continue + + # Check to see if the port matches. + if ( + origin_port != secure_port and + secure_port != "*" and + secure_port is not None + ): + continue + + # If we've gotten here, then this origin matches the current + # secure origin and we should return True + return True + + # If we've gotten to this point, then the origin isn't secure and we + # will not accept it as a valid location to search. We will however + # log a warning that we are ignoring it. + logger.warning( + "The repository located at %s is not a trusted or secure host and " + "is being ignored. If this repository is available via HTTPS we " + "recommend you use HTTPS instead, otherwise you may silence " + "this warning and allow it anyway with '--trusted-host %s'.", + origin_host, + origin_host, + ) + + return False + + def request(self, method, url, *args, **kwargs): + # Allow setting a default timeout on a session + kwargs.setdefault("timeout", self.timeout) + + # Dispatch the actual request + return super(PipSession, self).request(method, url, *args, **kwargs) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/network/utils.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/network/utils.py new file mode 100644 index 0000000000..907b3fed49 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/network/utils.py @@ -0,0 +1,97 @@ +from pip._vendor.requests.models import CONTENT_CHUNK_SIZE, Response + +from pip._internal.exceptions import NetworkConnectionError +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Dict, Iterator + +# The following comments and HTTP headers were originally added by +# Donald Stufft in git commit 22c562429a61bb77172039e480873fb239dd8c03. +# +# We use Accept-Encoding: identity here because requests defaults to +# accepting compressed responses. This breaks in a variety of ways +# depending on how the server is configured. +# - Some servers will notice that the file isn't a compressible file +# and will leave the file alone and with an empty Content-Encoding +# - Some servers will notice that the file is already compressed and +# will leave the file alone, adding a Content-Encoding: gzip header +# - Some servers won't notice anything at all and will take a file +# that's already been compressed and compress it again, and set +# the Content-Encoding: gzip header +# By setting this to request only the identity encoding we're hoping +# to eliminate the third case. Hopefully there does not exist a server +# which when given a file will notice it is already compressed and that +# you're not asking for a compressed file and will then decompress it +# before sending because if that's the case I don't think it'll ever be +# possible to make this work. +HEADERS = {'Accept-Encoding': 'identity'} # type: Dict[str, str] + + +def raise_for_status(resp): + # type: (Response) -> None + http_error_msg = u'' + if isinstance(resp.reason, bytes): + # We attempt to decode utf-8 first because some servers + # choose to localize their reason strings. If the string + # isn't utf-8, we fall back to iso-8859-1 for all other + # encodings. + try: + reason = resp.reason.decode('utf-8') + except UnicodeDecodeError: + reason = resp.reason.decode('iso-8859-1') + else: + reason = resp.reason + + if 400 <= resp.status_code < 500: + http_error_msg = u'%s Client Error: %s for url: %s' % ( + resp.status_code, reason, resp.url) + + elif 500 <= resp.status_code < 600: + http_error_msg = u'%s Server Error: %s for url: %s' % ( + resp.status_code, reason, resp.url) + + if http_error_msg: + raise NetworkConnectionError(http_error_msg, response=resp) + + +def response_chunks(response, chunk_size=CONTENT_CHUNK_SIZE): + # type: (Response, int) -> Iterator[bytes] + """Given a requests Response, provide the data chunks. + """ + try: + # Special case for urllib3. + for chunk in response.raw.stream( + chunk_size, + # We use decode_content=False here because we don't + # want urllib3 to mess with the raw bytes we get + # from the server. If we decompress inside of + # urllib3 then we cannot verify the checksum + # because the checksum will be of the compressed + # file. This breakage will only occur if the + # server adds a Content-Encoding header, which + # depends on how the server was configured: + # - Some servers will notice that the file isn't a + # compressible file and will leave the file alone + # and with an empty Content-Encoding + # - Some servers will notice that the file is + # already compressed and will leave the file + # alone and will add a Content-Encoding: gzip + # header + # - Some servers won't notice anything at all and + # will take a file that's already been compressed + # and compress it again and set the + # Content-Encoding: gzip header + # + # By setting this not to decode automatically we + # hope to eliminate problems with the second case. + decode_content=False, + ): + yield chunk + except AttributeError: + # Standard file-like object. + while True: + chunk = response.raw.read(chunk_size) + if not chunk: + break + yield chunk diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/network/xmlrpc.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/network/xmlrpc.py new file mode 100644 index 0000000000..504018f28f --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/network/xmlrpc.py @@ -0,0 +1,53 @@ +"""xmlrpclib.Transport implementation +""" + +import logging + +# NOTE: XMLRPC Client is not annotated in typeshed as on 2017-07-17, which is +# why we ignore the type on this import +from pip._vendor.six.moves import xmlrpc_client # type: ignore +from pip._vendor.six.moves.urllib import parse as urllib_parse + +from pip._internal.exceptions import NetworkConnectionError +from pip._internal.network.utils import raise_for_status +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Dict + + from pip._internal.network.session import PipSession + + +logger = logging.getLogger(__name__) + + +class PipXmlrpcTransport(xmlrpc_client.Transport): + """Provide a `xmlrpclib.Transport` implementation via a `PipSession` + object. + """ + + def __init__(self, index_url, session, use_datetime=False): + # type: (str, PipSession, bool) -> None + xmlrpc_client.Transport.__init__(self, use_datetime) + index_parts = urllib_parse.urlparse(index_url) + self._scheme = index_parts.scheme + self._session = session + + def request(self, host, handler, request_body, verbose=False): + # type: (str, str, Dict[str, str], bool) -> None + parts = (self._scheme, host, handler, None, None, None) + url = urllib_parse.urlunparse(parts) + try: + headers = {'Content-Type': 'text/xml'} + response = self._session.post(url, data=request_body, + headers=headers, stream=True) + raise_for_status(response) + self.verbose = verbose + return self.parse_response(response.raw) + except NetworkConnectionError as exc: + assert exc.response + logger.critical( + "HTTP error %s while getting %s", + exc.response.status_code, url, + ) + raise diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/operations/__init__.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/operations/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/operations/check.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/operations/check.py new file mode 100644 index 0000000000..5dee6bcb40 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/operations/check.py @@ -0,0 +1,155 @@ +"""Validation of dependencies of packages +""" + +import logging +from collections import namedtuple + +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.pkg_resources import RequirementParseError + +from pip._internal.distributions import make_distribution_for_install_requirement +from pip._internal.utils.misc import get_installed_distributions +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +logger = logging.getLogger(__name__) + +if MYPY_CHECK_RUNNING: + from typing import Any, Callable, Dict, List, Optional, Set, Tuple + + from pip._internal.req.req_install import InstallRequirement + + # Shorthands + PackageSet = Dict[str, 'PackageDetails'] + Missing = Tuple[str, Any] + Conflicting = Tuple[str, str, Any] + + MissingDict = Dict[str, List[Missing]] + ConflictingDict = Dict[str, List[Conflicting]] + CheckResult = Tuple[MissingDict, ConflictingDict] + ConflictDetails = Tuple[PackageSet, CheckResult] + +PackageDetails = namedtuple('PackageDetails', ['version', 'requires']) + + +def create_package_set_from_installed(**kwargs): + # type: (**Any) -> Tuple[PackageSet, bool] + """Converts a list of distributions into a PackageSet. + """ + # Default to using all packages installed on the system + if kwargs == {}: + kwargs = {"local_only": False, "skip": ()} + + package_set = {} + problems = False + for dist in get_installed_distributions(**kwargs): + name = canonicalize_name(dist.project_name) + try: + package_set[name] = PackageDetails(dist.version, dist.requires()) + except (OSError, RequirementParseError) as e: + # Don't crash on unreadable or broken metadata + logger.warning("Error parsing requirements for %s: %s", name, e) + problems = True + return package_set, problems + + +def check_package_set(package_set, should_ignore=None): + # type: (PackageSet, Optional[Callable[[str], bool]]) -> CheckResult + """Check if a package set is consistent + + If should_ignore is passed, it should be a callable that takes a + package name and returns a boolean. + """ + + missing = {} + conflicting = {} + + for package_name in package_set: + # Info about dependencies of package_name + missing_deps = set() # type: Set[Missing] + conflicting_deps = set() # type: Set[Conflicting] + + if should_ignore and should_ignore(package_name): + continue + + for req in package_set[package_name].requires: + name = canonicalize_name(req.project_name) # type: str + + # Check if it's missing + if name not in package_set: + missed = True + if req.marker is not None: + missed = req.marker.evaluate() + if missed: + missing_deps.add((name, req)) + continue + + # Check if there's a conflict + version = package_set[name].version # type: str + if not req.specifier.contains(version, prereleases=True): + conflicting_deps.add((name, version, req)) + + if missing_deps: + missing[package_name] = sorted(missing_deps, key=str) + if conflicting_deps: + conflicting[package_name] = sorted(conflicting_deps, key=str) + + return missing, conflicting + + +def check_install_conflicts(to_install): + # type: (List[InstallRequirement]) -> ConflictDetails + """For checking if the dependency graph would be consistent after \ + installing given requirements + """ + # Start from the current state + package_set, _ = create_package_set_from_installed() + # Install packages + would_be_installed = _simulate_installation_of(to_install, package_set) + + # Only warn about directly-dependent packages; create a whitelist of them + whitelist = _create_whitelist(would_be_installed, package_set) + + return ( + package_set, + check_package_set( + package_set, should_ignore=lambda name: name not in whitelist + ) + ) + + +def _simulate_installation_of(to_install, package_set): + # type: (List[InstallRequirement], PackageSet) -> Set[str] + """Computes the version of packages after installing to_install. + """ + + # Keep track of packages that were installed + installed = set() + + # Modify it as installing requirement_set would (assuming no errors) + for inst_req in to_install: + abstract_dist = make_distribution_for_install_requirement(inst_req) + dist = abstract_dist.get_pkg_resources_distribution() + + assert dist is not None + name = canonicalize_name(dist.key) + package_set[name] = PackageDetails(dist.version, dist.requires()) + + installed.add(name) + + return installed + + +def _create_whitelist(would_be_installed, package_set): + # type: (Set[str], PackageSet) -> Set[str] + packages_affected = set(would_be_installed) + + for package_name in package_set: + if package_name in packages_affected: + continue + + for req in package_set[package_name].requires: + if canonicalize_name(req.name) in packages_affected: + packages_affected.add(package_name) + break + + return packages_affected diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/operations/freeze.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/operations/freeze.py new file mode 100644 index 0000000000..d4f790cd44 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/operations/freeze.py @@ -0,0 +1,277 @@ +from __future__ import absolute_import + +import collections +import logging +import os + +from pip._vendor import six +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.pkg_resources import RequirementParseError + +from pip._internal.exceptions import BadCommand, InstallationError +from pip._internal.req.constructors import ( + install_req_from_editable, + install_req_from_line, +) +from pip._internal.req.req_file import COMMENT_RE +from pip._internal.utils.direct_url_helpers import ( + direct_url_as_pep440_direct_reference, + dist_get_direct_url, +) +from pip._internal.utils.misc import dist_is_editable, get_installed_distributions +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import ( + Container, + Dict, + Iterable, + Iterator, + List, + Optional, + Set, + Tuple, + Union, + ) + + from pip._vendor.pkg_resources import Distribution, Requirement + + from pip._internal.cache import WheelCache + + RequirementInfo = Tuple[Optional[Union[str, Requirement]], bool, List[str]] + + +logger = logging.getLogger(__name__) + + +def freeze( + requirement=None, # type: Optional[List[str]] + find_links=None, # type: Optional[List[str]] + local_only=False, # type: bool + user_only=False, # type: bool + paths=None, # type: Optional[List[str]] + isolated=False, # type: bool + wheel_cache=None, # type: Optional[WheelCache] + exclude_editable=False, # type: bool + skip=() # type: Container[str] +): + # type: (...) -> Iterator[str] + find_links = find_links or [] + + for link in find_links: + yield '-f {}'.format(link) + installations = {} # type: Dict[str, FrozenRequirement] + + for dist in get_installed_distributions( + local_only=local_only, + skip=(), + user_only=user_only, + paths=paths + ): + try: + req = FrozenRequirement.from_dist(dist) + except RequirementParseError as exc: + # We include dist rather than dist.project_name because the + # dist string includes more information, like the version and + # location. We also include the exception message to aid + # troubleshooting. + logger.warning( + 'Could not generate requirement for distribution %r: %s', + dist, exc + ) + continue + if exclude_editable and req.editable: + continue + installations[req.canonical_name] = req + + if requirement: + # the options that don't get turned into an InstallRequirement + # should only be emitted once, even if the same option is in multiple + # requirements files, so we need to keep track of what has been emitted + # so that we don't emit it again if it's seen again + emitted_options = set() # type: Set[str] + # keep track of which files a requirement is in so that we can + # give an accurate warning if a requirement appears multiple times. + req_files = collections.defaultdict(list) # type: Dict[str, List[str]] + for req_file_path in requirement: + with open(req_file_path) as req_file: + for line in req_file: + if (not line.strip() or + line.strip().startswith('#') or + line.startswith(( + '-r', '--requirement', + '-f', '--find-links', + '-i', '--index-url', + '--pre', + '--trusted-host', + '--process-dependency-links', + '--extra-index-url', + '--use-feature'))): + line = line.rstrip() + if line not in emitted_options: + emitted_options.add(line) + yield line + continue + + if line.startswith('-e') or line.startswith('--editable'): + if line.startswith('-e'): + line = line[2:].strip() + else: + line = line[len('--editable'):].strip().lstrip('=') + line_req = install_req_from_editable( + line, + isolated=isolated, + ) + else: + line_req = install_req_from_line( + COMMENT_RE.sub('', line).strip(), + isolated=isolated, + ) + + if not line_req.name: + logger.info( + "Skipping line in requirement file [%s] because " + "it's not clear what it would install: %s", + req_file_path, line.strip(), + ) + logger.info( + " (add #egg=PackageName to the URL to avoid" + " this warning)" + ) + else: + line_req_canonical_name = canonicalize_name( + line_req.name) + if line_req_canonical_name not in installations: + # either it's not installed, or it is installed + # but has been processed already + if not req_files[line_req.name]: + logger.warning( + "Requirement file [%s] contains %s, but " + "package %r is not installed", + req_file_path, + COMMENT_RE.sub('', line).strip(), + line_req.name + ) + else: + req_files[line_req.name].append(req_file_path) + else: + yield str(installations[ + line_req_canonical_name]).rstrip() + del installations[line_req_canonical_name] + req_files[line_req.name].append(req_file_path) + + # Warn about requirements that were included multiple times (in a + # single requirements file or in different requirements files). + for name, files in six.iteritems(req_files): + if len(files) > 1: + logger.warning("Requirement %s included multiple times [%s]", + name, ', '.join(sorted(set(files)))) + + yield( + '## The following requirements were added by ' + 'pip freeze:' + ) + for installation in sorted( + installations.values(), key=lambda x: x.name.lower()): + if installation.canonical_name not in skip: + yield str(installation).rstrip() + + +def get_requirement_info(dist): + # type: (Distribution) -> RequirementInfo + """ + Compute and return values (req, editable, comments) for use in + FrozenRequirement.from_dist(). + """ + if not dist_is_editable(dist): + return (None, False, []) + + location = os.path.normcase(os.path.abspath(dist.location)) + + from pip._internal.vcs import RemoteNotFoundError, vcs + vcs_backend = vcs.get_backend_for_dir(location) + + if vcs_backend is None: + req = dist.as_requirement() + logger.debug( + 'No VCS found for editable requirement "%s" in: %r', req, + location, + ) + comments = [ + '# Editable install with no version control ({})'.format(req) + ] + return (location, True, comments) + + try: + req = vcs_backend.get_src_requirement(location, dist.project_name) + except RemoteNotFoundError: + req = dist.as_requirement() + comments = [ + '# Editable {} install with no remote ({})'.format( + type(vcs_backend).__name__, req, + ) + ] + return (location, True, comments) + + except BadCommand: + logger.warning( + 'cannot determine version of editable source in %s ' + '(%s command not found in path)', + location, + vcs_backend.name, + ) + return (None, True, []) + + except InstallationError as exc: + logger.warning( + "Error when trying to get requirement for VCS system %s, " + "falling back to uneditable format", exc + ) + else: + if req is not None: + return (req, True, []) + + logger.warning( + 'Could not determine repository location of %s', location + ) + comments = ['## !! Could not determine repository location'] + + return (None, False, comments) + + +class FrozenRequirement(object): + def __init__(self, name, req, editable, comments=()): + # type: (str, Union[str, Requirement], bool, Iterable[str]) -> None + self.name = name + self.canonical_name = canonicalize_name(name) + self.req = req + self.editable = editable + self.comments = comments + + @classmethod + def from_dist(cls, dist): + # type: (Distribution) -> FrozenRequirement + # TODO `get_requirement_info` is taking care of editable requirements. + # TODO This should be refactored when we will add detection of + # editable that provide .dist-info metadata. + req, editable, comments = get_requirement_info(dist) + if req is None and not editable: + # if PEP 610 metadata is present, attempt to use it + direct_url = dist_get_direct_url(dist) + if direct_url: + req = direct_url_as_pep440_direct_reference( + direct_url, dist.project_name + ) + comments = [] + if req is None: + # name==version requirement + req = dist.as_requirement() + + return cls(dist.project_name, req, editable, comments=comments) + + def __str__(self): + # type: () -> str + req = self.req + if self.editable: + req = '-e {}'.format(req) + return '\n'.join(list(self.comments) + [str(req)]) + '\n' diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/operations/install/__init__.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/operations/install/__init__.py new file mode 100644 index 0000000000..24d6a5dd31 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/operations/install/__init__.py @@ -0,0 +1,2 @@ +"""For modules related to installing packages. +""" diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/operations/install/editable_legacy.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/operations/install/editable_legacy.py new file mode 100644 index 0000000000..a668a61dc6 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/operations/install/editable_legacy.py @@ -0,0 +1,52 @@ +"""Legacy editable installation process, i.e. `setup.py develop`. +""" +import logging + +from pip._internal.utils.logging import indent_log +from pip._internal.utils.setuptools_build import make_setuptools_develop_args +from pip._internal.utils.subprocess import call_subprocess +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List, Optional, Sequence + + from pip._internal.build_env import BuildEnvironment + + +logger = logging.getLogger(__name__) + + +def install_editable( + install_options, # type: List[str] + global_options, # type: Sequence[str] + prefix, # type: Optional[str] + home, # type: Optional[str] + use_user_site, # type: bool + name, # type: str + setup_py_path, # type: str + isolated, # type: bool + build_env, # type: BuildEnvironment + unpacked_source_directory, # type: str +): + # type: (...) -> None + """Install a package in editable mode. Most arguments are pass-through + to setuptools. + """ + logger.info('Running setup.py develop for %s', name) + + args = make_setuptools_develop_args( + setup_py_path, + global_options=global_options, + install_options=install_options, + no_user_config=isolated, + prefix=prefix, + home=home, + use_user_site=use_user_site, + ) + + with indent_log(): + with build_env: + call_subprocess( + args, + cwd=unpacked_source_directory, + ) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/operations/install/legacy.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/operations/install/legacy.py new file mode 100644 index 0000000000..87227d5fed --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/operations/install/legacy.py @@ -0,0 +1,130 @@ +"""Legacy installation process, i.e. `setup.py install`. +""" + +import logging +import os +import sys +from distutils.util import change_root + +from pip._internal.exceptions import InstallationError +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import ensure_dir +from pip._internal.utils.setuptools_build import make_setuptools_install_args +from pip._internal.utils.subprocess import runner_with_spinner_message +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List, Optional, Sequence + + from pip._internal.build_env import BuildEnvironment + from pip._internal.models.scheme import Scheme + + +logger = logging.getLogger(__name__) + + +class LegacyInstallFailure(Exception): + def __init__(self): + # type: () -> None + self.parent = sys.exc_info() + + +def install( + install_options, # type: List[str] + global_options, # type: Sequence[str] + root, # type: Optional[str] + home, # type: Optional[str] + prefix, # type: Optional[str] + use_user_site, # type: bool + pycompile, # type: bool + scheme, # type: Scheme + setup_py_path, # type: str + isolated, # type: bool + req_name, # type: str + build_env, # type: BuildEnvironment + unpacked_source_directory, # type: str + req_description, # type: str +): + # type: (...) -> bool + + header_dir = scheme.headers + + with TempDirectory(kind="record") as temp_dir: + try: + record_filename = os.path.join(temp_dir.path, 'install-record.txt') + install_args = make_setuptools_install_args( + setup_py_path, + global_options=global_options, + install_options=install_options, + record_filename=record_filename, + root=root, + prefix=prefix, + header_dir=header_dir, + home=home, + use_user_site=use_user_site, + no_user_config=isolated, + pycompile=pycompile, + ) + + runner = runner_with_spinner_message( + "Running setup.py install for {}".format(req_name) + ) + with indent_log(), build_env: + runner( + cmd=install_args, + cwd=unpacked_source_directory, + ) + + if not os.path.exists(record_filename): + logger.debug('Record file %s not found', record_filename) + # Signal to the caller that we didn't install the new package + return False + + except Exception: + # Signal to the caller that we didn't install the new package + raise LegacyInstallFailure + + # At this point, we have successfully installed the requirement. + + # We intentionally do not use any encoding to read the file because + # setuptools writes the file using distutils.file_util.write_file, + # which does not specify an encoding. + with open(record_filename) as f: + record_lines = f.read().splitlines() + + def prepend_root(path): + # type: (str) -> str + if root is None or not os.path.isabs(path): + return path + else: + return change_root(root, path) + + for line in record_lines: + directory = os.path.dirname(line) + if directory.endswith('.egg-info'): + egg_info_dir = prepend_root(directory) + break + else: + message = ( + "{} did not indicate that it installed an " + ".egg-info directory. Only setup.py projects " + "generating .egg-info directories are supported." + ).format(req_description) + raise InstallationError(message) + + new_lines = [] + for line in record_lines: + filename = line.strip() + if os.path.isdir(filename): + filename += os.path.sep + new_lines.append( + os.path.relpath(prepend_root(filename), egg_info_dir) + ) + new_lines.sort() + ensure_dir(egg_info_dir) + inst_files_path = os.path.join(egg_info_dir, 'installed-files.txt') + with open(inst_files_path, 'w') as f: + f.write('\n'.join(new_lines) + '\n') + + return True diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/operations/install/wheel.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/operations/install/wheel.py new file mode 100644 index 0000000000..8b67ebb943 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/operations/install/wheel.py @@ -0,0 +1,846 @@ +"""Support for installing and building the "wheel" binary package format. +""" + +from __future__ import absolute_import + +import collections +import compileall +import contextlib +import csv +import importlib +import logging +import os.path +import re +import shutil +import sys +import warnings +from base64 import urlsafe_b64encode +from itertools import chain, starmap +from zipfile import ZipFile + +from pip._vendor import pkg_resources +from pip._vendor.distlib.scripts import ScriptMaker +from pip._vendor.distlib.util import get_export_entry +from pip._vendor.six import PY2, ensure_str, ensure_text, itervalues, reraise, text_type +from pip._vendor.six.moves import filterfalse, map + +from pip._internal.exceptions import InstallationError +from pip._internal.locations import get_major_minor_version +from pip._internal.models.direct_url import DIRECT_URL_METADATA_NAME, DirectUrl +from pip._internal.models.scheme import SCHEME_KEYS +from pip._internal.utils.filesystem import adjacent_tmp_file, replace +from pip._internal.utils.misc import captured_stdout, ensure_dir, hash_file, partition +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.unpacking import ( + current_umask, + is_within_directory, + set_extracted_file_to_default_mode_plus_executable, + zip_item_is_executable, +) +from pip._internal.utils.wheel import parse_wheel, pkg_resources_distribution_for_wheel + +# Use the custom cast function at runtime to make cast work, +# and import typing.cast when performing pre-commit and type +# checks +if not MYPY_CHECK_RUNNING: + from pip._internal.utils.typing import cast +else: + from email.message import Message + from typing import ( + IO, + Any, + Callable, + Dict, + Iterable, + Iterator, + List, + NewType, + Optional, + Protocol, + Sequence, + Set, + Tuple, + Union, + cast, + ) + from zipfile import ZipInfo + + from pip._vendor.pkg_resources import Distribution + + from pip._internal.models.scheme import Scheme + from pip._internal.utils.filesystem import NamedTemporaryFileResult + + RecordPath = NewType('RecordPath', text_type) + InstalledCSVRow = Tuple[RecordPath, str, Union[int, str]] + + class File(Protocol): + src_record_path = None # type: RecordPath + dest_path = None # type: text_type + changed = None # type: bool + + def save(self): + # type: () -> None + pass + + +logger = logging.getLogger(__name__) + + +def rehash(path, blocksize=1 << 20): + # type: (text_type, int) -> Tuple[str, str] + """Return (encoded_digest, length) for path using hashlib.sha256()""" + h, length = hash_file(path, blocksize) + digest = 'sha256=' + urlsafe_b64encode( + h.digest() + ).decode('latin1').rstrip('=') + # unicode/str python2 issues + return (digest, str(length)) # type: ignore + + +def csv_io_kwargs(mode): + # type: (str) -> Dict[str, Any] + """Return keyword arguments to properly open a CSV file + in the given mode. + """ + if PY2: + return {'mode': '{}b'.format(mode)} + else: + return {'mode': mode, 'newline': '', 'encoding': 'utf-8'} + + +def fix_script(path): + # type: (text_type) -> bool + """Replace #!python with #!/path/to/python + Return True if file was changed. + """ + # XXX RECORD hashes will need to be updated + assert os.path.isfile(path) + + with open(path, 'rb') as script: + firstline = script.readline() + if not firstline.startswith(b'#!python'): + return False + exename = sys.executable.encode(sys.getfilesystemencoding()) + firstline = b'#!' + exename + os.linesep.encode("ascii") + rest = script.read() + with open(path, 'wb') as script: + script.write(firstline) + script.write(rest) + return True + + +def wheel_root_is_purelib(metadata): + # type: (Message) -> bool + return metadata.get("Root-Is-Purelib", "").lower() == "true" + + +def get_entrypoints(distribution): + # type: (Distribution) -> Tuple[Dict[str, str], Dict[str, str]] + # get the entry points and then the script names + try: + console = distribution.get_entry_map('console_scripts') + gui = distribution.get_entry_map('gui_scripts') + except KeyError: + # Our dict-based Distribution raises KeyError if entry_points.txt + # doesn't exist. + return {}, {} + + def _split_ep(s): + # type: (pkg_resources.EntryPoint) -> Tuple[str, str] + """get the string representation of EntryPoint, + remove space and split on '=' + """ + split_parts = str(s).replace(" ", "").split("=") + return split_parts[0], split_parts[1] + + # convert the EntryPoint objects into strings with module:function + console = dict(_split_ep(v) for v in console.values()) + gui = dict(_split_ep(v) for v in gui.values()) + return console, gui + + +def message_about_scripts_not_on_PATH(scripts): + # type: (Sequence[str]) -> Optional[str] + """Determine if any scripts are not on PATH and format a warning. + Returns a warning message if one or more scripts are not on PATH, + otherwise None. + """ + if not scripts: + return None + + # Group scripts by the path they were installed in + grouped_by_dir = collections.defaultdict(set) # type: Dict[str, Set[str]] + for destfile in scripts: + parent_dir = os.path.dirname(destfile) + script_name = os.path.basename(destfile) + grouped_by_dir[parent_dir].add(script_name) + + # We don't want to warn for directories that are on PATH. + not_warn_dirs = [ + os.path.normcase(i).rstrip(os.sep) for i in + os.environ.get("PATH", "").split(os.pathsep) + ] + # If an executable sits with sys.executable, we don't warn for it. + # This covers the case of venv invocations without activating the venv. + not_warn_dirs.append(os.path.normcase(os.path.dirname(sys.executable))) + warn_for = { + parent_dir: scripts for parent_dir, scripts in grouped_by_dir.items() + if os.path.normcase(parent_dir) not in not_warn_dirs + } # type: Dict[str, Set[str]] + if not warn_for: + return None + + # Format a message + msg_lines = [] + for parent_dir, dir_scripts in warn_for.items(): + sorted_scripts = sorted(dir_scripts) # type: List[str] + if len(sorted_scripts) == 1: + start_text = "script {} is".format(sorted_scripts[0]) + else: + start_text = "scripts {} are".format( + ", ".join(sorted_scripts[:-1]) + " and " + sorted_scripts[-1] + ) + + msg_lines.append( + "The {} installed in '{}' which is not on PATH." + .format(start_text, parent_dir) + ) + + last_line_fmt = ( + "Consider adding {} to PATH or, if you prefer " + "to suppress this warning, use --no-warn-script-location." + ) + if len(msg_lines) == 1: + msg_lines.append(last_line_fmt.format("this directory")) + else: + msg_lines.append(last_line_fmt.format("these directories")) + + # Add a note if any directory starts with ~ + warn_for_tilde = any( + i[0] == "~" for i in os.environ.get("PATH", "").split(os.pathsep) if i + ) + if warn_for_tilde: + tilde_warning_msg = ( + "NOTE: The current PATH contains path(s) starting with `~`, " + "which may not be expanded by all applications." + ) + msg_lines.append(tilde_warning_msg) + + # Returns the formatted multiline message + return "\n".join(msg_lines) + + +def _normalized_outrows(outrows): + # type: (Iterable[InstalledCSVRow]) -> List[Tuple[str, str, str]] + """Normalize the given rows of a RECORD file. + + Items in each row are converted into str. Rows are then sorted to make + the value more predictable for tests. + + Each row is a 3-tuple (path, hash, size) and corresponds to a record of + a RECORD file (see PEP 376 and PEP 427 for details). For the rows + passed to this function, the size can be an integer as an int or string, + or the empty string. + """ + # Normally, there should only be one row per path, in which case the + # second and third elements don't come into play when sorting. + # However, in cases in the wild where a path might happen to occur twice, + # we don't want the sort operation to trigger an error (but still want + # determinism). Since the third element can be an int or string, we + # coerce each element to a string to avoid a TypeError in this case. + # For additional background, see-- + # https://github.com/pypa/pip/issues/5868 + return sorted( + (ensure_str(record_path, encoding='utf-8'), hash_, str(size)) + for record_path, hash_, size in outrows + ) + + +def _record_to_fs_path(record_path): + # type: (RecordPath) -> text_type + return record_path + + +def _fs_to_record_path(path, relative_to=None): + # type: (text_type, Optional[text_type]) -> RecordPath + if relative_to is not None: + # On Windows, do not handle relative paths if they belong to different + # logical disks + if os.path.splitdrive(path)[0].lower() == \ + os.path.splitdrive(relative_to)[0].lower(): + path = os.path.relpath(path, relative_to) + path = path.replace(os.path.sep, '/') + return cast('RecordPath', path) + + +def _parse_record_path(record_column): + # type: (str) -> RecordPath + p = ensure_text(record_column, encoding='utf-8') + return cast('RecordPath', p) + + +def get_csv_rows_for_installed( + old_csv_rows, # type: List[List[str]] + installed, # type: Dict[RecordPath, RecordPath] + changed, # type: Set[RecordPath] + generated, # type: List[str] + lib_dir, # type: str +): + # type: (...) -> List[InstalledCSVRow] + """ + :param installed: A map from archive RECORD path to installation RECORD + path. + """ + installed_rows = [] # type: List[InstalledCSVRow] + for row in old_csv_rows: + if len(row) > 3: + logger.warning('RECORD line has more than three elements: %s', row) + old_record_path = _parse_record_path(row[0]) + new_record_path = installed.pop(old_record_path, old_record_path) + if new_record_path in changed: + digest, length = rehash(_record_to_fs_path(new_record_path)) + else: + digest = row[1] if len(row) > 1 else '' + length = row[2] if len(row) > 2 else '' + installed_rows.append((new_record_path, digest, length)) + for f in generated: + path = _fs_to_record_path(f, lib_dir) + digest, length = rehash(f) + installed_rows.append((path, digest, length)) + for installed_record_path in itervalues(installed): + installed_rows.append((installed_record_path, '', '')) + return installed_rows + + +def get_console_script_specs(console): + # type: (Dict[str, str]) -> List[str] + """ + Given the mapping from entrypoint name to callable, return the relevant + console script specs. + """ + # Don't mutate caller's version + console = console.copy() + + scripts_to_generate = [] + + # Special case pip and setuptools to generate versioned wrappers + # + # The issue is that some projects (specifically, pip and setuptools) use + # code in setup.py to create "versioned" entry points - pip2.7 on Python + # 2.7, pip3.3 on Python 3.3, etc. But these entry points are baked into + # the wheel metadata at build time, and so if the wheel is installed with + # a *different* version of Python the entry points will be wrong. The + # correct fix for this is to enhance the metadata to be able to describe + # such versioned entry points, but that won't happen till Metadata 2.0 is + # available. + # In the meantime, projects using versioned entry points will either have + # incorrect versioned entry points, or they will not be able to distribute + # "universal" wheels (i.e., they will need a wheel per Python version). + # + # Because setuptools and pip are bundled with _ensurepip and virtualenv, + # we need to use universal wheels. So, as a stopgap until Metadata 2.0, we + # override the versioned entry points in the wheel and generate the + # correct ones. This code is purely a short-term measure until Metadata 2.0 + # is available. + # + # To add the level of hack in this section of code, in order to support + # ensurepip this code will look for an ``ENSUREPIP_OPTIONS`` environment + # variable which will control which version scripts get installed. + # + # ENSUREPIP_OPTIONS=altinstall + # - Only pipX.Y and easy_install-X.Y will be generated and installed + # ENSUREPIP_OPTIONS=install + # - pipX.Y, pipX, easy_install-X.Y will be generated and installed. Note + # that this option is technically if ENSUREPIP_OPTIONS is set and is + # not altinstall + # DEFAULT + # - The default behavior is to install pip, pipX, pipX.Y, easy_install + # and easy_install-X.Y. + pip_script = console.pop('pip', None) + if pip_script: + if "ENSUREPIP_OPTIONS" not in os.environ: + scripts_to_generate.append('pip = ' + pip_script) + + if os.environ.get("ENSUREPIP_OPTIONS", "") != "altinstall": + scripts_to_generate.append( + 'pip{} = {}'.format(sys.version_info[0], pip_script) + ) + + scripts_to_generate.append( + 'pip{} = {}'.format(get_major_minor_version(), pip_script) + ) + # Delete any other versioned pip entry points + pip_ep = [k for k in console if re.match(r'pip(\d(\.\d)?)?$', k)] + for k in pip_ep: + del console[k] + easy_install_script = console.pop('easy_install', None) + if easy_install_script: + if "ENSUREPIP_OPTIONS" not in os.environ: + scripts_to_generate.append( + 'easy_install = ' + easy_install_script + ) + + scripts_to_generate.append( + 'easy_install-{} = {}'.format( + get_major_minor_version(), easy_install_script + ) + ) + # Delete any other versioned easy_install entry points + easy_install_ep = [ + k for k in console if re.match(r'easy_install(-\d\.\d)?$', k) + ] + for k in easy_install_ep: + del console[k] + + # Generate the console entry points specified in the wheel + scripts_to_generate.extend(starmap('{} = {}'.format, console.items())) + + return scripts_to_generate + + +class ZipBackedFile(object): + def __init__(self, src_record_path, dest_path, zip_file): + # type: (RecordPath, text_type, ZipFile) -> None + self.src_record_path = src_record_path + self.dest_path = dest_path + self._zip_file = zip_file + self.changed = False + + def _getinfo(self): + # type: () -> ZipInfo + if not PY2: + return self._zip_file.getinfo(self.src_record_path) + # Python 2 does not expose a way to detect a ZIP's encoding, but the + # wheel specification (PEP 427) explicitly mandates that paths should + # use UTF-8, so we assume it is true. + return self._zip_file.getinfo(self.src_record_path.encode("utf-8")) + + def save(self): + # type: () -> None + # directory creation is lazy and after file filtering + # to ensure we don't install empty dirs; empty dirs can't be + # uninstalled. + parent_dir = os.path.dirname(self.dest_path) + ensure_dir(parent_dir) + + # When we open the output file below, any existing file is truncated + # before we start writing the new contents. This is fine in most + # cases, but can cause a segfault if pip has loaded a shared + # object (e.g. from pyopenssl through its vendored urllib3) + # Since the shared object is mmap'd an attempt to call a + # symbol in it will then cause a segfault. Unlinking the file + # allows writing of new contents while allowing the process to + # continue to use the old copy. + if os.path.exists(self.dest_path): + os.unlink(self.dest_path) + + zipinfo = self._getinfo() + + with self._zip_file.open(zipinfo) as f: + with open(self.dest_path, "wb") as dest: + shutil.copyfileobj(f, dest) + + if zip_item_is_executable(zipinfo): + set_extracted_file_to_default_mode_plus_executable(self.dest_path) + + +class ScriptFile(object): + def __init__(self, file): + # type: (File) -> None + self._file = file + self.src_record_path = self._file.src_record_path + self.dest_path = self._file.dest_path + self.changed = False + + def save(self): + # type: () -> None + self._file.save() + self.changed = fix_script(self.dest_path) + + +class MissingCallableSuffix(InstallationError): + def __init__(self, entry_point): + # type: (str) -> None + super(MissingCallableSuffix, self).__init__( + "Invalid script entry point: {} - A callable " + "suffix is required. Cf https://packaging.python.org/" + "specifications/entry-points/#use-for-scripts for more " + "information.".format(entry_point) + ) + + +def _raise_for_invalid_entrypoint(specification): + # type: (str) -> None + entry = get_export_entry(specification) + if entry is not None and entry.suffix is None: + raise MissingCallableSuffix(str(entry)) + + +class PipScriptMaker(ScriptMaker): + def make(self, specification, options=None): + # type: (str, Dict[str, Any]) -> List[str] + _raise_for_invalid_entrypoint(specification) + return super(PipScriptMaker, self).make(specification, options) + + +def _install_wheel( + name, # type: str + wheel_zip, # type: ZipFile + wheel_path, # type: str + scheme, # type: Scheme + pycompile=True, # type: bool + warn_script_location=True, # type: bool + direct_url=None, # type: Optional[DirectUrl] + requested=False, # type: bool +): + # type: (...) -> None + """Install a wheel. + + :param name: Name of the project to install + :param wheel_zip: open ZipFile for wheel being installed + :param scheme: Distutils scheme dictating the install directories + :param req_description: String used in place of the requirement, for + logging + :param pycompile: Whether to byte-compile installed Python files + :param warn_script_location: Whether to check that scripts are installed + into a directory on PATH + :raises UnsupportedWheel: + * when the directory holds an unpacked wheel with incompatible + Wheel-Version + * when the .dist-info dir does not match the wheel + """ + info_dir, metadata = parse_wheel(wheel_zip, name) + + if wheel_root_is_purelib(metadata): + lib_dir = scheme.purelib + else: + lib_dir = scheme.platlib + + # Record details of the files moved + # installed = files copied from the wheel to the destination + # changed = files changed while installing (scripts #! line typically) + # generated = files newly generated during the install (script wrappers) + installed = {} # type: Dict[RecordPath, RecordPath] + changed = set() # type: Set[RecordPath] + generated = [] # type: List[str] + + def record_installed(srcfile, destfile, modified=False): + # type: (RecordPath, text_type, bool) -> None + """Map archive RECORD paths to installation RECORD paths.""" + newpath = _fs_to_record_path(destfile, lib_dir) + installed[srcfile] = newpath + if modified: + changed.add(_fs_to_record_path(destfile)) + + def all_paths(): + # type: () -> Iterable[RecordPath] + names = wheel_zip.namelist() + # If a flag is set, names may be unicode in Python 2. We convert to + # text explicitly so these are valid for lookup in RECORD. + decoded_names = map(ensure_text, names) + for name in decoded_names: + yield cast("RecordPath", name) + + def is_dir_path(path): + # type: (RecordPath) -> bool + return path.endswith("/") + + def assert_no_path_traversal(dest_dir_path, target_path): + # type: (text_type, text_type) -> None + if not is_within_directory(dest_dir_path, target_path): + message = ( + "The wheel {!r} has a file {!r} trying to install" + " outside the target directory {!r}" + ) + raise InstallationError( + message.format(wheel_path, target_path, dest_dir_path) + ) + + def root_scheme_file_maker(zip_file, dest): + # type: (ZipFile, text_type) -> Callable[[RecordPath], File] + def make_root_scheme_file(record_path): + # type: (RecordPath) -> File + normed_path = os.path.normpath(record_path) + dest_path = os.path.join(dest, normed_path) + assert_no_path_traversal(dest, dest_path) + return ZipBackedFile(record_path, dest_path, zip_file) + + return make_root_scheme_file + + def data_scheme_file_maker(zip_file, scheme): + # type: (ZipFile, Scheme) -> Callable[[RecordPath], File] + scheme_paths = {} + for key in SCHEME_KEYS: + encoded_key = ensure_text(key) + scheme_paths[encoded_key] = ensure_text( + getattr(scheme, key), encoding=sys.getfilesystemencoding() + ) + + def make_data_scheme_file(record_path): + # type: (RecordPath) -> File + normed_path = os.path.normpath(record_path) + try: + _, scheme_key, dest_subpath = normed_path.split(os.path.sep, 2) + except ValueError: + message = ( + "Unexpected file in {}: {!r}. .data directory contents" + " should be named like: '/'." + ).format(wheel_path, record_path) + raise InstallationError(message) + + try: + scheme_path = scheme_paths[scheme_key] + except KeyError: + valid_scheme_keys = ", ".join(sorted(scheme_paths)) + message = ( + "Unknown scheme key used in {}: {} (for file {!r}). .data" + " directory contents should be in subdirectories named" + " with a valid scheme key ({})" + ).format( + wheel_path, scheme_key, record_path, valid_scheme_keys + ) + raise InstallationError(message) + + dest_path = os.path.join(scheme_path, dest_subpath) + assert_no_path_traversal(scheme_path, dest_path) + return ZipBackedFile(record_path, dest_path, zip_file) + + return make_data_scheme_file + + def is_data_scheme_path(path): + # type: (RecordPath) -> bool + return path.split("/", 1)[0].endswith(".data") + + paths = all_paths() + file_paths = filterfalse(is_dir_path, paths) + root_scheme_paths, data_scheme_paths = partition( + is_data_scheme_path, file_paths + ) + + make_root_scheme_file = root_scheme_file_maker( + wheel_zip, + ensure_text(lib_dir, encoding=sys.getfilesystemencoding()), + ) + files = map(make_root_scheme_file, root_scheme_paths) + + def is_script_scheme_path(path): + # type: (RecordPath) -> bool + parts = path.split("/", 2) + return ( + len(parts) > 2 and + parts[0].endswith(".data") and + parts[1] == "scripts" + ) + + other_scheme_paths, script_scheme_paths = partition( + is_script_scheme_path, data_scheme_paths + ) + + make_data_scheme_file = data_scheme_file_maker(wheel_zip, scheme) + other_scheme_files = map(make_data_scheme_file, other_scheme_paths) + files = chain(files, other_scheme_files) + + # Get the defined entry points + distribution = pkg_resources_distribution_for_wheel( + wheel_zip, name, wheel_path + ) + console, gui = get_entrypoints(distribution) + + def is_entrypoint_wrapper(file): + # type: (File) -> bool + # EP, EP.exe and EP-script.py are scripts generated for + # entry point EP by setuptools + path = file.dest_path + name = os.path.basename(path) + if name.lower().endswith('.exe'): + matchname = name[:-4] + elif name.lower().endswith('-script.py'): + matchname = name[:-10] + elif name.lower().endswith(".pya"): + matchname = name[:-4] + else: + matchname = name + # Ignore setuptools-generated scripts + return (matchname in console or matchname in gui) + + script_scheme_files = map(make_data_scheme_file, script_scheme_paths) + script_scheme_files = filterfalse( + is_entrypoint_wrapper, script_scheme_files + ) + script_scheme_files = map(ScriptFile, script_scheme_files) + files = chain(files, script_scheme_files) + + for file in files: + file.save() + record_installed(file.src_record_path, file.dest_path, file.changed) + + def pyc_source_file_paths(): + # type: () -> Iterator[text_type] + # We de-duplicate installation paths, since there can be overlap (e.g. + # file in .data maps to same location as file in wheel root). + # Sorting installation paths makes it easier to reproduce and debug + # issues related to permissions on existing files. + for installed_path in sorted(set(installed.values())): + full_installed_path = os.path.join(lib_dir, installed_path) + if not os.path.isfile(full_installed_path): + continue + if not full_installed_path.endswith('.py'): + continue + yield full_installed_path + + def pyc_output_path(path): + # type: (text_type) -> text_type + """Return the path the pyc file would have been written to. + """ + if PY2: + if sys.flags.optimize: + return path + 'o' + else: + return path + 'c' + else: + return importlib.util.cache_from_source(path) + + # Compile all of the pyc files for the installed files + if pycompile: + with captured_stdout() as stdout: + with warnings.catch_warnings(): + warnings.filterwarnings('ignore') + for path in pyc_source_file_paths(): + # Python 2's `compileall.compile_file` requires a str in + # error cases, so we must convert to the native type. + path_arg = ensure_str( + path, encoding=sys.getfilesystemencoding() + ) + success = compileall.compile_file( + path_arg, force=True, quiet=True + ) + if success: + pyc_path = pyc_output_path(path) + assert os.path.exists(pyc_path) + pyc_record_path = cast( + "RecordPath", pyc_path.replace(os.path.sep, "/") + ) + record_installed(pyc_record_path, pyc_path) + logger.debug(stdout.getvalue()) + + maker = PipScriptMaker(None, scheme.scripts) + + # Ensure old scripts are overwritten. + # See https://github.com/pypa/pip/issues/1800 + maker.clobber = True + + # Ensure we don't generate any variants for scripts because this is almost + # never what somebody wants. + # See https://bitbucket.org/pypa/distlib/issue/35/ + maker.variants = {''} + + # This is required because otherwise distlib creates scripts that are not + # executable. + # See https://bitbucket.org/pypa/distlib/issue/32/ + maker.set_mode = True + + # Generate the console and GUI entry points specified in the wheel + scripts_to_generate = get_console_script_specs(console) + + gui_scripts_to_generate = list(starmap('{} = {}'.format, gui.items())) + + generated_console_scripts = maker.make_multiple(scripts_to_generate) + generated.extend(generated_console_scripts) + + generated.extend( + maker.make_multiple(gui_scripts_to_generate, {'gui': True}) + ) + + if warn_script_location: + msg = message_about_scripts_not_on_PATH(generated_console_scripts) + if msg is not None: + logger.warning(msg) + + generated_file_mode = 0o666 & ~current_umask() + + @contextlib.contextmanager + def _generate_file(path, **kwargs): + # type: (str, **Any) -> Iterator[NamedTemporaryFileResult] + with adjacent_tmp_file(path, **kwargs) as f: + yield f + os.chmod(f.name, generated_file_mode) + replace(f.name, path) + + dest_info_dir = os.path.join(lib_dir, info_dir) + + # Record pip as the installer + installer_path = os.path.join(dest_info_dir, 'INSTALLER') + with _generate_file(installer_path) as installer_file: + installer_file.write(b'pip\n') + generated.append(installer_path) + + # Record the PEP 610 direct URL reference + if direct_url is not None: + direct_url_path = os.path.join(dest_info_dir, DIRECT_URL_METADATA_NAME) + with _generate_file(direct_url_path) as direct_url_file: + direct_url_file.write(direct_url.to_json().encode("utf-8")) + generated.append(direct_url_path) + + # Record the REQUESTED file + if requested: + requested_path = os.path.join(dest_info_dir, 'REQUESTED') + with open(requested_path, "w"): + pass + generated.append(requested_path) + + record_text = distribution.get_metadata('RECORD') + record_rows = list(csv.reader(record_text.splitlines())) + + rows = get_csv_rows_for_installed( + record_rows, + installed=installed, + changed=changed, + generated=generated, + lib_dir=lib_dir) + + # Record details of all files installed + record_path = os.path.join(dest_info_dir, 'RECORD') + + with _generate_file(record_path, **csv_io_kwargs('w')) as record_file: + # The type mypy infers for record_file is different for Python 3 + # (typing.IO[Any]) and Python 2 (typing.BinaryIO). We explicitly + # cast to typing.IO[str] as a workaround. + writer = csv.writer(cast('IO[str]', record_file)) + writer.writerows(_normalized_outrows(rows)) + + +@contextlib.contextmanager +def req_error_context(req_description): + # type: (str) -> Iterator[None] + try: + yield + except InstallationError as e: + message = "For req: {}. {}".format(req_description, e.args[0]) + reraise( + InstallationError, InstallationError(message), sys.exc_info()[2] + ) + + +def install_wheel( + name, # type: str + wheel_path, # type: str + scheme, # type: Scheme + req_description, # type: str + pycompile=True, # type: bool + warn_script_location=True, # type: bool + direct_url=None, # type: Optional[DirectUrl] + requested=False, # type: bool +): + # type: (...) -> None + with ZipFile(wheel_path, allowZip64=True) as z: + with req_error_context(req_description): + _install_wheel( + name=name, + wheel_zip=z, + wheel_path=wheel_path, + scheme=scheme, + pycompile=pycompile, + warn_script_location=warn_script_location, + direct_url=direct_url, + requested=requested, + ) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/operations/prepare.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/operations/prepare.py new file mode 100644 index 0000000000..13b2c0beee --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/operations/prepare.py @@ -0,0 +1,608 @@ +"""Prepares a distribution for installation +""" + +# The following comment should be removed at some point in the future. +# mypy: strict-optional=False + +import logging +import mimetypes +import os +import shutil + +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.six import PY2 + +from pip._internal.distributions import make_distribution_for_install_requirement +from pip._internal.distributions.installed import InstalledDistribution +from pip._internal.exceptions import ( + DirectoryUrlHashUnsupported, + HashMismatch, + HashUnpinned, + InstallationError, + NetworkConnectionError, + PreviousBuildDirError, + VcsHashUnsupported, +) +from pip._internal.models.wheel import Wheel +from pip._internal.network.download import BatchDownloader, Downloader +from pip._internal.network.lazy_wheel import ( + HTTPRangeRequestUnsupported, + dist_from_wheel_url, +) +from pip._internal.utils.filesystem import copy2_fixed +from pip._internal.utils.hashes import MissingHashes +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import display_path, hide_url, path_to_display, rmtree +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.unpacking import unpack_file +from pip._internal.vcs import vcs + +if MYPY_CHECK_RUNNING: + from typing import Callable, Dict, Iterable, List, Optional, Tuple + + from mypy_extensions import TypedDict + from pip._vendor.pkg_resources import Distribution + + from pip._internal.index.package_finder import PackageFinder + from pip._internal.models.link import Link + from pip._internal.network.session import PipSession + from pip._internal.req.req_install import InstallRequirement + from pip._internal.req.req_tracker import RequirementTracker + from pip._internal.utils.hashes import Hashes + + if PY2: + CopytreeKwargs = TypedDict( + 'CopytreeKwargs', + { + 'ignore': Callable[[str, List[str]], List[str]], + 'symlinks': bool, + }, + total=False, + ) + else: + CopytreeKwargs = TypedDict( + 'CopytreeKwargs', + { + 'copy_function': Callable[[str, str], None], + 'ignore': Callable[[str, List[str]], List[str]], + 'ignore_dangling_symlinks': bool, + 'symlinks': bool, + }, + total=False, + ) + +logger = logging.getLogger(__name__) + + +def _get_prepared_distribution( + req, # type: InstallRequirement + req_tracker, # type: RequirementTracker + finder, # type: PackageFinder + build_isolation, # type: bool +): + # type: (...) -> Distribution + """Prepare a distribution for installation.""" + abstract_dist = make_distribution_for_install_requirement(req) + with req_tracker.track(req): + abstract_dist.prepare_distribution_metadata(finder, build_isolation) + return abstract_dist.get_pkg_resources_distribution() + + +def unpack_vcs_link(link, location): + # type: (Link, str) -> None + vcs_backend = vcs.get_backend_for_scheme(link.scheme) + assert vcs_backend is not None + vcs_backend.unpack(location, url=hide_url(link.url)) + + +class File(object): + + def __init__(self, path, content_type): + # type: (str, Optional[str]) -> None + self.path = path + if content_type is None: + self.content_type = mimetypes.guess_type(path)[0] + else: + self.content_type = content_type + + +def get_http_url( + link, # type: Link + download, # type: Downloader + download_dir=None, # type: Optional[str] + hashes=None, # type: Optional[Hashes] +): + # type: (...) -> File + temp_dir = TempDirectory(kind="unpack", globally_managed=True) + # If a download dir is specified, is the file already downloaded there? + already_downloaded_path = None + if download_dir: + already_downloaded_path = _check_download_dir( + link, download_dir, hashes + ) + + if already_downloaded_path: + from_path = already_downloaded_path + content_type = None + else: + # let's download to a tmp dir + from_path, content_type = download(link, temp_dir.path) + if hashes: + hashes.check_against_path(from_path) + + return File(from_path, content_type) + + +def _copy2_ignoring_special_files(src, dest): + # type: (str, str) -> None + """Copying special files is not supported, but as a convenience to users + we skip errors copying them. This supports tools that may create e.g. + socket files in the project source directory. + """ + try: + copy2_fixed(src, dest) + except shutil.SpecialFileError as e: + # SpecialFileError may be raised due to either the source or + # destination. If the destination was the cause then we would actually + # care, but since the destination directory is deleted prior to + # copy we ignore all of them assuming it is caused by the source. + logger.warning( + "Ignoring special file error '%s' encountered copying %s to %s.", + str(e), + path_to_display(src), + path_to_display(dest), + ) + + +def _copy_source_tree(source, target): + # type: (str, str) -> None + target_abspath = os.path.abspath(target) + target_basename = os.path.basename(target_abspath) + target_dirname = os.path.dirname(target_abspath) + + def ignore(d, names): + # type: (str, List[str]) -> List[str] + skipped = [] # type: List[str] + if d == source: + # Pulling in those directories can potentially be very slow, + # exclude the following directories if they appear in the top + # level dir (and only it). + # See discussion at https://github.com/pypa/pip/pull/6770 + skipped += ['.tox', '.nox'] + if os.path.abspath(d) == target_dirname: + # Prevent an infinite recursion if the target is in source. + # This can happen when TMPDIR is set to ${PWD}/... + # and we copy PWD to TMPDIR. + skipped += [target_basename] + return skipped + + kwargs = dict(ignore=ignore, symlinks=True) # type: CopytreeKwargs + + if not PY2: + # Python 2 does not support copy_function, so we only ignore + # errors on special file copy in Python 3. + kwargs['copy_function'] = _copy2_ignoring_special_files + + shutil.copytree(source, target, **kwargs) + + +def get_file_url( + link, # type: Link + download_dir=None, # type: Optional[str] + hashes=None # type: Optional[Hashes] +): + # type: (...) -> File + """Get file and optionally check its hash. + """ + # If a download dir is specified, is the file already there and valid? + already_downloaded_path = None + if download_dir: + already_downloaded_path = _check_download_dir( + link, download_dir, hashes + ) + + if already_downloaded_path: + from_path = already_downloaded_path + else: + from_path = link.file_path + + # If --require-hashes is off, `hashes` is either empty, the + # link's embedded hash, or MissingHashes; it is required to + # match. If --require-hashes is on, we are satisfied by any + # hash in `hashes` matching: a URL-based or an option-based + # one; no internet-sourced hash will be in `hashes`. + if hashes: + hashes.check_against_path(from_path) + return File(from_path, None) + + +def unpack_url( + link, # type: Link + location, # type: str + download, # type: Downloader + download_dir=None, # type: Optional[str] + hashes=None, # type: Optional[Hashes] +): + # type: (...) -> Optional[File] + """Unpack link into location, downloading if required. + + :param hashes: A Hashes object, one of whose embedded hashes must match, + or HashMismatch will be raised. If the Hashes is empty, no matches are + required, and unhashable types of requirements (like VCS ones, which + would ordinarily raise HashUnsupported) are allowed. + """ + # non-editable vcs urls + if link.is_vcs: + unpack_vcs_link(link, location) + return None + + # If it's a url to a local directory + if link.is_existing_dir(): + if os.path.isdir(location): + rmtree(location) + _copy_source_tree(link.file_path, location) + return None + + # file urls + if link.is_file: + file = get_file_url(link, download_dir, hashes=hashes) + + # http urls + else: + file = get_http_url( + link, + download, + download_dir, + hashes=hashes, + ) + + # unpack the archive to the build dir location. even when only downloading + # archives, they have to be unpacked to parse dependencies, except wheels + if not link.is_wheel: + unpack_file(file.path, location, file.content_type) + + return file + + +def _check_download_dir(link, download_dir, hashes): + # type: (Link, str, Optional[Hashes]) -> Optional[str] + """ Check download_dir for previously downloaded file with correct hash + If a correct file is found return its path else None + """ + download_path = os.path.join(download_dir, link.filename) + + if not os.path.exists(download_path): + return None + + # If already downloaded, does its hash match? + logger.info('File was already downloaded %s', download_path) + if hashes: + try: + hashes.check_against_path(download_path) + except HashMismatch: + logger.warning( + 'Previously-downloaded file %s has bad hash. ' + 'Re-downloading.', + download_path + ) + os.unlink(download_path) + return None + return download_path + + +class RequirementPreparer(object): + """Prepares a Requirement + """ + + def __init__( + self, + build_dir, # type: str + download_dir, # type: Optional[str] + src_dir, # type: str + build_isolation, # type: bool + req_tracker, # type: RequirementTracker + session, # type: PipSession + progress_bar, # type: str + finder, # type: PackageFinder + require_hashes, # type: bool + use_user_site, # type: bool + lazy_wheel, # type: bool + ): + # type: (...) -> None + super(RequirementPreparer, self).__init__() + + self.src_dir = src_dir + self.build_dir = build_dir + self.req_tracker = req_tracker + self._session = session + self._download = Downloader(session, progress_bar) + self._batch_download = BatchDownloader(session, progress_bar) + self.finder = finder + + # Where still-packed archives should be written to. If None, they are + # not saved, and are deleted immediately after unpacking. + self.download_dir = download_dir + + # Is build isolation allowed? + self.build_isolation = build_isolation + + # Should hash-checking be required? + self.require_hashes = require_hashes + + # Should install in user site-packages? + self.use_user_site = use_user_site + + # Should wheels be downloaded lazily? + self.use_lazy_wheel = lazy_wheel + + # Memoized downloaded files, as mapping of url: (path, mime type) + self._downloaded = {} # type: Dict[str, Tuple[str, str]] + + # Previous "header" printed for a link-based InstallRequirement + self._previous_requirement_header = ("", "") + + def _log_preparing_link(self, req): + # type: (InstallRequirement) -> None + """Provide context for the requirement being prepared.""" + if req.link.is_file and not req.original_link_is_in_wheel_cache: + message = "Processing %s" + information = str(display_path(req.link.file_path)) + else: + message = "Collecting %s" + information = str(req.req or req) + + if (message, information) != self._previous_requirement_header: + self._previous_requirement_header = (message, information) + logger.info(message, information) + + if req.original_link_is_in_wheel_cache: + with indent_log(): + logger.info("Using cached %s", req.link.filename) + + def _ensure_link_req_src_dir(self, req, parallel_builds): + # type: (InstallRequirement, bool) -> None + """Ensure source_dir of a linked InstallRequirement.""" + # Since source_dir is only set for editable requirements. + if req.link.is_wheel: + # We don't need to unpack wheels, so no need for a source + # directory. + return + assert req.source_dir is None + # We always delete unpacked sdists after pip runs. + req.ensure_has_source_dir( + self.build_dir, + autodelete=True, + parallel_builds=parallel_builds, + ) + + # If a checkout exists, it's unwise to keep going. version + # inconsistencies are logged later, but do not fail the + # installation. + # FIXME: this won't upgrade when there's an existing + # package unpacked in `req.source_dir` + if os.path.exists(os.path.join(req.source_dir, 'setup.py')): + raise PreviousBuildDirError( + "pip can't proceed with requirements '{}' due to a" + "pre-existing build directory ({}). This is likely " + "due to a previous installation that failed . pip is " + "being responsible and not assuming it can delete this. " + "Please delete it and try again.".format(req, req.source_dir) + ) + + def _get_linked_req_hashes(self, req): + # type: (InstallRequirement) -> Hashes + # By the time this is called, the requirement's link should have + # been checked so we can tell what kind of requirements req is + # and raise some more informative errors than otherwise. + # (For example, we can raise VcsHashUnsupported for a VCS URL + # rather than HashMissing.) + if not self.require_hashes: + return req.hashes(trust_internet=True) + + # We could check these first 2 conditions inside unpack_url + # and save repetition of conditions, but then we would + # report less-useful error messages for unhashable + # requirements, complaining that there's no hash provided. + if req.link.is_vcs: + raise VcsHashUnsupported() + if req.link.is_existing_dir(): + raise DirectoryUrlHashUnsupported() + + # Unpinned packages are asking for trouble when a new version + # is uploaded. This isn't a security check, but it saves users + # a surprising hash mismatch in the future. + # file:/// URLs aren't pinnable, so don't complain about them + # not being pinned. + if req.original_link is None and not req.is_pinned: + raise HashUnpinned() + + # If known-good hashes are missing for this requirement, + # shim it with a facade object that will provoke hash + # computation and then raise a HashMissing exception + # showing the user what the hash should be. + return req.hashes(trust_internet=False) or MissingHashes() + + def _fetch_metadata_using_lazy_wheel(self, link): + # type: (Link) -> Optional[Distribution] + """Fetch metadata using lazy wheel, if possible.""" + if not self.use_lazy_wheel: + return None + if self.require_hashes: + logger.debug('Lazy wheel is not used as hash checking is required') + return None + if link.is_file or not link.is_wheel: + logger.debug( + 'Lazy wheel is not used as ' + '%r does not points to a remote wheel', + link, + ) + return None + + wheel = Wheel(link.filename) + name = canonicalize_name(wheel.name) + logger.info( + 'Obtaining dependency information from %s %s', + name, wheel.version, + ) + url = link.url.split('#', 1)[0] + try: + return dist_from_wheel_url(name, url, self._session) + except HTTPRangeRequestUnsupported: + logger.debug('%s does not support range requests', url) + return None + + def prepare_linked_requirement(self, req, parallel_builds=False): + # type: (InstallRequirement, bool) -> Distribution + """Prepare a requirement to be obtained from req.link.""" + assert req.link + link = req.link + self._log_preparing_link(req) + with indent_log(): + # Check if the relevant file is already available + # in the download directory + file_path = None + if self.download_dir is not None and link.is_wheel: + hashes = self._get_linked_req_hashes(req) + file_path = _check_download_dir(req.link, self.download_dir, hashes) + + if file_path is not None: + # The file is already available, so mark it as downloaded + self._downloaded[req.link.url] = file_path, None + else: + # The file is not available, attempt to fetch only metadata + wheel_dist = self._fetch_metadata_using_lazy_wheel(link) + if wheel_dist is not None: + req.needs_more_preparation = True + return wheel_dist + + # None of the optimizations worked, fully prepare the requirement + return self._prepare_linked_requirement(req, parallel_builds) + + def prepare_linked_requirements_more(self, reqs, parallel_builds=False): + # type: (Iterable[InstallRequirement], bool) -> None + """Prepare a linked requirement more, if needed.""" + reqs = [req for req in reqs if req.needs_more_preparation] + links = [req.link for req in reqs] + + # Let's download to a temporary directory. + tmpdir = TempDirectory(kind="unpack", globally_managed=True).path + self._downloaded.update(self._batch_download(links, tmpdir)) + for req in reqs: + self._prepare_linked_requirement(req, parallel_builds) + + def _prepare_linked_requirement(self, req, parallel_builds): + # type: (InstallRequirement, bool) -> Distribution + assert req.link + link = req.link + + self._ensure_link_req_src_dir(req, parallel_builds) + hashes = self._get_linked_req_hashes(req) + if link.url not in self._downloaded: + try: + local_file = unpack_url( + link, req.source_dir, self._download, + self.download_dir, hashes, + ) + except NetworkConnectionError as exc: + raise InstallationError( + 'Could not install requirement {} because of HTTP ' + 'error {} for URL {}'.format(req, exc, link) + ) + else: + file_path, content_type = self._downloaded[link.url] + if hashes: + hashes.check_against_path(file_path) + local_file = File(file_path, content_type) + + # For use in later processing, + # preserve the file path on the requirement. + if local_file: + req.local_file_path = local_file.path + + dist = _get_prepared_distribution( + req, self.req_tracker, self.finder, self.build_isolation, + ) + return dist + + def save_linked_requirement(self, req): + # type: (InstallRequirement) -> None + assert self.download_dir is not None + assert req.link is not None + link = req.link + if link.is_vcs or (link.is_existing_dir() and req.editable): + # Make a .zip of the source_dir we already created. + req.archive(self.download_dir) + return + + if link.is_existing_dir(): + logger.debug( + 'Not copying link to destination directory ' + 'since it is a directory: %s', link, + ) + return + if req.local_file_path is None: + # No distribution was downloaded for this requirement. + return + + download_location = os.path.join(self.download_dir, link.filename) + if not os.path.exists(download_location): + shutil.copy(req.local_file_path, download_location) + download_path = display_path(download_location) + logger.info('Saved %s', download_path) + + def prepare_editable_requirement( + self, + req, # type: InstallRequirement + ): + # type: (...) -> Distribution + """Prepare an editable requirement + """ + assert req.editable, "cannot prepare a non-editable req as editable" + + logger.info('Obtaining %s', req) + + with indent_log(): + if self.require_hashes: + raise InstallationError( + 'The editable requirement {} cannot be installed when ' + 'requiring hashes, because there is no single file to ' + 'hash.'.format(req) + ) + req.ensure_has_source_dir(self.src_dir) + req.update_editable(self.download_dir is None) + + dist = _get_prepared_distribution( + req, self.req_tracker, self.finder, self.build_isolation, + ) + + req.check_if_exists(self.use_user_site) + + return dist + + def prepare_installed_requirement( + self, + req, # type: InstallRequirement + skip_reason # type: str + ): + # type: (...) -> Distribution + """Prepare an already-installed requirement + """ + assert req.satisfied_by, "req should have been satisfied but isn't" + assert skip_reason is not None, ( + "did not get skip reason skipped but req.satisfied_by " + "is set to {}".format(req.satisfied_by) + ) + logger.info( + 'Requirement %s: %s (%s)', + skip_reason, req, req.satisfied_by.version + ) + with indent_log(): + if self.require_hashes: + logger.debug( + 'Since it is already installed, we are trusting this ' + 'package without checking its hash. To ensure a ' + 'completely repeatable environment, install into an ' + 'empty virtualenv.' + ) + return InstalledDistribution(req).get_pkg_resources_distribution() diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/pyproject.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/pyproject.py new file mode 100644 index 0000000000..4144a9ed60 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/pyproject.py @@ -0,0 +1,196 @@ +from __future__ import absolute_import + +import io +import os +import sys +from collections import namedtuple + +from pip._vendor import six, toml +from pip._vendor.packaging.requirements import InvalidRequirement, Requirement + +from pip._internal.exceptions import InstallationError +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Any, List, Optional + + +def _is_list_of_str(obj): + # type: (Any) -> bool + return ( + isinstance(obj, list) and + all(isinstance(item, six.string_types) for item in obj) + ) + + +def make_pyproject_path(unpacked_source_directory): + # type: (str) -> str + path = os.path.join(unpacked_source_directory, 'pyproject.toml') + + # Python2 __file__ should not be unicode + if six.PY2 and isinstance(path, six.text_type): + path = path.encode(sys.getfilesystemencoding()) + + return path + + +BuildSystemDetails = namedtuple('BuildSystemDetails', [ + 'requires', 'backend', 'check', 'backend_path' +]) + + +def load_pyproject_toml( + use_pep517, # type: Optional[bool] + pyproject_toml, # type: str + setup_py, # type: str + req_name # type: str +): + # type: (...) -> Optional[BuildSystemDetails] + """Load the pyproject.toml file. + + Parameters: + use_pep517 - Has the user requested PEP 517 processing? None + means the user hasn't explicitly specified. + pyproject_toml - Location of the project's pyproject.toml file + setup_py - Location of the project's setup.py file + req_name - The name of the requirement we're processing (for + error reporting) + + Returns: + None if we should use the legacy code path, otherwise a tuple + ( + requirements from pyproject.toml, + name of PEP 517 backend, + requirements we should check are installed after setting + up the build environment + directory paths to import the backend from (backend-path), + relative to the project root. + ) + """ + has_pyproject = os.path.isfile(pyproject_toml) + has_setup = os.path.isfile(setup_py) + + if has_pyproject: + with io.open(pyproject_toml, encoding="utf-8") as f: + pp_toml = toml.load(f) + build_system = pp_toml.get("build-system") + else: + build_system = None + + # The following cases must use PEP 517 + # We check for use_pep517 being non-None and falsey because that means + # the user explicitly requested --no-use-pep517. The value 0 as + # opposed to False can occur when the value is provided via an + # environment variable or config file option (due to the quirk of + # strtobool() returning an integer in pip's configuration code). + if has_pyproject and not has_setup: + if use_pep517 is not None and not use_pep517: + raise InstallationError( + "Disabling PEP 517 processing is invalid: " + "project does not have a setup.py" + ) + use_pep517 = True + elif build_system and "build-backend" in build_system: + if use_pep517 is not None and not use_pep517: + raise InstallationError( + "Disabling PEP 517 processing is invalid: " + "project specifies a build backend of {} " + "in pyproject.toml".format( + build_system["build-backend"] + ) + ) + use_pep517 = True + + # If we haven't worked out whether to use PEP 517 yet, + # and the user hasn't explicitly stated a preference, + # we do so if the project has a pyproject.toml file. + elif use_pep517 is None: + use_pep517 = has_pyproject + + # At this point, we know whether we're going to use PEP 517. + assert use_pep517 is not None + + # If we're using the legacy code path, there is nothing further + # for us to do here. + if not use_pep517: + return None + + if build_system is None: + # Either the user has a pyproject.toml with no build-system + # section, or the user has no pyproject.toml, but has opted in + # explicitly via --use-pep517. + # In the absence of any explicit backend specification, we + # assume the setuptools backend that most closely emulates the + # traditional direct setup.py execution, and require wheel and + # a version of setuptools that supports that backend. + + build_system = { + "requires": ["setuptools>=40.8.0", "wheel"], + "build-backend": "setuptools.build_meta:__legacy__", + } + + # If we're using PEP 517, we have build system information (either + # from pyproject.toml, or defaulted by the code above). + # Note that at this point, we do not know if the user has actually + # specified a backend, though. + assert build_system is not None + + # Ensure that the build-system section in pyproject.toml conforms + # to PEP 518. + error_template = ( + "{package} has a pyproject.toml file that does not comply " + "with PEP 518: {reason}" + ) + + # Specifying the build-system table but not the requires key is invalid + if "requires" not in build_system: + raise InstallationError( + error_template.format(package=req_name, reason=( + "it has a 'build-system' table but not " + "'build-system.requires' which is mandatory in the table" + )) + ) + + # Error out if requires is not a list of strings + requires = build_system["requires"] + if not _is_list_of_str(requires): + raise InstallationError(error_template.format( + package=req_name, + reason="'build-system.requires' is not a list of strings.", + )) + + # Each requirement must be valid as per PEP 508 + for requirement in requires: + try: + Requirement(requirement) + except InvalidRequirement: + raise InstallationError( + error_template.format( + package=req_name, + reason=( + "'build-system.requires' contains an invalid " + "requirement: {!r}".format(requirement) + ), + ) + ) + + backend = build_system.get("build-backend") + backend_path = build_system.get("backend-path", []) + check = [] # type: List[str] + if backend is None: + # If the user didn't specify a backend, we assume they want to use + # the setuptools backend. But we can't be sure they have included + # a version of setuptools which supplies the backend, or wheel + # (which is needed by the backend) in their requirements. So we + # make a note to check that those requirements are present once + # we have set up the environment. + # This is quite a lot of work to check for a very specific case. But + # the problem is, that case is potentially quite common - projects that + # adopted PEP 518 early for the ability to specify requirements to + # execute setup.py, but never considered needing to mention the build + # tools themselves. The original PEP 518 code had a similar check (but + # implemented in a different way). + backend = "setuptools.build_meta:__legacy__" + check = ["setuptools>=40.8.0", "wheel"] + + return BuildSystemDetails(requires, backend, check, backend_path) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/req/__init__.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/req/__init__.py new file mode 100644 index 0000000000..8568d3f8b6 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/req/__init__.py @@ -0,0 +1,103 @@ +from __future__ import absolute_import + +import collections +import logging + +from pip._internal.utils.logging import indent_log +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +from .req_file import parse_requirements +from .req_install import InstallRequirement +from .req_set import RequirementSet + +if MYPY_CHECK_RUNNING: + from typing import Iterator, List, Optional, Sequence, Tuple + +__all__ = [ + "RequirementSet", "InstallRequirement", + "parse_requirements", "install_given_reqs", +] + +logger = logging.getLogger(__name__) + + +class InstallationResult(object): + def __init__(self, name): + # type: (str) -> None + self.name = name + + def __repr__(self): + # type: () -> str + return "InstallationResult(name={!r})".format(self.name) + + +def _validate_requirements( + requirements, # type: List[InstallRequirement] +): + # type: (...) -> Iterator[Tuple[str, InstallRequirement]] + for req in requirements: + assert req.name, "invalid to-be-installed requirement: {}".format(req) + yield req.name, req + + +def install_given_reqs( + requirements, # type: List[InstallRequirement] + install_options, # type: List[str] + global_options, # type: Sequence[str] + root, # type: Optional[str] + home, # type: Optional[str] + prefix, # type: Optional[str] + warn_script_location, # type: bool + use_user_site, # type: bool + pycompile, # type: bool +): + # type: (...) -> List[InstallationResult] + """ + Install everything in the given list. + + (to be called after having downloaded and unpacked the packages) + """ + to_install = collections.OrderedDict(_validate_requirements(requirements)) + + if to_install: + logger.info( + 'Installing collected packages: %s', + ', '.join(to_install.keys()), + ) + + installed = [] + + with indent_log(): + for req_name, requirement in to_install.items(): + if requirement.should_reinstall: + logger.info('Attempting uninstall: %s', req_name) + with indent_log(): + uninstalled_pathset = requirement.uninstall( + auto_confirm=True + ) + else: + uninstalled_pathset = None + + try: + requirement.install( + install_options, + global_options, + root=root, + home=home, + prefix=prefix, + warn_script_location=warn_script_location, + use_user_site=use_user_site, + pycompile=pycompile, + ) + except Exception: + # if install did not succeed, rollback previous uninstall + if uninstalled_pathset and not requirement.install_succeeded: + uninstalled_pathset.rollback() + raise + else: + if uninstalled_pathset and requirement.install_succeeded: + uninstalled_pathset.commit() + + installed.append(InstallationResult(req_name)) + + return installed diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/req/constructors.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/req/constructors.py new file mode 100644 index 0000000000..2245cb826f --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/req/constructors.py @@ -0,0 +1,476 @@ +"""Backing implementation for InstallRequirement's various constructors + +The idea here is that these formed a major chunk of InstallRequirement's size +so, moving them and support code dedicated to them outside of that class +helps creates for better understandability for the rest of the code. + +These are meant to be used elsewhere within pip to create instances of +InstallRequirement. +""" + +import logging +import os +import re + +from pip._vendor.packaging.markers import Marker +from pip._vendor.packaging.requirements import InvalidRequirement, Requirement +from pip._vendor.packaging.specifiers import Specifier +from pip._vendor.pkg_resources import RequirementParseError, parse_requirements + +from pip._internal.exceptions import InstallationError +from pip._internal.models.index import PyPI, TestPyPI +from pip._internal.models.link import Link +from pip._internal.models.wheel import Wheel +from pip._internal.pyproject import make_pyproject_path +from pip._internal.req.req_install import InstallRequirement +from pip._internal.utils.deprecation import deprecated +from pip._internal.utils.filetypes import is_archive_file +from pip._internal.utils.misc import is_installable_dir +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.urls import path_to_url +from pip._internal.vcs import is_url, vcs + +if MYPY_CHECK_RUNNING: + from typing import Any, Dict, Optional, Set, Tuple, Union + + from pip._internal.req.req_file import ParsedRequirement + + +__all__ = [ + "install_req_from_editable", "install_req_from_line", + "parse_editable" +] + +logger = logging.getLogger(__name__) +operators = Specifier._operators.keys() + + +def _strip_extras(path): + # type: (str) -> Tuple[str, Optional[str]] + m = re.match(r'^(.+)(\[[^\]]+\])$', path) + extras = None + if m: + path_no_extras = m.group(1) + extras = m.group(2) + else: + path_no_extras = path + + return path_no_extras, extras + + +def convert_extras(extras): + # type: (Optional[str]) -> Set[str] + if not extras: + return set() + return Requirement("placeholder" + extras.lower()).extras + + +def parse_editable(editable_req): + # type: (str) -> Tuple[Optional[str], str, Set[str]] + """Parses an editable requirement into: + - a requirement name + - an URL + - extras + - editable options + Accepted requirements: + svn+http://blahblah@rev#egg=Foobar[baz]&subdirectory=version_subdir + .[some_extra] + """ + + url = editable_req + + # If a file path is specified with extras, strip off the extras. + url_no_extras, extras = _strip_extras(url) + + if os.path.isdir(url_no_extras): + if not os.path.exists(os.path.join(url_no_extras, 'setup.py')): + msg = ( + 'File "setup.py" not found. Directory cannot be installed ' + 'in editable mode: {}'.format(os.path.abspath(url_no_extras)) + ) + pyproject_path = make_pyproject_path(url_no_extras) + if os.path.isfile(pyproject_path): + msg += ( + '\n(A "pyproject.toml" file was found, but editable ' + 'mode currently requires a setup.py based build.)' + ) + raise InstallationError(msg) + + # Treating it as code that has already been checked out + url_no_extras = path_to_url(url_no_extras) + + if url_no_extras.lower().startswith('file:'): + package_name = Link(url_no_extras).egg_fragment + if extras: + return ( + package_name, + url_no_extras, + Requirement("placeholder" + extras.lower()).extras, + ) + else: + return package_name, url_no_extras, set() + + for version_control in vcs: + if url.lower().startswith('{}:'.format(version_control)): + url = '{}+{}'.format(version_control, url) + break + + if '+' not in url: + raise InstallationError( + '{} is not a valid editable requirement. ' + 'It should either be a path to a local project or a VCS URL ' + '(beginning with svn+, git+, hg+, or bzr+).'.format(editable_req) + ) + + vc_type = url.split('+', 1)[0].lower() + + if not vcs.get_backend(vc_type): + backends = ", ".join([bends.name + '+URL' for bends in vcs.backends]) + error_message = "For --editable={}, " \ + "only {} are currently supported".format( + editable_req, backends) + raise InstallationError(error_message) + + package_name = Link(url).egg_fragment + if not package_name: + raise InstallationError( + "Could not detect requirement name for '{}', please specify one " + "with #egg=your_package_name".format(editable_req) + ) + return package_name, url, set() + + +def deduce_helpful_msg(req): + # type: (str) -> str + """Returns helpful msg in case requirements file does not exist, + or cannot be parsed. + + :params req: Requirements file path + """ + msg = "" + if os.path.exists(req): + msg = " The path does exist. " + # Try to parse and check if it is a requirements file. + try: + with open(req, 'r') as fp: + # parse first line only + next(parse_requirements(fp.read())) + msg += ( + "The argument you provided " + "({}) appears to be a" + " requirements file. If that is the" + " case, use the '-r' flag to install" + " the packages specified within it." + ).format(req) + except RequirementParseError: + logger.debug( + "Cannot parse '%s' as requirements file", req, exc_info=True + ) + else: + msg += " File '{}' does not exist.".format(req) + return msg + + +class RequirementParts(object): + def __init__( + self, + requirement, # type: Optional[Requirement] + link, # type: Optional[Link] + markers, # type: Optional[Marker] + extras, # type: Set[str] + ): + self.requirement = requirement + self.link = link + self.markers = markers + self.extras = extras + + +def parse_req_from_editable(editable_req): + # type: (str) -> RequirementParts + name, url, extras_override = parse_editable(editable_req) + + if name is not None: + try: + req = Requirement(name) + except InvalidRequirement: + raise InstallationError("Invalid requirement: '{}'".format(name)) + else: + req = None + + link = Link(url) + + return RequirementParts(req, link, None, extras_override) + + +# ---- The actual constructors follow ---- + + +def install_req_from_editable( + editable_req, # type: str + comes_from=None, # type: Optional[Union[InstallRequirement, str]] + use_pep517=None, # type: Optional[bool] + isolated=False, # type: bool + options=None, # type: Optional[Dict[str, Any]] + constraint=False, # type: bool + user_supplied=False, # type: bool +): + # type: (...) -> InstallRequirement + + parts = parse_req_from_editable(editable_req) + + return InstallRequirement( + parts.requirement, + comes_from=comes_from, + user_supplied=user_supplied, + editable=True, + link=parts.link, + constraint=constraint, + use_pep517=use_pep517, + isolated=isolated, + install_options=options.get("install_options", []) if options else [], + global_options=options.get("global_options", []) if options else [], + hash_options=options.get("hashes", {}) if options else {}, + extras=parts.extras, + ) + + +def _looks_like_path(name): + # type: (str) -> bool + """Checks whether the string "looks like" a path on the filesystem. + + This does not check whether the target actually exists, only judge from the + appearance. + + Returns true if any of the following conditions is true: + * a path separator is found (either os.path.sep or os.path.altsep); + * a dot is found (which represents the current directory). + """ + if os.path.sep in name: + return True + if os.path.altsep is not None and os.path.altsep in name: + return True + if name.startswith("."): + return True + return False + + +def _get_url_from_path(path, name): + # type: (str, str) -> Optional[str] + """ + First, it checks whether a provided path is an installable directory + (e.g. it has a setup.py). If it is, returns the path. + + If false, check if the path is an archive file (such as a .whl). + The function checks if the path is a file. If false, if the path has + an @, it will treat it as a PEP 440 URL requirement and return the path. + """ + if _looks_like_path(name) and os.path.isdir(path): + if is_installable_dir(path): + return path_to_url(path) + raise InstallationError( + "Directory {name!r} is not installable. Neither 'setup.py' " + "nor 'pyproject.toml' found.".format(**locals()) + ) + if not is_archive_file(path): + return None + if os.path.isfile(path): + return path_to_url(path) + urlreq_parts = name.split('@', 1) + if len(urlreq_parts) >= 2 and not _looks_like_path(urlreq_parts[0]): + # If the path contains '@' and the part before it does not look + # like a path, try to treat it as a PEP 440 URL req instead. + return None + logger.warning( + 'Requirement %r looks like a filename, but the ' + 'file does not exist', + name + ) + return path_to_url(path) + + +def parse_req_from_line(name, line_source): + # type: (str, Optional[str]) -> RequirementParts + if is_url(name): + marker_sep = '; ' + else: + marker_sep = ';' + if marker_sep in name: + name, markers_as_string = name.split(marker_sep, 1) + markers_as_string = markers_as_string.strip() + if not markers_as_string: + markers = None + else: + markers = Marker(markers_as_string) + else: + markers = None + name = name.strip() + req_as_string = None + path = os.path.normpath(os.path.abspath(name)) + link = None + extras_as_string = None + + if is_url(name): + link = Link(name) + else: + p, extras_as_string = _strip_extras(path) + url = _get_url_from_path(p, name) + if url is not None: + link = Link(url) + + # it's a local file, dir, or url + if link: + # Handle relative file URLs + if link.scheme == 'file' and re.search(r'\.\./', link.url): + link = Link( + path_to_url(os.path.normpath(os.path.abspath(link.path)))) + # wheel file + if link.is_wheel: + wheel = Wheel(link.filename) # can raise InvalidWheelFilename + req_as_string = "{wheel.name}=={wheel.version}".format(**locals()) + else: + # set the req to the egg fragment. when it's not there, this + # will become an 'unnamed' requirement + req_as_string = link.egg_fragment + + # a requirement specifier + else: + req_as_string = name + + extras = convert_extras(extras_as_string) + + def with_source(text): + # type: (str) -> str + if not line_source: + return text + return '{} (from {})'.format(text, line_source) + + if req_as_string is not None: + try: + req = Requirement(req_as_string) + except InvalidRequirement: + if os.path.sep in req_as_string: + add_msg = "It looks like a path." + add_msg += deduce_helpful_msg(req_as_string) + elif ('=' in req_as_string and + not any(op in req_as_string for op in operators)): + add_msg = "= is not a valid operator. Did you mean == ?" + else: + add_msg = '' + msg = with_source( + 'Invalid requirement: {!r}'.format(req_as_string) + ) + if add_msg: + msg += '\nHint: {}'.format(add_msg) + raise InstallationError(msg) + else: + # Deprecate extras after specifiers: "name>=1.0[extras]" + # This currently works by accident because _strip_extras() parses + # any extras in the end of the string and those are saved in + # RequirementParts + for spec in req.specifier: + spec_str = str(spec) + if spec_str.endswith(']'): + msg = "Extras after version '{}'.".format(spec_str) + replace = "moving the extras before version specifiers" + deprecated(msg, replacement=replace, gone_in="21.0") + else: + req = None + + return RequirementParts(req, link, markers, extras) + + +def install_req_from_line( + name, # type: str + comes_from=None, # type: Optional[Union[str, InstallRequirement]] + use_pep517=None, # type: Optional[bool] + isolated=False, # type: bool + options=None, # type: Optional[Dict[str, Any]] + constraint=False, # type: bool + line_source=None, # type: Optional[str] + user_supplied=False, # type: bool +): + # type: (...) -> InstallRequirement + """Creates an InstallRequirement from a name, which might be a + requirement, directory containing 'setup.py', filename, or URL. + + :param line_source: An optional string describing where the line is from, + for logging purposes in case of an error. + """ + parts = parse_req_from_line(name, line_source) + + return InstallRequirement( + parts.requirement, comes_from, link=parts.link, markers=parts.markers, + use_pep517=use_pep517, isolated=isolated, + install_options=options.get("install_options", []) if options else [], + global_options=options.get("global_options", []) if options else [], + hash_options=options.get("hashes", {}) if options else {}, + constraint=constraint, + extras=parts.extras, + user_supplied=user_supplied, + ) + + +def install_req_from_req_string( + req_string, # type: str + comes_from=None, # type: Optional[InstallRequirement] + isolated=False, # type: bool + use_pep517=None, # type: Optional[bool] + user_supplied=False, # type: bool +): + # type: (...) -> InstallRequirement + try: + req = Requirement(req_string) + except InvalidRequirement: + raise InstallationError("Invalid requirement: '{}'".format(req_string)) + + domains_not_allowed = [ + PyPI.file_storage_domain, + TestPyPI.file_storage_domain, + ] + if (req.url and comes_from and comes_from.link and + comes_from.link.netloc in domains_not_allowed): + # Explicitly disallow pypi packages that depend on external urls + raise InstallationError( + "Packages installed from PyPI cannot depend on packages " + "which are not also hosted on PyPI.\n" + "{} depends on {} ".format(comes_from.name, req) + ) + + return InstallRequirement( + req, + comes_from, + isolated=isolated, + use_pep517=use_pep517, + user_supplied=user_supplied, + ) + + +def install_req_from_parsed_requirement( + parsed_req, # type: ParsedRequirement + isolated=False, # type: bool + use_pep517=None, # type: Optional[bool] + user_supplied=False, # type: bool +): + # type: (...) -> InstallRequirement + if parsed_req.is_editable: + req = install_req_from_editable( + parsed_req.requirement, + comes_from=parsed_req.comes_from, + use_pep517=use_pep517, + constraint=parsed_req.constraint, + isolated=isolated, + user_supplied=user_supplied, + ) + + else: + req = install_req_from_line( + parsed_req.requirement, + comes_from=parsed_req.comes_from, + use_pep517=use_pep517, + isolated=isolated, + options=parsed_req.options, + constraint=parsed_req.constraint, + line_source=parsed_req.line_source, + user_supplied=user_supplied, + ) + return req diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/req/req_file.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/req/req_file.py new file mode 100644 index 0000000000..0af60fa056 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/req/req_file.py @@ -0,0 +1,574 @@ +""" +Requirements file parsing +""" + +from __future__ import absolute_import + +import optparse +import os +import re +import shlex +import sys + +from pip._vendor.six.moves.urllib import parse as urllib_parse + +from pip._internal.cli import cmdoptions +from pip._internal.exceptions import InstallationError, RequirementsFileParseError +from pip._internal.models.search_scope import SearchScope +from pip._internal.network.utils import raise_for_status +from pip._internal.utils.encoding import auto_decode +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.urls import get_url_scheme, url_to_path + +if MYPY_CHECK_RUNNING: + from optparse import Values + from typing import ( + Any, + Callable, + Dict, + Iterator, + List, + NoReturn, + Optional, + Text, + Tuple, + ) + + from pip._internal.index.package_finder import PackageFinder + from pip._internal.network.session import PipSession + + ReqFileLines = Iterator[Tuple[int, Text]] + + LineParser = Callable[[Text], Tuple[str, Values]] + + +__all__ = ['parse_requirements'] + +SCHEME_RE = re.compile(r'^(http|https|file):', re.I) +COMMENT_RE = re.compile(r'(^|\s+)#.*$') + +# Matches environment variable-style values in '${MY_VARIABLE_1}' with the +# variable name consisting of only uppercase letters, digits or the '_' +# (underscore). This follows the POSIX standard defined in IEEE Std 1003.1, +# 2013 Edition. +ENV_VAR_RE = re.compile(r'(?P\$\{(?P[A-Z0-9_]+)\})') + +SUPPORTED_OPTIONS = [ + cmdoptions.index_url, + cmdoptions.extra_index_url, + cmdoptions.no_index, + cmdoptions.constraints, + cmdoptions.requirements, + cmdoptions.editable, + cmdoptions.find_links, + cmdoptions.no_binary, + cmdoptions.only_binary, + cmdoptions.prefer_binary, + cmdoptions.require_hashes, + cmdoptions.pre, + cmdoptions.trusted_host, + cmdoptions.use_new_feature, +] # type: List[Callable[..., optparse.Option]] + +# options to be passed to requirements +SUPPORTED_OPTIONS_REQ = [ + cmdoptions.install_options, + cmdoptions.global_options, + cmdoptions.hash, +] # type: List[Callable[..., optparse.Option]] + +# the 'dest' string values +SUPPORTED_OPTIONS_REQ_DEST = [str(o().dest) for o in SUPPORTED_OPTIONS_REQ] + + +class ParsedRequirement(object): + def __init__( + self, + requirement, # type:str + is_editable, # type: bool + comes_from, # type: str + constraint, # type: bool + options=None, # type: Optional[Dict[str, Any]] + line_source=None, # type: Optional[str] + ): + # type: (...) -> None + self.requirement = requirement + self.is_editable = is_editable + self.comes_from = comes_from + self.options = options + self.constraint = constraint + self.line_source = line_source + + +class ParsedLine(object): + def __init__( + self, + filename, # type: str + lineno, # type: int + args, # type: str + opts, # type: Values + constraint, # type: bool + ): + # type: (...) -> None + self.filename = filename + self.lineno = lineno + self.opts = opts + self.constraint = constraint + + if args: + self.is_requirement = True + self.is_editable = False + self.requirement = args + elif opts.editables: + self.is_requirement = True + self.is_editable = True + # We don't support multiple -e on one line + self.requirement = opts.editables[0] + else: + self.is_requirement = False + + +def parse_requirements( + filename, # type: str + session, # type: PipSession + finder=None, # type: Optional[PackageFinder] + options=None, # type: Optional[optparse.Values] + constraint=False, # type: bool +): + # type: (...) -> Iterator[ParsedRequirement] + """Parse a requirements file and yield ParsedRequirement instances. + + :param filename: Path or url of requirements file. + :param session: PipSession instance. + :param finder: Instance of pip.index.PackageFinder. + :param options: cli options. + :param constraint: If true, parsing a constraint file rather than + requirements file. + """ + line_parser = get_line_parser(finder) + parser = RequirementsFileParser(session, line_parser) + + for parsed_line in parser.parse(filename, constraint): + parsed_req = handle_line( + parsed_line, + options=options, + finder=finder, + session=session + ) + if parsed_req is not None: + yield parsed_req + + +def preprocess(content): + # type: (Text) -> ReqFileLines + """Split, filter, and join lines, and return a line iterator + + :param content: the content of the requirements file + """ + lines_enum = enumerate(content.splitlines(), start=1) # type: ReqFileLines + lines_enum = join_lines(lines_enum) + lines_enum = ignore_comments(lines_enum) + lines_enum = expand_env_variables(lines_enum) + return lines_enum + + +def handle_requirement_line( + line, # type: ParsedLine + options=None, # type: Optional[optparse.Values] +): + # type: (...) -> ParsedRequirement + + # preserve for the nested code path + line_comes_from = '{} {} (line {})'.format( + '-c' if line.constraint else '-r', line.filename, line.lineno, + ) + + assert line.is_requirement + + if line.is_editable: + # For editable requirements, we don't support per-requirement + # options, so just return the parsed requirement. + return ParsedRequirement( + requirement=line.requirement, + is_editable=line.is_editable, + comes_from=line_comes_from, + constraint=line.constraint, + ) + else: + if options: + # Disable wheels if the user has specified build options + cmdoptions.check_install_build_global(options, line.opts) + + # get the options that apply to requirements + req_options = {} + for dest in SUPPORTED_OPTIONS_REQ_DEST: + if dest in line.opts.__dict__ and line.opts.__dict__[dest]: + req_options[dest] = line.opts.__dict__[dest] + + line_source = 'line {} of {}'.format(line.lineno, line.filename) + return ParsedRequirement( + requirement=line.requirement, + is_editable=line.is_editable, + comes_from=line_comes_from, + constraint=line.constraint, + options=req_options, + line_source=line_source, + ) + + +def handle_option_line( + opts, # type: Values + filename, # type: str + lineno, # type: int + finder=None, # type: Optional[PackageFinder] + options=None, # type: Optional[optparse.Values] + session=None, # type: Optional[PipSession] +): + # type: (...) -> None + + if options: + # percolate options upward + if opts.require_hashes: + options.require_hashes = opts.require_hashes + if opts.features_enabled: + options.features_enabled.extend( + f for f in opts.features_enabled + if f not in options.features_enabled + ) + + # set finder options + if finder: + find_links = finder.find_links + index_urls = finder.index_urls + if opts.index_url: + index_urls = [opts.index_url] + if opts.no_index is True: + index_urls = [] + if opts.extra_index_urls: + index_urls.extend(opts.extra_index_urls) + if opts.find_links: + # FIXME: it would be nice to keep track of the source + # of the find_links: support a find-links local path + # relative to a requirements file. + value = opts.find_links[0] + req_dir = os.path.dirname(os.path.abspath(filename)) + relative_to_reqs_file = os.path.join(req_dir, value) + if os.path.exists(relative_to_reqs_file): + value = relative_to_reqs_file + find_links.append(value) + + if session: + # We need to update the auth urls in session + session.update_index_urls(index_urls) + + search_scope = SearchScope( + find_links=find_links, + index_urls=index_urls, + ) + finder.search_scope = search_scope + + if opts.pre: + finder.set_allow_all_prereleases() + + if opts.prefer_binary: + finder.set_prefer_binary() + + if session: + for host in opts.trusted_hosts or []: + source = 'line {} of {}'.format(lineno, filename) + session.add_trusted_host(host, source=source) + + +def handle_line( + line, # type: ParsedLine + options=None, # type: Optional[optparse.Values] + finder=None, # type: Optional[PackageFinder] + session=None, # type: Optional[PipSession] +): + # type: (...) -> Optional[ParsedRequirement] + """Handle a single parsed requirements line; This can result in + creating/yielding requirements, or updating the finder. + + :param line: The parsed line to be processed. + :param options: CLI options. + :param finder: The finder - updated by non-requirement lines. + :param session: The session - updated by non-requirement lines. + + Returns a ParsedRequirement object if the line is a requirement line, + otherwise returns None. + + For lines that contain requirements, the only options that have an effect + are from SUPPORTED_OPTIONS_REQ, and they are scoped to the + requirement. Other options from SUPPORTED_OPTIONS may be present, but are + ignored. + + For lines that do not contain requirements, the only options that have an + effect are from SUPPORTED_OPTIONS. Options from SUPPORTED_OPTIONS_REQ may + be present, but are ignored. These lines may contain multiple options + (although our docs imply only one is supported), and all our parsed and + affect the finder. + """ + + if line.is_requirement: + parsed_req = handle_requirement_line(line, options) + return parsed_req + else: + handle_option_line( + line.opts, + line.filename, + line.lineno, + finder, + options, + session, + ) + return None + + +class RequirementsFileParser(object): + def __init__( + self, + session, # type: PipSession + line_parser, # type: LineParser + ): + # type: (...) -> None + self._session = session + self._line_parser = line_parser + + def parse(self, filename, constraint): + # type: (str, bool) -> Iterator[ParsedLine] + """Parse a given file, yielding parsed lines. + """ + for line in self._parse_and_recurse(filename, constraint): + yield line + + def _parse_and_recurse(self, filename, constraint): + # type: (str, bool) -> Iterator[ParsedLine] + for line in self._parse_file(filename, constraint): + if ( + not line.is_requirement and + (line.opts.requirements or line.opts.constraints) + ): + # parse a nested requirements file + if line.opts.requirements: + req_path = line.opts.requirements[0] + nested_constraint = False + else: + req_path = line.opts.constraints[0] + nested_constraint = True + + # original file is over http + if SCHEME_RE.search(filename): + # do a url join so relative paths work + req_path = urllib_parse.urljoin(filename, req_path) + # original file and nested file are paths + elif not SCHEME_RE.search(req_path): + # do a join so relative paths work + req_path = os.path.join( + os.path.dirname(filename), req_path, + ) + + for inner_line in self._parse_and_recurse( + req_path, nested_constraint, + ): + yield inner_line + else: + yield line + + def _parse_file(self, filename, constraint): + # type: (str, bool) -> Iterator[ParsedLine] + _, content = get_file_content(filename, self._session) + + lines_enum = preprocess(content) + + for line_number, line in lines_enum: + try: + args_str, opts = self._line_parser(line) + except OptionParsingError as e: + # add offending line + msg = 'Invalid requirement: {}\n{}'.format(line, e.msg) + raise RequirementsFileParseError(msg) + + yield ParsedLine( + filename, + line_number, + args_str, + opts, + constraint, + ) + + +def get_line_parser(finder): + # type: (Optional[PackageFinder]) -> LineParser + def parse_line(line): + # type: (Text) -> Tuple[str, Values] + # Build new parser for each line since it accumulates appendable + # options. + parser = build_parser() + defaults = parser.get_default_values() + defaults.index_url = None + if finder: + defaults.format_control = finder.format_control + + args_str, options_str = break_args_options(line) + # Prior to 2.7.3, shlex cannot deal with unicode entries + if sys.version_info < (2, 7, 3): + # https://github.com/python/mypy/issues/1174 + options_str = options_str.encode('utf8') # type: ignore + + # https://github.com/python/mypy/issues/1174 + opts, _ = parser.parse_args( + shlex.split(options_str), defaults) # type: ignore + + return args_str, opts + + return parse_line + + +def break_args_options(line): + # type: (Text) -> Tuple[str, Text] + """Break up the line into an args and options string. We only want to shlex + (and then optparse) the options, not the args. args can contain markers + which are corrupted by shlex. + """ + tokens = line.split(' ') + args = [] + options = tokens[:] + for token in tokens: + if token.startswith('-') or token.startswith('--'): + break + else: + args.append(token) + options.pop(0) + return ' '.join(args), ' '.join(options) # type: ignore + + +class OptionParsingError(Exception): + def __init__(self, msg): + # type: (str) -> None + self.msg = msg + + +def build_parser(): + # type: () -> optparse.OptionParser + """ + Return a parser for parsing requirement lines + """ + parser = optparse.OptionParser(add_help_option=False) + + option_factories = SUPPORTED_OPTIONS + SUPPORTED_OPTIONS_REQ + for option_factory in option_factories: + option = option_factory() + parser.add_option(option) + + # By default optparse sys.exits on parsing errors. We want to wrap + # that in our own exception. + def parser_exit(self, msg): + # type: (Any, str) -> NoReturn + raise OptionParsingError(msg) + # NOTE: mypy disallows assigning to a method + # https://github.com/python/mypy/issues/2427 + parser.exit = parser_exit # type: ignore + + return parser + + +def join_lines(lines_enum): + # type: (ReqFileLines) -> ReqFileLines + """Joins a line ending in '\' with the previous line (except when following + comments). The joined line takes on the index of the first line. + """ + primary_line_number = None + new_line = [] # type: List[Text] + for line_number, line in lines_enum: + if not line.endswith('\\') or COMMENT_RE.match(line): + if COMMENT_RE.match(line): + # this ensures comments are always matched later + line = ' ' + line + if new_line: + new_line.append(line) + assert primary_line_number is not None + yield primary_line_number, ''.join(new_line) + new_line = [] + else: + yield line_number, line + else: + if not new_line: + primary_line_number = line_number + new_line.append(line.strip('\\')) + + # last line contains \ + if new_line: + assert primary_line_number is not None + yield primary_line_number, ''.join(new_line) + + # TODO: handle space after '\'. + + +def ignore_comments(lines_enum): + # type: (ReqFileLines) -> ReqFileLines + """ + Strips comments and filter empty lines. + """ + for line_number, line in lines_enum: + line = COMMENT_RE.sub('', line) + line = line.strip() + if line: + yield line_number, line + + +def expand_env_variables(lines_enum): + # type: (ReqFileLines) -> ReqFileLines + """Replace all environment variables that can be retrieved via `os.getenv`. + + The only allowed format for environment variables defined in the + requirement file is `${MY_VARIABLE_1}` to ensure two things: + + 1. Strings that contain a `$` aren't accidentally (partially) expanded. + 2. Ensure consistency across platforms for requirement files. + + These points are the result of a discussion on the `github pull + request #3514 `_. + + Valid characters in variable names follow the `POSIX standard + `_ and are limited + to uppercase letter, digits and the `_` (underscore). + """ + for line_number, line in lines_enum: + for env_var, var_name in ENV_VAR_RE.findall(line): + value = os.getenv(var_name) + if not value: + continue + + line = line.replace(env_var, value) + + yield line_number, line + + +def get_file_content(url, session): + # type: (str, PipSession) -> Tuple[str, Text] + """Gets the content of a file; it may be a filename, file: URL, or + http: URL. Returns (location, content). Content is unicode. + Respects # -*- coding: declarations on the retrieved files. + + :param url: File path or url. + :param session: PipSession instance. + """ + scheme = get_url_scheme(url) + + if scheme in ['http', 'https']: + # FIXME: catch some errors + resp = session.get(url) + raise_for_status(resp) + return resp.url, resp.text + + elif scheme == 'file': + url = url_to_path(url) + + try: + with open(url, 'rb') as f: + content = auto_decode(f.read()) + except IOError as exc: + raise InstallationError( + 'Could not open requirements file: {}'.format(exc) + ) + return url, content diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/req/req_install.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/req/req_install.py new file mode 100644 index 0000000000..548c00db4b --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/req/req_install.py @@ -0,0 +1,915 @@ +# The following comment should be removed at some point in the future. +# mypy: strict-optional=False + +from __future__ import absolute_import + +import logging +import os +import shutil +import sys +import uuid +import zipfile + +from pip._vendor import pkg_resources, six +from pip._vendor.packaging.requirements import Requirement +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.packaging.version import Version +from pip._vendor.packaging.version import parse as parse_version +from pip._vendor.pep517.wrappers import Pep517HookCaller + +from pip._internal.build_env import NoOpBuildEnvironment +from pip._internal.exceptions import InstallationError +from pip._internal.locations import get_scheme +from pip._internal.models.link import Link +from pip._internal.operations.build.metadata import generate_metadata +from pip._internal.operations.build.metadata_legacy import ( + generate_metadata as generate_metadata_legacy, +) +from pip._internal.operations.install.editable_legacy import ( + install_editable as install_editable_legacy, +) +from pip._internal.operations.install.legacy import LegacyInstallFailure +from pip._internal.operations.install.legacy import install as install_legacy +from pip._internal.operations.install.wheel import install_wheel +from pip._internal.pyproject import load_pyproject_toml, make_pyproject_path +from pip._internal.req.req_uninstall import UninstallPathSet +from pip._internal.utils.deprecation import deprecated +from pip._internal.utils.direct_url_helpers import direct_url_from_link +from pip._internal.utils.hashes import Hashes +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import ( + ask_path_exists, + backup_dir, + display_path, + dist_in_site_packages, + dist_in_usersite, + get_distribution, + get_installed_version, + hide_url, + redact_auth_from_url, +) +from pip._internal.utils.packaging import get_metadata +from pip._internal.utils.temp_dir import TempDirectory, tempdir_kinds +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.virtualenv import running_under_virtualenv +from pip._internal.vcs import vcs + +if MYPY_CHECK_RUNNING: + from typing import Any, Dict, Iterable, List, Optional, Sequence, Union + + from pip._vendor.packaging.markers import Marker + from pip._vendor.packaging.specifiers import SpecifierSet + from pip._vendor.pkg_resources import Distribution + + from pip._internal.build_env import BuildEnvironment + + +logger = logging.getLogger(__name__) + + +def _get_dist(metadata_directory): + # type: (str) -> Distribution + """Return a pkg_resources.Distribution for the provided + metadata directory. + """ + dist_dir = metadata_directory.rstrip(os.sep) + + # Build a PathMetadata object, from path to metadata. :wink: + base_dir, dist_dir_name = os.path.split(dist_dir) + metadata = pkg_resources.PathMetadata(base_dir, dist_dir) + + # Determine the correct Distribution object type. + if dist_dir.endswith(".egg-info"): + dist_cls = pkg_resources.Distribution + dist_name = os.path.splitext(dist_dir_name)[0] + else: + assert dist_dir.endswith(".dist-info") + dist_cls = pkg_resources.DistInfoDistribution + dist_name = os.path.splitext(dist_dir_name)[0].split("-")[0] + + return dist_cls( + base_dir, + project_name=dist_name, + metadata=metadata, + ) + + +class InstallRequirement(object): + """ + Represents something that may be installed later on, may have information + about where to fetch the relevant requirement and also contains logic for + installing the said requirement. + """ + + def __init__( + self, + req, # type: Optional[Requirement] + comes_from, # type: Optional[Union[str, InstallRequirement]] + editable=False, # type: bool + link=None, # type: Optional[Link] + markers=None, # type: Optional[Marker] + use_pep517=None, # type: Optional[bool] + isolated=False, # type: bool + install_options=None, # type: Optional[List[str]] + global_options=None, # type: Optional[List[str]] + hash_options=None, # type: Optional[Dict[str, List[str]]] + constraint=False, # type: bool + extras=(), # type: Iterable[str] + user_supplied=False, # type: bool + ): + # type: (...) -> None + assert req is None or isinstance(req, Requirement), req + self.req = req + self.comes_from = comes_from + self.constraint = constraint + self.editable = editable + self.legacy_install_reason = None # type: Optional[int] + + # source_dir is the local directory where the linked requirement is + # located, or unpacked. In case unpacking is needed, creating and + # populating source_dir is done by the RequirementPreparer. Note this + # is not necessarily the directory where pyproject.toml or setup.py is + # located - that one is obtained via unpacked_source_directory. + self.source_dir = None # type: Optional[str] + if self.editable: + assert link + if link.is_file: + self.source_dir = os.path.normpath( + os.path.abspath(link.file_path) + ) + + if link is None and req and req.url: + # PEP 508 URL requirement + link = Link(req.url) + self.link = self.original_link = link + self.original_link_is_in_wheel_cache = False + + # Path to any downloaded or already-existing package. + self.local_file_path = None # type: Optional[str] + if self.link and self.link.is_file: + self.local_file_path = self.link.file_path + + if extras: + self.extras = extras + elif req: + self.extras = { + pkg_resources.safe_extra(extra) for extra in req.extras + } + else: + self.extras = set() + if markers is None and req: + markers = req.marker + self.markers = markers + + # This holds the pkg_resources.Distribution object if this requirement + # is already available: + self.satisfied_by = None # type: Optional[Distribution] + # Whether the installation process should try to uninstall an existing + # distribution before installing this requirement. + self.should_reinstall = False + # Temporary build location + self._temp_build_dir = None # type: Optional[TempDirectory] + # Set to True after successful installation + self.install_succeeded = None # type: Optional[bool] + # Supplied options + self.install_options = install_options if install_options else [] + self.global_options = global_options if global_options else [] + self.hash_options = hash_options if hash_options else {} + # Set to True after successful preparation of this requirement + self.prepared = False + # User supplied requirement are explicitly requested for installation + # by the user via CLI arguments or requirements files, as opposed to, + # e.g. dependencies, extras or constraints. + self.user_supplied = user_supplied + + self.isolated = isolated + self.build_env = NoOpBuildEnvironment() # type: BuildEnvironment + + # For PEP 517, the directory where we request the project metadata + # gets stored. We need this to pass to build_wheel, so the backend + # can ensure that the wheel matches the metadata (see the PEP for + # details). + self.metadata_directory = None # type: Optional[str] + + # The static build requirements (from pyproject.toml) + self.pyproject_requires = None # type: Optional[List[str]] + + # Build requirements that we will check are available + self.requirements_to_check = [] # type: List[str] + + # The PEP 517 backend we should use to build the project + self.pep517_backend = None # type: Optional[Pep517HookCaller] + + # Are we using PEP 517 for this requirement? + # After pyproject.toml has been loaded, the only valid values are True + # and False. Before loading, None is valid (meaning "use the default"). + # Setting an explicit value before loading pyproject.toml is supported, + # but after loading this flag should be treated as read only. + self.use_pep517 = use_pep517 + + # This requirement needs more preparation before it can be built + self.needs_more_preparation = False + + def __str__(self): + # type: () -> str + if self.req: + s = str(self.req) + if self.link: + s += ' from {}'.format(redact_auth_from_url(self.link.url)) + elif self.link: + s = redact_auth_from_url(self.link.url) + else: + s = '' + if self.satisfied_by is not None: + s += ' in {}'.format(display_path(self.satisfied_by.location)) + if self.comes_from: + if isinstance(self.comes_from, six.string_types): + comes_from = self.comes_from # type: Optional[str] + else: + comes_from = self.comes_from.from_path() + if comes_from: + s += ' (from {})'.format(comes_from) + return s + + def __repr__(self): + # type: () -> str + return '<{} object: {} editable={!r}>'.format( + self.__class__.__name__, str(self), self.editable) + + def format_debug(self): + # type: () -> str + """An un-tested helper for getting state, for debugging. + """ + attributes = vars(self) + names = sorted(attributes) + + state = ( + "{}={!r}".format(attr, attributes[attr]) for attr in sorted(names) + ) + return '<{name} object: {{{state}}}>'.format( + name=self.__class__.__name__, + state=", ".join(state), + ) + + # Things that are valid for all kinds of requirements? + @property + def name(self): + # type: () -> Optional[str] + if self.req is None: + return None + return six.ensure_str(pkg_resources.safe_name(self.req.name)) + + @property + def specifier(self): + # type: () -> SpecifierSet + return self.req.specifier + + @property + def is_pinned(self): + # type: () -> bool + """Return whether I am pinned to an exact version. + + For example, some-package==1.2 is pinned; some-package>1.2 is not. + """ + specifiers = self.specifier + return (len(specifiers) == 1 and + next(iter(specifiers)).operator in {'==', '==='}) + + @property + def installed_version(self): + # type: () -> Optional[str] + return get_installed_version(self.name) + + def match_markers(self, extras_requested=None): + # type: (Optional[Iterable[str]]) -> bool + if not extras_requested: + # Provide an extra to safely evaluate the markers + # without matching any extra + extras_requested = ('',) + if self.markers is not None: + return any( + self.markers.evaluate({'extra': extra}) + for extra in extras_requested) + else: + return True + + @property + def has_hash_options(self): + # type: () -> bool + """Return whether any known-good hashes are specified as options. + + These activate --require-hashes mode; hashes specified as part of a + URL do not. + + """ + return bool(self.hash_options) + + def hashes(self, trust_internet=True): + # type: (bool) -> Hashes + """Return a hash-comparer that considers my option- and URL-based + hashes to be known-good. + + Hashes in URLs--ones embedded in the requirements file, not ones + downloaded from an index server--are almost peers with ones from + flags. They satisfy --require-hashes (whether it was implicitly or + explicitly activated) but do not activate it. md5 and sha224 are not + allowed in flags, which should nudge people toward good algos. We + always OR all hashes together, even ones from URLs. + + :param trust_internet: Whether to trust URL-based (#md5=...) hashes + downloaded from the internet, as by populate_link() + + """ + good_hashes = self.hash_options.copy() + link = self.link if trust_internet else self.original_link + if link and link.hash: + good_hashes.setdefault(link.hash_name, []).append(link.hash) + return Hashes(good_hashes) + + def from_path(self): + # type: () -> Optional[str] + """Format a nice indicator to show where this "comes from" + """ + if self.req is None: + return None + s = str(self.req) + if self.comes_from: + if isinstance(self.comes_from, six.string_types): + comes_from = self.comes_from + else: + comes_from = self.comes_from.from_path() + if comes_from: + s += '->' + comes_from + return s + + def ensure_build_location(self, build_dir, autodelete, parallel_builds): + # type: (str, bool, bool) -> str + assert build_dir is not None + if self._temp_build_dir is not None: + assert self._temp_build_dir.path + return self._temp_build_dir.path + if self.req is None: + # Some systems have /tmp as a symlink which confuses custom + # builds (such as numpy). Thus, we ensure that the real path + # is returned. + self._temp_build_dir = TempDirectory( + kind=tempdir_kinds.REQ_BUILD, globally_managed=True + ) + + return self._temp_build_dir.path + + # This is the only remaining place where we manually determine the path + # for the temporary directory. It is only needed for editables where + # it is the value of the --src option. + + # When parallel builds are enabled, add a UUID to the build directory + # name so multiple builds do not interfere with each other. + dir_name = canonicalize_name(self.name) + if parallel_builds: + dir_name = "{}_{}".format(dir_name, uuid.uuid4().hex) + + # FIXME: Is there a better place to create the build_dir? (hg and bzr + # need this) + if not os.path.exists(build_dir): + logger.debug('Creating directory %s', build_dir) + os.makedirs(build_dir) + actual_build_dir = os.path.join(build_dir, dir_name) + # `None` indicates that we respect the globally-configured deletion + # settings, which is what we actually want when auto-deleting. + delete_arg = None if autodelete else False + return TempDirectory( + path=actual_build_dir, + delete=delete_arg, + kind=tempdir_kinds.REQ_BUILD, + globally_managed=True, + ).path + + def _set_requirement(self): + # type: () -> None + """Set requirement after generating metadata. + """ + assert self.req is None + assert self.metadata is not None + assert self.source_dir is not None + + # Construct a Requirement object from the generated metadata + if isinstance(parse_version(self.metadata["Version"]), Version): + op = "==" + else: + op = "===" + + self.req = Requirement( + "".join([ + self.metadata["Name"], + op, + self.metadata["Version"], + ]) + ) + + def warn_on_mismatching_name(self): + # type: () -> None + metadata_name = canonicalize_name(self.metadata["Name"]) + if canonicalize_name(self.req.name) == metadata_name: + # Everything is fine. + return + + # If we're here, there's a mismatch. Log a warning about it. + logger.warning( + 'Generating metadata for package %s ' + 'produced metadata for project name %s. Fix your ' + '#egg=%s fragments.', + self.name, metadata_name, self.name + ) + self.req = Requirement(metadata_name) + + def check_if_exists(self, use_user_site): + # type: (bool) -> None + """Find an installed distribution that satisfies or conflicts + with this requirement, and set self.satisfied_by or + self.should_reinstall appropriately. + """ + if self.req is None: + return + existing_dist = get_distribution(self.req.name) + if not existing_dist: + return + + # pkg_resouces may contain a different copy of packaging.version from + # pip in if the downstream distributor does a poor job debundling pip. + # We avoid existing_dist.parsed_version and let SpecifierSet.contains + # parses the version instead. + existing_version = existing_dist.version + version_compatible = ( + existing_version is not None and + self.req.specifier.contains(existing_version, prereleases=True) + ) + if not version_compatible: + self.satisfied_by = None + if use_user_site: + if dist_in_usersite(existing_dist): + self.should_reinstall = True + elif (running_under_virtualenv() and + dist_in_site_packages(existing_dist)): + raise InstallationError( + "Will not install to the user site because it will " + "lack sys.path precedence to {} in {}".format( + existing_dist.project_name, existing_dist.location) + ) + else: + self.should_reinstall = True + else: + if self.editable: + self.should_reinstall = True + # when installing editables, nothing pre-existing should ever + # satisfy + self.satisfied_by = None + else: + self.satisfied_by = existing_dist + + # Things valid for wheels + @property + def is_wheel(self): + # type: () -> bool + if not self.link: + return False + return self.link.is_wheel + + # Things valid for sdists + @property + def unpacked_source_directory(self): + # type: () -> str + return os.path.join( + self.source_dir, + self.link and self.link.subdirectory_fragment or '') + + @property + def setup_py_path(self): + # type: () -> str + assert self.source_dir, "No source dir for {}".format(self) + setup_py = os.path.join(self.unpacked_source_directory, 'setup.py') + + # Python2 __file__ should not be unicode + if six.PY2 and isinstance(setup_py, six.text_type): + setup_py = setup_py.encode(sys.getfilesystemencoding()) + + return setup_py + + @property + def pyproject_toml_path(self): + # type: () -> str + assert self.source_dir, "No source dir for {}".format(self) + return make_pyproject_path(self.unpacked_source_directory) + + def load_pyproject_toml(self): + # type: () -> None + """Load the pyproject.toml file. + + After calling this routine, all of the attributes related to PEP 517 + processing for this requirement have been set. In particular, the + use_pep517 attribute can be used to determine whether we should + follow the PEP 517 or legacy (setup.py) code path. + """ + pyproject_toml_data = load_pyproject_toml( + self.use_pep517, + self.pyproject_toml_path, + self.setup_py_path, + str(self) + ) + + if pyproject_toml_data is None: + self.use_pep517 = False + return + + self.use_pep517 = True + requires, backend, check, backend_path = pyproject_toml_data + self.requirements_to_check = check + self.pyproject_requires = requires + self.pep517_backend = Pep517HookCaller( + self.unpacked_source_directory, backend, backend_path=backend_path, + ) + + def _generate_metadata(self): + # type: () -> str + """Invokes metadata generator functions, with the required arguments. + """ + if not self.use_pep517: + assert self.unpacked_source_directory + + return generate_metadata_legacy( + build_env=self.build_env, + setup_py_path=self.setup_py_path, + source_dir=self.unpacked_source_directory, + isolated=self.isolated, + details=self.name or "from {}".format(self.link) + ) + + assert self.pep517_backend is not None + + return generate_metadata( + build_env=self.build_env, + backend=self.pep517_backend, + ) + + def prepare_metadata(self): + # type: () -> None + """Ensure that project metadata is available. + + Under PEP 517, call the backend hook to prepare the metadata. + Under legacy processing, call setup.py egg-info. + """ + assert self.source_dir + + with indent_log(): + self.metadata_directory = self._generate_metadata() + + # Act on the newly generated metadata, based on the name and version. + if not self.name: + self._set_requirement() + else: + self.warn_on_mismatching_name() + + self.assert_source_matches_version() + + @property + def metadata(self): + # type: () -> Any + if not hasattr(self, '_metadata'): + self._metadata = get_metadata(self.get_dist()) + + return self._metadata + + def get_dist(self): + # type: () -> Distribution + return _get_dist(self.metadata_directory) + + def assert_source_matches_version(self): + # type: () -> None + assert self.source_dir + version = self.metadata['version'] + if self.req.specifier and version not in self.req.specifier: + logger.warning( + 'Requested %s, but installing version %s', + self, + version, + ) + else: + logger.debug( + 'Source in %s has version %s, which satisfies requirement %s', + display_path(self.source_dir), + version, + self, + ) + + # For both source distributions and editables + def ensure_has_source_dir( + self, + parent_dir, + autodelete=False, + parallel_builds=False, + ): + # type: (str, bool, bool) -> None + """Ensure that a source_dir is set. + + This will create a temporary build dir if the name of the requirement + isn't known yet. + + :param parent_dir: The ideal pip parent_dir for the source_dir. + Generally src_dir for editables and build_dir for sdists. + :return: self.source_dir + """ + if self.source_dir is None: + self.source_dir = self.ensure_build_location( + parent_dir, + autodelete=autodelete, + parallel_builds=parallel_builds, + ) + + # For editable installations + def update_editable(self, obtain=True): + # type: (bool) -> None + if not self.link: + logger.debug( + "Cannot update repository at %s; repository location is " + "unknown", + self.source_dir, + ) + return + assert self.editable + assert self.source_dir + if self.link.scheme == 'file': + # Static paths don't get updated + return + assert '+' in self.link.url, \ + "bad url: {self.link.url!r}".format(**locals()) + vc_type, url = self.link.url.split('+', 1) + vcs_backend = vcs.get_backend(vc_type) + if vcs_backend: + if not self.link.is_vcs: + reason = ( + "This form of VCS requirement is being deprecated: {}." + ).format( + self.link.url + ) + replacement = None + if self.link.url.startswith("git+git@"): + replacement = ( + "git+https://git@example.com/..., " + "git+ssh://git@example.com/..., " + "or the insecure git+git://git@example.com/..." + ) + deprecated(reason, replacement, gone_in="21.0", issue=7554) + hidden_url = hide_url(self.link.url) + if obtain: + vcs_backend.obtain(self.source_dir, url=hidden_url) + else: + vcs_backend.export(self.source_dir, url=hidden_url) + else: + assert 0, ( + 'Unexpected version control type (in {}): {}'.format( + self.link, vc_type)) + + # Top-level Actions + def uninstall(self, auto_confirm=False, verbose=False): + # type: (bool, bool) -> Optional[UninstallPathSet] + """ + Uninstall the distribution currently satisfying this requirement. + + Prompts before removing or modifying files unless + ``auto_confirm`` is True. + + Refuses to delete or modify files outside of ``sys.prefix`` - + thus uninstallation within a virtual environment can only + modify that virtual environment, even if the virtualenv is + linked to global site-packages. + + """ + assert self.req + dist = get_distribution(self.req.name) + if not dist: + logger.warning("Skipping %s as it is not installed.", self.name) + return None + logger.info('Found existing installation: %s', dist) + + uninstalled_pathset = UninstallPathSet.from_dist(dist) + uninstalled_pathset.remove(auto_confirm, verbose) + return uninstalled_pathset + + def _get_archive_name(self, path, parentdir, rootdir): + # type: (str, str, str) -> str + + def _clean_zip_name(name, prefix): + # type: (str, str) -> str + assert name.startswith(prefix + os.path.sep), ( + "name {name!r} doesn't start with prefix {prefix!r}" + .format(**locals()) + ) + name = name[len(prefix) + 1:] + name = name.replace(os.path.sep, '/') + return name + + path = os.path.join(parentdir, path) + name = _clean_zip_name(path, rootdir) + return self.name + '/' + name + + def archive(self, build_dir): + # type: (Optional[str]) -> None + """Saves archive to provided build_dir. + + Used for saving downloaded VCS requirements as part of `pip download`. + """ + assert self.source_dir + if build_dir is None: + return + + create_archive = True + archive_name = '{}-{}.zip'.format(self.name, self.metadata["version"]) + archive_path = os.path.join(build_dir, archive_name) + + if os.path.exists(archive_path): + response = ask_path_exists( + 'The file {} exists. (i)gnore, (w)ipe, ' + '(b)ackup, (a)bort '.format( + display_path(archive_path)), + ('i', 'w', 'b', 'a')) + if response == 'i': + create_archive = False + elif response == 'w': + logger.warning('Deleting %s', display_path(archive_path)) + os.remove(archive_path) + elif response == 'b': + dest_file = backup_dir(archive_path) + logger.warning( + 'Backing up %s to %s', + display_path(archive_path), + display_path(dest_file), + ) + shutil.move(archive_path, dest_file) + elif response == 'a': + sys.exit(-1) + + if not create_archive: + return + + zip_output = zipfile.ZipFile( + archive_path, 'w', zipfile.ZIP_DEFLATED, allowZip64=True, + ) + with zip_output: + dir = os.path.normcase( + os.path.abspath(self.unpacked_source_directory) + ) + for dirpath, dirnames, filenames in os.walk(dir): + for dirname in dirnames: + dir_arcname = self._get_archive_name( + dirname, parentdir=dirpath, rootdir=dir, + ) + zipdir = zipfile.ZipInfo(dir_arcname + '/') + zipdir.external_attr = 0x1ED << 16 # 0o755 + zip_output.writestr(zipdir, '') + for filename in filenames: + file_arcname = self._get_archive_name( + filename, parentdir=dirpath, rootdir=dir, + ) + filename = os.path.join(dirpath, filename) + zip_output.write(filename, file_arcname) + + logger.info('Saved %s', display_path(archive_path)) + + def install( + self, + install_options, # type: List[str] + global_options=None, # type: Optional[Sequence[str]] + root=None, # type: Optional[str] + home=None, # type: Optional[str] + prefix=None, # type: Optional[str] + warn_script_location=True, # type: bool + use_user_site=False, # type: bool + pycompile=True # type: bool + ): + # type: (...) -> None + scheme = get_scheme( + self.name, + user=use_user_site, + home=home, + root=root, + isolated=self.isolated, + prefix=prefix, + ) + + global_options = global_options if global_options is not None else [] + if self.editable: + install_editable_legacy( + install_options, + global_options, + prefix=prefix, + home=home, + use_user_site=use_user_site, + name=self.name, + setup_py_path=self.setup_py_path, + isolated=self.isolated, + build_env=self.build_env, + unpacked_source_directory=self.unpacked_source_directory, + ) + self.install_succeeded = True + return + + if self.is_wheel: + assert self.local_file_path + direct_url = None + if self.original_link: + direct_url = direct_url_from_link( + self.original_link, + self.source_dir, + self.original_link_is_in_wheel_cache, + ) + install_wheel( + self.name, + self.local_file_path, + scheme=scheme, + req_description=str(self.req), + pycompile=pycompile, + warn_script_location=warn_script_location, + direct_url=direct_url, + requested=self.user_supplied, + ) + self.install_succeeded = True + return + + # TODO: Why don't we do this for editable installs? + + # Extend the list of global and install options passed on to + # the setup.py call with the ones from the requirements file. + # Options specified in requirements file override those + # specified on the command line, since the last option given + # to setup.py is the one that is used. + global_options = list(global_options) + self.global_options + install_options = list(install_options) + self.install_options + + try: + success = install_legacy( + install_options=install_options, + global_options=global_options, + root=root, + home=home, + prefix=prefix, + use_user_site=use_user_site, + pycompile=pycompile, + scheme=scheme, + setup_py_path=self.setup_py_path, + isolated=self.isolated, + req_name=self.name, + build_env=self.build_env, + unpacked_source_directory=self.unpacked_source_directory, + req_description=str(self.req), + ) + except LegacyInstallFailure as exc: + self.install_succeeded = False + six.reraise(*exc.parent) + except Exception: + self.install_succeeded = True + raise + + self.install_succeeded = success + + if success and self.legacy_install_reason == 8368: + deprecated( + reason=( + "{} was installed using the legacy 'setup.py install' " + "method, because a wheel could not be built for it.". + format(self.name) + ), + replacement="to fix the wheel build issue reported above", + gone_in="21.0", + issue=8368, + ) + + +def check_invalid_constraint_type(req): + # type: (InstallRequirement) -> str + + # Check for unsupported forms + problem = "" + if not req.name: + problem = "Unnamed requirements are not allowed as constraints" + elif req.link: + problem = "Links are not allowed as constraints" + elif req.extras: + problem = "Constraints cannot have extras" + + if problem: + deprecated( + reason=( + "Constraints are only allowed to take the form of a package " + "name and a version specifier. Other forms were originally " + "permitted as an accident of the implementation, but were " + "undocumented. The new implementation of the resolver no " + "longer supports these forms." + ), + replacement=( + "replacing the constraint with a requirement." + ), + # No plan yet for when the new resolver becomes default + gone_in=None, + issue=8210 + ) + + return problem diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/req/req_set.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/req/req_set.py new file mode 100644 index 0000000000..c9ea3be5dd --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/req/req_set.py @@ -0,0 +1,204 @@ +from __future__ import absolute_import + +import logging +from collections import OrderedDict + +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.exceptions import InstallationError +from pip._internal.models.wheel import Wheel +from pip._internal.utils import compatibility_tags +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Dict, Iterable, List, Optional, Tuple + + from pip._internal.req.req_install import InstallRequirement + + +logger = logging.getLogger(__name__) + + +class RequirementSet(object): + + def __init__(self, check_supported_wheels=True): + # type: (bool) -> None + """Create a RequirementSet. + """ + + self.requirements = OrderedDict() # type: Dict[str, InstallRequirement] # noqa: E501 + self.check_supported_wheels = check_supported_wheels + + self.unnamed_requirements = [] # type: List[InstallRequirement] + + def __str__(self): + # type: () -> str + requirements = sorted( + (req for req in self.requirements.values() if not req.comes_from), + key=lambda req: canonicalize_name(req.name), + ) + return ' '.join(str(req.req) for req in requirements) + + def __repr__(self): + # type: () -> str + requirements = sorted( + self.requirements.values(), + key=lambda req: canonicalize_name(req.name), + ) + + format_string = '<{classname} object; {count} requirement(s): {reqs}>' + return format_string.format( + classname=self.__class__.__name__, + count=len(requirements), + reqs=', '.join(str(req.req) for req in requirements), + ) + + def add_unnamed_requirement(self, install_req): + # type: (InstallRequirement) -> None + assert not install_req.name + self.unnamed_requirements.append(install_req) + + def add_named_requirement(self, install_req): + # type: (InstallRequirement) -> None + assert install_req.name + + project_name = canonicalize_name(install_req.name) + self.requirements[project_name] = install_req + + def add_requirement( + self, + install_req, # type: InstallRequirement + parent_req_name=None, # type: Optional[str] + extras_requested=None # type: Optional[Iterable[str]] + ): + # type: (...) -> Tuple[List[InstallRequirement], Optional[InstallRequirement]] # noqa: E501 + """Add install_req as a requirement to install. + + :param parent_req_name: The name of the requirement that needed this + added. The name is used because when multiple unnamed requirements + resolve to the same name, we could otherwise end up with dependency + links that point outside the Requirements set. parent_req must + already be added. Note that None implies that this is a user + supplied requirement, vs an inferred one. + :param extras_requested: an iterable of extras used to evaluate the + environment markers. + :return: Additional requirements to scan. That is either [] if + the requirement is not applicable, or [install_req] if the + requirement is applicable and has just been added. + """ + # If the markers do not match, ignore this requirement. + if not install_req.match_markers(extras_requested): + logger.info( + "Ignoring %s: markers '%s' don't match your environment", + install_req.name, install_req.markers, + ) + return [], None + + # If the wheel is not supported, raise an error. + # Should check this after filtering out based on environment markers to + # allow specifying different wheels based on the environment/OS, in a + # single requirements file. + if install_req.link and install_req.link.is_wheel: + wheel = Wheel(install_req.link.filename) + tags = compatibility_tags.get_supported() + if (self.check_supported_wheels and not wheel.supported(tags)): + raise InstallationError( + "{} is not a supported wheel on this platform.".format( + wheel.filename) + ) + + # This next bit is really a sanity check. + assert not install_req.user_supplied or parent_req_name is None, ( + "a user supplied req shouldn't have a parent" + ) + + # Unnamed requirements are scanned again and the requirement won't be + # added as a dependency until after scanning. + if not install_req.name: + self.add_unnamed_requirement(install_req) + return [install_req], None + + try: + existing_req = self.get_requirement( + install_req.name) # type: Optional[InstallRequirement] + except KeyError: + existing_req = None + + has_conflicting_requirement = ( + parent_req_name is None and + existing_req and + not existing_req.constraint and + existing_req.extras == install_req.extras and + existing_req.req.specifier != install_req.req.specifier + ) + if has_conflicting_requirement: + raise InstallationError( + "Double requirement given: {} (already in {}, name={!r})" + .format(install_req, existing_req, install_req.name) + ) + + # When no existing requirement exists, add the requirement as a + # dependency and it will be scanned again after. + if not existing_req: + self.add_named_requirement(install_req) + # We'd want to rescan this requirement later + return [install_req], install_req + + # Assume there's no need to scan, and that we've already + # encountered this for scanning. + if install_req.constraint or not existing_req.constraint: + return [], existing_req + + does_not_satisfy_constraint = ( + install_req.link and + not ( + existing_req.link and + install_req.link.path == existing_req.link.path + ) + ) + if does_not_satisfy_constraint: + raise InstallationError( + "Could not satisfy constraints for '{}': " + "installation from path or url cannot be " + "constrained to a version".format(install_req.name) + ) + # If we're now installing a constraint, mark the existing + # object for real installation. + existing_req.constraint = False + # If we're now installing a user supplied requirement, + # mark the existing object as such. + if install_req.user_supplied: + existing_req.user_supplied = True + existing_req.extras = tuple(sorted( + set(existing_req.extras) | set(install_req.extras) + )) + logger.debug( + "Setting %s extras to: %s", + existing_req, existing_req.extras, + ) + # Return the existing requirement for addition to the parent and + # scanning again. + return [existing_req], existing_req + + def has_requirement(self, name): + # type: (str) -> bool + project_name = canonicalize_name(name) + + return ( + project_name in self.requirements and + not self.requirements[project_name].constraint + ) + + def get_requirement(self, name): + # type: (str) -> InstallRequirement + project_name = canonicalize_name(name) + + if project_name in self.requirements: + return self.requirements[project_name] + + raise KeyError("No project with the name {name!r}".format(**locals())) + + @property + def all_requirements(self): + # type: () -> List[InstallRequirement] + return self.unnamed_requirements + list(self.requirements.values()) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/req/req_tracker.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/req/req_tracker.py new file mode 100644 index 0000000000..7379c307b3 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/req/req_tracker.py @@ -0,0 +1,151 @@ +from __future__ import absolute_import + +import contextlib +import errno +import hashlib +import logging +import os + +from pip._vendor import contextlib2 + +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from types import TracebackType + from typing import Dict, Iterator, Optional, Set, Type, Union + + from pip._internal.models.link import Link + from pip._internal.req.req_install import InstallRequirement + +logger = logging.getLogger(__name__) + + +@contextlib.contextmanager +def update_env_context_manager(**changes): + # type: (str) -> Iterator[None] + target = os.environ + + # Save values from the target and change them. + non_existent_marker = object() + saved_values = {} # type: Dict[str, Union[object, str]] + for name, new_value in changes.items(): + try: + saved_values[name] = target[name] + except KeyError: + saved_values[name] = non_existent_marker + target[name] = new_value + + try: + yield + finally: + # Restore original values in the target. + for name, original_value in saved_values.items(): + if original_value is non_existent_marker: + del target[name] + else: + assert isinstance(original_value, str) # for mypy + target[name] = original_value + + +@contextlib.contextmanager +def get_requirement_tracker(): + # type: () -> Iterator[RequirementTracker] + root = os.environ.get('PIP_REQ_TRACKER') + with contextlib2.ExitStack() as ctx: + if root is None: + root = ctx.enter_context( + TempDirectory(kind='req-tracker') + ).path + ctx.enter_context(update_env_context_manager(PIP_REQ_TRACKER=root)) + logger.debug("Initialized build tracking at %s", root) + + with RequirementTracker(root) as tracker: + yield tracker + + +class RequirementTracker(object): + + def __init__(self, root): + # type: (str) -> None + self._root = root + self._entries = set() # type: Set[InstallRequirement] + logger.debug("Created build tracker: %s", self._root) + + def __enter__(self): + # type: () -> RequirementTracker + logger.debug("Entered build tracker: %s", self._root) + return self + + def __exit__( + self, + exc_type, # type: Optional[Type[BaseException]] + exc_val, # type: Optional[BaseException] + exc_tb # type: Optional[TracebackType] + ): + # type: (...) -> None + self.cleanup() + + def _entry_path(self, link): + # type: (Link) -> str + hashed = hashlib.sha224(link.url_without_fragment.encode()).hexdigest() + return os.path.join(self._root, hashed) + + def add(self, req): + # type: (InstallRequirement) -> None + """Add an InstallRequirement to build tracking. + """ + + assert req.link + # Get the file to write information about this requirement. + entry_path = self._entry_path(req.link) + + # Try reading from the file. If it exists and can be read from, a build + # is already in progress, so a LookupError is raised. + try: + with open(entry_path) as fp: + contents = fp.read() + except IOError as e: + # if the error is anything other than "file does not exist", raise. + if e.errno != errno.ENOENT: + raise + else: + message = '{} is already being built: {}'.format( + req.link, contents) + raise LookupError(message) + + # If we're here, req should really not be building already. + assert req not in self._entries + + # Start tracking this requirement. + with open(entry_path, 'w') as fp: + fp.write(str(req)) + self._entries.add(req) + + logger.debug('Added %s to build tracker %r', req, self._root) + + def remove(self, req): + # type: (InstallRequirement) -> None + """Remove an InstallRequirement from build tracking. + """ + + assert req.link + # Delete the created file and the corresponding entries. + os.unlink(self._entry_path(req.link)) + self._entries.remove(req) + + logger.debug('Removed %s from build tracker %r', req, self._root) + + def cleanup(self): + # type: () -> None + for req in set(self._entries): + self.remove(req) + + logger.debug("Removed build tracker: %r", self._root) + + @contextlib.contextmanager + def track(self, req): + # type: (InstallRequirement) -> Iterator[None] + self.add(req) + yield + self.remove(req) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/req/req_uninstall.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/req/req_uninstall.py new file mode 100644 index 0000000000..2e7dfcc736 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/req/req_uninstall.py @@ -0,0 +1,657 @@ +from __future__ import absolute_import + +import csv +import functools +import logging +import os +import sys +import sysconfig + +from pip._vendor import pkg_resources + +from pip._internal.exceptions import UninstallationError +from pip._internal.locations import bin_py, bin_user +from pip._internal.utils.compat import WINDOWS, cache_from_source, uses_pycache +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import ( + FakeFile, + ask, + dist_in_usersite, + dist_is_local, + egg_link_path, + is_local, + normalize_path, + renames, + rmtree, +) +from pip._internal.utils.temp_dir import AdjacentTempDirectory, TempDirectory +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import ( + Any, + Callable, + Dict, + Iterable, + Iterator, + List, + Optional, + Set, + Tuple, + ) + + from pip._vendor.pkg_resources import Distribution + +logger = logging.getLogger(__name__) + + +def _script_names(dist, script_name, is_gui): + # type: (Distribution, str, bool) -> List[str] + """Create the fully qualified name of the files created by + {console,gui}_scripts for the given ``dist``. + Returns the list of file names + """ + if dist_in_usersite(dist): + bin_dir = bin_user + else: + bin_dir = bin_py + exe_name = os.path.join(bin_dir, script_name) + paths_to_remove = [exe_name] + if WINDOWS: + paths_to_remove.append(exe_name + '.exe') + paths_to_remove.append(exe_name + '.exe.manifest') + if is_gui: + paths_to_remove.append(exe_name + '-script.pyw') + else: + paths_to_remove.append(exe_name + '-script.py') + return paths_to_remove + + +def _unique(fn): + # type: (Callable[..., Iterator[Any]]) -> Callable[..., Iterator[Any]] + @functools.wraps(fn) + def unique(*args, **kw): + # type: (Any, Any) -> Iterator[Any] + seen = set() # type: Set[Any] + for item in fn(*args, **kw): + if item not in seen: + seen.add(item) + yield item + return unique + + +@_unique +def uninstallation_paths(dist): + # type: (Distribution) -> Iterator[str] + """ + Yield all the uninstallation paths for dist based on RECORD-without-.py[co] + + Yield paths to all the files in RECORD. For each .py file in RECORD, add + the .pyc and .pyo in the same directory. + + UninstallPathSet.add() takes care of the __pycache__ .py[co]. + """ + r = csv.reader(FakeFile(dist.get_metadata_lines('RECORD'))) + for row in r: + path = os.path.join(dist.location, row[0]) + yield path + if path.endswith('.py'): + dn, fn = os.path.split(path) + base = fn[:-3] + path = os.path.join(dn, base + '.pyc') + yield path + path = os.path.join(dn, base + '.pyo') + yield path + + +def compact(paths): + # type: (Iterable[str]) -> Set[str] + """Compact a path set to contain the minimal number of paths + necessary to contain all paths in the set. If /a/path/ and + /a/path/to/a/file.txt are both in the set, leave only the + shorter path.""" + + sep = os.path.sep + short_paths = set() # type: Set[str] + for path in sorted(paths, key=len): + should_skip = any( + path.startswith(shortpath.rstrip("*")) and + path[len(shortpath.rstrip("*").rstrip(sep))] == sep + for shortpath in short_paths + ) + if not should_skip: + short_paths.add(path) + return short_paths + + +def compress_for_rename(paths): + # type: (Iterable[str]) -> Set[str] + """Returns a set containing the paths that need to be renamed. + + This set may include directories when the original sequence of paths + included every file on disk. + """ + case_map = dict((os.path.normcase(p), p) for p in paths) + remaining = set(case_map) + unchecked = sorted(set(os.path.split(p)[0] + for p in case_map.values()), key=len) + wildcards = set() # type: Set[str] + + def norm_join(*a): + # type: (str) -> str + return os.path.normcase(os.path.join(*a)) + + for root in unchecked: + if any(os.path.normcase(root).startswith(w) + for w in wildcards): + # This directory has already been handled. + continue + + all_files = set() # type: Set[str] + all_subdirs = set() # type: Set[str] + for dirname, subdirs, files in os.walk(root): + all_subdirs.update(norm_join(root, dirname, d) + for d in subdirs) + all_files.update(norm_join(root, dirname, f) + for f in files) + # If all the files we found are in our remaining set of files to + # remove, then remove them from the latter set and add a wildcard + # for the directory. + if not (all_files - remaining): + remaining.difference_update(all_files) + wildcards.add(root + os.sep) + + return set(map(case_map.__getitem__, remaining)) | wildcards + + +def compress_for_output_listing(paths): + # type: (Iterable[str]) -> Tuple[Set[str], Set[str]] + """Returns a tuple of 2 sets of which paths to display to user + + The first set contains paths that would be deleted. Files of a package + are not added and the top-level directory of the package has a '*' added + at the end - to signify that all it's contents are removed. + + The second set contains files that would have been skipped in the above + folders. + """ + + will_remove = set(paths) + will_skip = set() + + # Determine folders and files + folders = set() + files = set() + for path in will_remove: + if path.endswith(".pyc"): + continue + if path.endswith("__init__.py") or ".dist-info" in path: + folders.add(os.path.dirname(path)) + files.add(path) + + # probably this one https://github.com/python/mypy/issues/390 + _normcased_files = set(map(os.path.normcase, files)) # type: ignore + + folders = compact(folders) + + # This walks the tree using os.walk to not miss extra folders + # that might get added. + for folder in folders: + for dirpath, _, dirfiles in os.walk(folder): + for fname in dirfiles: + if fname.endswith(".pyc"): + continue + + file_ = os.path.join(dirpath, fname) + if (os.path.isfile(file_) and + os.path.normcase(file_) not in _normcased_files): + # We are skipping this file. Add it to the set. + will_skip.add(file_) + + will_remove = files | { + os.path.join(folder, "*") for folder in folders + } + + return will_remove, will_skip + + +class StashedUninstallPathSet(object): + """A set of file rename operations to stash files while + tentatively uninstalling them.""" + def __init__(self): + # type: () -> None + # Mapping from source file root to [Adjacent]TempDirectory + # for files under that directory. + self._save_dirs = {} # type: Dict[str, TempDirectory] + # (old path, new path) tuples for each move that may need + # to be undone. + self._moves = [] # type: List[Tuple[str, str]] + + def _get_directory_stash(self, path): + # type: (str) -> str + """Stashes a directory. + + Directories are stashed adjacent to their original location if + possible, or else moved/copied into the user's temp dir.""" + + try: + save_dir = AdjacentTempDirectory(path) # type: TempDirectory + except OSError: + save_dir = TempDirectory(kind="uninstall") + self._save_dirs[os.path.normcase(path)] = save_dir + + return save_dir.path + + def _get_file_stash(self, path): + # type: (str) -> str + """Stashes a file. + + If no root has been provided, one will be created for the directory + in the user's temp directory.""" + path = os.path.normcase(path) + head, old_head = os.path.dirname(path), None + save_dir = None + + while head != old_head: + try: + save_dir = self._save_dirs[head] + break + except KeyError: + pass + head, old_head = os.path.dirname(head), head + else: + # Did not find any suitable root + head = os.path.dirname(path) + save_dir = TempDirectory(kind='uninstall') + self._save_dirs[head] = save_dir + + relpath = os.path.relpath(path, head) + if relpath and relpath != os.path.curdir: + return os.path.join(save_dir.path, relpath) + return save_dir.path + + def stash(self, path): + # type: (str) -> str + """Stashes the directory or file and returns its new location. + Handle symlinks as files to avoid modifying the symlink targets. + """ + path_is_dir = os.path.isdir(path) and not os.path.islink(path) + if path_is_dir: + new_path = self._get_directory_stash(path) + else: + new_path = self._get_file_stash(path) + + self._moves.append((path, new_path)) + if (path_is_dir and os.path.isdir(new_path)): + # If we're moving a directory, we need to + # remove the destination first or else it will be + # moved to inside the existing directory. + # We just created new_path ourselves, so it will + # be removable. + os.rmdir(new_path) + renames(path, new_path) + return new_path + + def commit(self): + # type: () -> None + """Commits the uninstall by removing stashed files.""" + for _, save_dir in self._save_dirs.items(): + save_dir.cleanup() + self._moves = [] + self._save_dirs = {} + + def rollback(self): + # type: () -> None + """Undoes the uninstall by moving stashed files back.""" + for p in self._moves: + logger.info("Moving to %s\n from %s", *p) + + for new_path, path in self._moves: + try: + logger.debug('Replacing %s from %s', new_path, path) + if os.path.isfile(new_path) or os.path.islink(new_path): + os.unlink(new_path) + elif os.path.isdir(new_path): + rmtree(new_path) + renames(path, new_path) + except OSError as ex: + logger.error("Failed to restore %s", new_path) + logger.debug("Exception: %s", ex) + + self.commit() + + @property + def can_rollback(self): + # type: () -> bool + return bool(self._moves) + + +class UninstallPathSet(object): + """A set of file paths to be removed in the uninstallation of a + requirement.""" + def __init__(self, dist): + # type: (Distribution) -> None + self.paths = set() # type: Set[str] + self._refuse = set() # type: Set[str] + self.pth = {} # type: Dict[str, UninstallPthEntries] + self.dist = dist + self._moved_paths = StashedUninstallPathSet() + + def _permitted(self, path): + # type: (str) -> bool + """ + Return True if the given path is one we are permitted to + remove/modify, False otherwise. + + """ + return is_local(path) + + def add(self, path): + # type: (str) -> None + head, tail = os.path.split(path) + + # we normalize the head to resolve parent directory symlinks, but not + # the tail, since we only want to uninstall symlinks, not their targets + path = os.path.join(normalize_path(head), os.path.normcase(tail)) + + if not os.path.exists(path): + return + if self._permitted(path): + self.paths.add(path) + else: + self._refuse.add(path) + + # __pycache__ files can show up after 'installed-files.txt' is created, + # due to imports + if os.path.splitext(path)[1] == '.py' and uses_pycache: + self.add(cache_from_source(path)) + + def add_pth(self, pth_file, entry): + # type: (str, str) -> None + pth_file = normalize_path(pth_file) + if self._permitted(pth_file): + if pth_file not in self.pth: + self.pth[pth_file] = UninstallPthEntries(pth_file) + self.pth[pth_file].add(entry) + else: + self._refuse.add(pth_file) + + def remove(self, auto_confirm=False, verbose=False): + # type: (bool, bool) -> None + """Remove paths in ``self.paths`` with confirmation (unless + ``auto_confirm`` is True).""" + + if not self.paths: + logger.info( + "Can't uninstall '%s'. No files were found to uninstall.", + self.dist.project_name, + ) + return + + dist_name_version = ( + self.dist.project_name + "-" + self.dist.version + ) + logger.info('Uninstalling %s:', dist_name_version) + + with indent_log(): + if auto_confirm or self._allowed_to_proceed(verbose): + moved = self._moved_paths + + for_rename = compress_for_rename(self.paths) + + for path in sorted(compact(for_rename)): + moved.stash(path) + logger.debug('Removing file or directory %s', path) + + for pth in self.pth.values(): + pth.remove() + + logger.info('Successfully uninstalled %s', dist_name_version) + + def _allowed_to_proceed(self, verbose): + # type: (bool) -> bool + """Display which files would be deleted and prompt for confirmation + """ + + def _display(msg, paths): + # type: (str, Iterable[str]) -> None + if not paths: + return + + logger.info(msg) + with indent_log(): + for path in sorted(compact(paths)): + logger.info(path) + + if not verbose: + will_remove, will_skip = compress_for_output_listing(self.paths) + else: + # In verbose mode, display all the files that are going to be + # deleted. + will_remove = set(self.paths) + will_skip = set() + + _display('Would remove:', will_remove) + _display('Would not remove (might be manually added):', will_skip) + _display('Would not remove (outside of prefix):', self._refuse) + if verbose: + _display('Will actually move:', compress_for_rename(self.paths)) + + return ask('Proceed (y/n)? ', ('y', 'n')) == 'y' + + def rollback(self): + # type: () -> None + """Rollback the changes previously made by remove().""" + if not self._moved_paths.can_rollback: + logger.error( + "Can't roll back %s; was not uninstalled", + self.dist.project_name, + ) + return + logger.info('Rolling back uninstall of %s', self.dist.project_name) + self._moved_paths.rollback() + for pth in self.pth.values(): + pth.rollback() + + def commit(self): + # type: () -> None + """Remove temporary save dir: rollback will no longer be possible.""" + self._moved_paths.commit() + + @classmethod + def from_dist(cls, dist): + # type: (Distribution) -> UninstallPathSet + dist_path = normalize_path(dist.location) + if not dist_is_local(dist): + logger.info( + "Not uninstalling %s at %s, outside environment %s", + dist.key, + dist_path, + sys.prefix, + ) + return cls(dist) + + if dist_path in {p for p in {sysconfig.get_path("stdlib"), + sysconfig.get_path("platstdlib")} + if p}: + logger.info( + "Not uninstalling %s at %s, as it is in the standard library.", + dist.key, + dist_path, + ) + return cls(dist) + + paths_to_remove = cls(dist) + develop_egg_link = egg_link_path(dist) + develop_egg_link_egg_info = '{}.egg-info'.format( + pkg_resources.to_filename(dist.project_name)) + egg_info_exists = dist.egg_info and os.path.exists(dist.egg_info) + # Special case for distutils installed package + distutils_egg_info = getattr(dist._provider, 'path', None) + + # Uninstall cases order do matter as in the case of 2 installs of the + # same package, pip needs to uninstall the currently detected version + if (egg_info_exists and dist.egg_info.endswith('.egg-info') and + not dist.egg_info.endswith(develop_egg_link_egg_info)): + # if dist.egg_info.endswith(develop_egg_link_egg_info), we + # are in fact in the develop_egg_link case + paths_to_remove.add(dist.egg_info) + if dist.has_metadata('installed-files.txt'): + for installed_file in dist.get_metadata( + 'installed-files.txt').splitlines(): + path = os.path.normpath( + os.path.join(dist.egg_info, installed_file) + ) + paths_to_remove.add(path) + # FIXME: need a test for this elif block + # occurs with --single-version-externally-managed/--record outside + # of pip + elif dist.has_metadata('top_level.txt'): + if dist.has_metadata('namespace_packages.txt'): + namespaces = dist.get_metadata('namespace_packages.txt') + else: + namespaces = [] + for top_level_pkg in [ + p for p + in dist.get_metadata('top_level.txt').splitlines() + if p and p not in namespaces]: + path = os.path.join(dist.location, top_level_pkg) + paths_to_remove.add(path) + paths_to_remove.add(path + '.py') + paths_to_remove.add(path + '.pyc') + paths_to_remove.add(path + '.pyo') + + elif distutils_egg_info: + raise UninstallationError( + "Cannot uninstall {!r}. It is a distutils installed project " + "and thus we cannot accurately determine which files belong " + "to it which would lead to only a partial uninstall.".format( + dist.project_name, + ) + ) + + elif dist.location.endswith('.egg'): + # package installed by easy_install + # We cannot match on dist.egg_name because it can slightly vary + # i.e. setuptools-0.6c11-py2.6.egg vs setuptools-0.6rc11-py2.6.egg + paths_to_remove.add(dist.location) + easy_install_egg = os.path.split(dist.location)[1] + easy_install_pth = os.path.join(os.path.dirname(dist.location), + 'easy-install.pth') + paths_to_remove.add_pth(easy_install_pth, './' + easy_install_egg) + + elif egg_info_exists and dist.egg_info.endswith('.dist-info'): + for path in uninstallation_paths(dist): + paths_to_remove.add(path) + + elif develop_egg_link: + # develop egg + with open(develop_egg_link, 'r') as fh: + link_pointer = os.path.normcase(fh.readline().strip()) + assert (link_pointer == dist.location), ( + 'Egg-link {} does not match installed location of {} ' + '(at {})'.format( + link_pointer, dist.project_name, dist.location) + ) + paths_to_remove.add(develop_egg_link) + easy_install_pth = os.path.join(os.path.dirname(develop_egg_link), + 'easy-install.pth') + paths_to_remove.add_pth(easy_install_pth, dist.location) + + else: + logger.debug( + 'Not sure how to uninstall: %s - Check: %s', + dist, dist.location, + ) + + # find distutils scripts= scripts + if dist.has_metadata('scripts') and dist.metadata_isdir('scripts'): + for script in dist.metadata_listdir('scripts'): + if dist_in_usersite(dist): + bin_dir = bin_user + else: + bin_dir = bin_py + paths_to_remove.add(os.path.join(bin_dir, script)) + if WINDOWS: + paths_to_remove.add(os.path.join(bin_dir, script) + '.bat') + + # find console_scripts + _scripts_to_remove = [] + console_scripts = dist.get_entry_map(group='console_scripts') + for name in console_scripts.keys(): + _scripts_to_remove.extend(_script_names(dist, name, False)) + # find gui_scripts + gui_scripts = dist.get_entry_map(group='gui_scripts') + for name in gui_scripts.keys(): + _scripts_to_remove.extend(_script_names(dist, name, True)) + + for s in _scripts_to_remove: + paths_to_remove.add(s) + + return paths_to_remove + + +class UninstallPthEntries(object): + def __init__(self, pth_file): + # type: (str) -> None + self.file = pth_file + self.entries = set() # type: Set[str] + self._saved_lines = None # type: Optional[List[bytes]] + + def add(self, entry): + # type: (str) -> None + entry = os.path.normcase(entry) + # On Windows, os.path.normcase converts the entry to use + # backslashes. This is correct for entries that describe absolute + # paths outside of site-packages, but all the others use forward + # slashes. + # os.path.splitdrive is used instead of os.path.isabs because isabs + # treats non-absolute paths with drive letter markings like c:foo\bar + # as absolute paths. It also does not recognize UNC paths if they don't + # have more than "\\sever\share". Valid examples: "\\server\share\" or + # "\\server\share\folder". Python 2.7.8+ support UNC in splitdrive. + if WINDOWS and not os.path.splitdrive(entry)[0]: + entry = entry.replace('\\', '/') + self.entries.add(entry) + + def remove(self): + # type: () -> None + logger.debug('Removing pth entries from %s:', self.file) + + # If the file doesn't exist, log a warning and return + if not os.path.isfile(self.file): + logger.warning( + "Cannot remove entries from nonexistent file %s", self.file + ) + return + with open(self.file, 'rb') as fh: + # windows uses '\r\n' with py3k, but uses '\n' with py2.x + lines = fh.readlines() + self._saved_lines = lines + if any(b'\r\n' in line for line in lines): + endline = '\r\n' + else: + endline = '\n' + # handle missing trailing newline + if lines and not lines[-1].endswith(endline.encode("utf-8")): + lines[-1] = lines[-1] + endline.encode("utf-8") + for entry in self.entries: + try: + logger.debug('Removing entry: %s', entry) + lines.remove((entry + endline).encode("utf-8")) + except ValueError: + pass + with open(self.file, 'wb') as fh: + fh.writelines(lines) + + def rollback(self): + # type: () -> bool + if self._saved_lines is None: + logger.error( + 'Cannot roll back changes to %s, none were made', self.file + ) + return False + logger.debug('Rolling %s back to previous state', self.file) + with open(self.file, 'wb') as fh: + fh.writelines(self._saved_lines) + return True diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/__init__.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/base.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/base.py new file mode 100644 index 0000000000..6d50555e53 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/base.py @@ -0,0 +1,21 @@ +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Callable, List + + from pip._internal.req.req_install import InstallRequirement + from pip._internal.req.req_set import RequirementSet + + InstallRequirementProvider = Callable[ + [str, InstallRequirement], InstallRequirement + ] + + +class BaseResolver(object): + def resolve(self, root_reqs, check_supported_wheels): + # type: (List[InstallRequirement], bool) -> RequirementSet + raise NotImplementedError() + + def get_installation_order(self, req_set): + # type: (RequirementSet) -> List[InstallRequirement] + raise NotImplementedError() diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/legacy/__init__.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/legacy/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/legacy/resolver.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/legacy/resolver.py new file mode 100644 index 0000000000..d0fc1a7b31 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/legacy/resolver.py @@ -0,0 +1,473 @@ +"""Dependency Resolution + +The dependency resolution in pip is performed as follows: + +for top-level requirements: + a. only one spec allowed per project, regardless of conflicts or not. + otherwise a "double requirement" exception is raised + b. they override sub-dependency requirements. +for sub-dependencies + a. "first found, wins" (where the order is breadth first) +""" + +# The following comment should be removed at some point in the future. +# mypy: strict-optional=False +# mypy: disallow-untyped-defs=False + +import logging +import sys +from collections import defaultdict +from itertools import chain + +from pip._vendor.packaging import specifiers + +from pip._internal.exceptions import ( + BestVersionAlreadyInstalled, + DistributionNotFound, + HashError, + HashErrors, + UnsupportedPythonVersion, +) +from pip._internal.req.req_install import check_invalid_constraint_type +from pip._internal.req.req_set import RequirementSet +from pip._internal.resolution.base import BaseResolver +from pip._internal.utils.compatibility_tags import get_supported +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import dist_in_usersite, normalize_version_info +from pip._internal.utils.packaging import check_requires_python, get_requires_python +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import DefaultDict, List, Optional, Set, Tuple + + from pip._vendor.pkg_resources import Distribution + + from pip._internal.cache import WheelCache + from pip._internal.index.package_finder import PackageFinder + from pip._internal.models.link import Link + from pip._internal.operations.prepare import RequirementPreparer + from pip._internal.req.req_install import InstallRequirement + from pip._internal.resolution.base import InstallRequirementProvider + + DiscoveredDependencies = DefaultDict[str, List[InstallRequirement]] + +logger = logging.getLogger(__name__) + + +def _check_dist_requires_python( + dist, # type: Distribution + version_info, # type: Tuple[int, int, int] + ignore_requires_python=False, # type: bool +): + # type: (...) -> None + """ + Check whether the given Python version is compatible with a distribution's + "Requires-Python" value. + + :param version_info: A 3-tuple of ints representing the Python + major-minor-micro version to check. + :param ignore_requires_python: Whether to ignore the "Requires-Python" + value if the given Python version isn't compatible. + + :raises UnsupportedPythonVersion: When the given Python version isn't + compatible. + """ + requires_python = get_requires_python(dist) + try: + is_compatible = check_requires_python( + requires_python, version_info=version_info, + ) + except specifiers.InvalidSpecifier as exc: + logger.warning( + "Package %r has an invalid Requires-Python: %s", + dist.project_name, exc, + ) + return + + if is_compatible: + return + + version = '.'.join(map(str, version_info)) + if ignore_requires_python: + logger.debug( + 'Ignoring failed Requires-Python check for package %r: ' + '%s not in %r', + dist.project_name, version, requires_python, + ) + return + + raise UnsupportedPythonVersion( + 'Package {!r} requires a different Python: {} not in {!r}'.format( + dist.project_name, version, requires_python, + )) + + +class Resolver(BaseResolver): + """Resolves which packages need to be installed/uninstalled to perform \ + the requested operation without breaking the requirements of any package. + """ + + _allowed_strategies = {"eager", "only-if-needed", "to-satisfy-only"} + + def __init__( + self, + preparer, # type: RequirementPreparer + finder, # type: PackageFinder + wheel_cache, # type: Optional[WheelCache] + make_install_req, # type: InstallRequirementProvider + use_user_site, # type: bool + ignore_dependencies, # type: bool + ignore_installed, # type: bool + ignore_requires_python, # type: bool + force_reinstall, # type: bool + upgrade_strategy, # type: str + py_version_info=None, # type: Optional[Tuple[int, ...]] + ): + # type: (...) -> None + super(Resolver, self).__init__() + assert upgrade_strategy in self._allowed_strategies + + if py_version_info is None: + py_version_info = sys.version_info[:3] + else: + py_version_info = normalize_version_info(py_version_info) + + self._py_version_info = py_version_info + + self.preparer = preparer + self.finder = finder + self.wheel_cache = wheel_cache + + self.upgrade_strategy = upgrade_strategy + self.force_reinstall = force_reinstall + self.ignore_dependencies = ignore_dependencies + self.ignore_installed = ignore_installed + self.ignore_requires_python = ignore_requires_python + self.use_user_site = use_user_site + self._make_install_req = make_install_req + + self._discovered_dependencies = \ + defaultdict(list) # type: DiscoveredDependencies + + def resolve(self, root_reqs, check_supported_wheels): + # type: (List[InstallRequirement], bool) -> RequirementSet + """Resolve what operations need to be done + + As a side-effect of this method, the packages (and their dependencies) + are downloaded, unpacked and prepared for installation. This + preparation is done by ``pip.operations.prepare``. + + Once PyPI has static dependency metadata available, it would be + possible to move the preparation to become a step separated from + dependency resolution. + """ + requirement_set = RequirementSet( + check_supported_wheels=check_supported_wheels + ) + for req in root_reqs: + if req.constraint: + check_invalid_constraint_type(req) + requirement_set.add_requirement(req) + + # Actually prepare the files, and collect any exceptions. Most hash + # exceptions cannot be checked ahead of time, because + # _populate_link() needs to be called before we can make decisions + # based on link type. + discovered_reqs = [] # type: List[InstallRequirement] + hash_errors = HashErrors() + for req in chain(requirement_set.all_requirements, discovered_reqs): + try: + discovered_reqs.extend(self._resolve_one(requirement_set, req)) + except HashError as exc: + exc.req = req + hash_errors.append(exc) + + if hash_errors: + raise hash_errors + + return requirement_set + + def _is_upgrade_allowed(self, req): + # type: (InstallRequirement) -> bool + if self.upgrade_strategy == "to-satisfy-only": + return False + elif self.upgrade_strategy == "eager": + return True + else: + assert self.upgrade_strategy == "only-if-needed" + return req.user_supplied or req.constraint + + def _set_req_to_reinstall(self, req): + # type: (InstallRequirement) -> None + """ + Set a requirement to be installed. + """ + # Don't uninstall the conflict if doing a user install and the + # conflict is not a user install. + if not self.use_user_site or dist_in_usersite(req.satisfied_by): + req.should_reinstall = True + req.satisfied_by = None + + def _check_skip_installed(self, req_to_install): + # type: (InstallRequirement) -> Optional[str] + """Check if req_to_install should be skipped. + + This will check if the req is installed, and whether we should upgrade + or reinstall it, taking into account all the relevant user options. + + After calling this req_to_install will only have satisfied_by set to + None if the req_to_install is to be upgraded/reinstalled etc. Any + other value will be a dist recording the current thing installed that + satisfies the requirement. + + Note that for vcs urls and the like we can't assess skipping in this + routine - we simply identify that we need to pull the thing down, + then later on it is pulled down and introspected to assess upgrade/ + reinstalls etc. + + :return: A text reason for why it was skipped, or None. + """ + if self.ignore_installed: + return None + + req_to_install.check_if_exists(self.use_user_site) + if not req_to_install.satisfied_by: + return None + + if self.force_reinstall: + self._set_req_to_reinstall(req_to_install) + return None + + if not self._is_upgrade_allowed(req_to_install): + if self.upgrade_strategy == "only-if-needed": + return 'already satisfied, skipping upgrade' + return 'already satisfied' + + # Check for the possibility of an upgrade. For link-based + # requirements we have to pull the tree down and inspect to assess + # the version #, so it's handled way down. + if not req_to_install.link: + try: + self.finder.find_requirement(req_to_install, upgrade=True) + except BestVersionAlreadyInstalled: + # Then the best version is installed. + return 'already up-to-date' + except DistributionNotFound: + # No distribution found, so we squash the error. It will + # be raised later when we re-try later to do the install. + # Why don't we just raise here? + pass + + self._set_req_to_reinstall(req_to_install) + return None + + def _find_requirement_link(self, req): + # type: (InstallRequirement) -> Optional[Link] + upgrade = self._is_upgrade_allowed(req) + best_candidate = self.finder.find_requirement(req, upgrade) + if not best_candidate: + return None + + # Log a warning per PEP 592 if necessary before returning. + link = best_candidate.link + if link.is_yanked: + reason = link.yanked_reason or '' + msg = ( + # Mark this as a unicode string to prevent + # "UnicodeEncodeError: 'ascii' codec can't encode character" + # in Python 2 when the reason contains non-ascii characters. + u'The candidate selected for download or install is a ' + 'yanked version: {candidate}\n' + 'Reason for being yanked: {reason}' + ).format(candidate=best_candidate, reason=reason) + logger.warning(msg) + + return link + + def _populate_link(self, req): + # type: (InstallRequirement) -> None + """Ensure that if a link can be found for this, that it is found. + + Note that req.link may still be None - if the requirement is already + installed and not needed to be upgraded based on the return value of + _is_upgrade_allowed(). + + If preparer.require_hashes is True, don't use the wheel cache, because + cached wheels, always built locally, have different hashes than the + files downloaded from the index server and thus throw false hash + mismatches. Furthermore, cached wheels at present have undeterministic + contents due to file modification times. + """ + if req.link is None: + req.link = self._find_requirement_link(req) + + if self.wheel_cache is None or self.preparer.require_hashes: + return + cache_entry = self.wheel_cache.get_cache_entry( + link=req.link, + package_name=req.name, + supported_tags=get_supported(), + ) + if cache_entry is not None: + logger.debug('Using cached wheel link: %s', cache_entry.link) + if req.link is req.original_link and cache_entry.persistent: + req.original_link_is_in_wheel_cache = True + req.link = cache_entry.link + + def _get_dist_for(self, req): + # type: (InstallRequirement) -> Distribution + """Takes a InstallRequirement and returns a single AbstractDist \ + representing a prepared variant of the same. + """ + if req.editable: + return self.preparer.prepare_editable_requirement(req) + + # satisfied_by is only evaluated by calling _check_skip_installed, + # so it must be None here. + assert req.satisfied_by is None + skip_reason = self._check_skip_installed(req) + + if req.satisfied_by: + return self.preparer.prepare_installed_requirement( + req, skip_reason + ) + + # We eagerly populate the link, since that's our "legacy" behavior. + self._populate_link(req) + dist = self.preparer.prepare_linked_requirement(req) + + # NOTE + # The following portion is for determining if a certain package is + # going to be re-installed/upgraded or not and reporting to the user. + # This should probably get cleaned up in a future refactor. + + # req.req is only avail after unpack for URL + # pkgs repeat check_if_exists to uninstall-on-upgrade + # (#14) + if not self.ignore_installed: + req.check_if_exists(self.use_user_site) + + if req.satisfied_by: + should_modify = ( + self.upgrade_strategy != "to-satisfy-only" or + self.force_reinstall or + self.ignore_installed or + req.link.scheme == 'file' + ) + if should_modify: + self._set_req_to_reinstall(req) + else: + logger.info( + 'Requirement already satisfied (use --upgrade to upgrade):' + ' %s', req, + ) + return dist + + def _resolve_one( + self, + requirement_set, # type: RequirementSet + req_to_install, # type: InstallRequirement + ): + # type: (...) -> List[InstallRequirement] + """Prepare a single requirements file. + + :return: A list of additional InstallRequirements to also install. + """ + # Tell user what we are doing for this requirement: + # obtain (editable), skipping, processing (local url), collecting + # (remote url or package name) + if req_to_install.constraint or req_to_install.prepared: + return [] + + req_to_install.prepared = True + + # Parse and return dependencies + dist = self._get_dist_for(req_to_install) + # This will raise UnsupportedPythonVersion if the given Python + # version isn't compatible with the distribution's Requires-Python. + _check_dist_requires_python( + dist, version_info=self._py_version_info, + ignore_requires_python=self.ignore_requires_python, + ) + + more_reqs = [] # type: List[InstallRequirement] + + def add_req(subreq, extras_requested): + sub_install_req = self._make_install_req( + str(subreq), + req_to_install, + ) + parent_req_name = req_to_install.name + to_scan_again, add_to_parent = requirement_set.add_requirement( + sub_install_req, + parent_req_name=parent_req_name, + extras_requested=extras_requested, + ) + if parent_req_name and add_to_parent: + self._discovered_dependencies[parent_req_name].append( + add_to_parent + ) + more_reqs.extend(to_scan_again) + + with indent_log(): + # We add req_to_install before its dependencies, so that we + # can refer to it when adding dependencies. + if not requirement_set.has_requirement(req_to_install.name): + # 'unnamed' requirements will get added here + # 'unnamed' requirements can only come from being directly + # provided by the user. + assert req_to_install.user_supplied + requirement_set.add_requirement( + req_to_install, parent_req_name=None, + ) + + if not self.ignore_dependencies: + if req_to_install.extras: + logger.debug( + "Installing extra requirements: %r", + ','.join(req_to_install.extras), + ) + missing_requested = sorted( + set(req_to_install.extras) - set(dist.extras) + ) + for missing in missing_requested: + logger.warning( + "%s does not provide the extra '%s'", + dist, missing + ) + + available_requested = sorted( + set(dist.extras) & set(req_to_install.extras) + ) + for subreq in dist.requires(available_requested): + add_req(subreq, extras_requested=available_requested) + + return more_reqs + + def get_installation_order(self, req_set): + # type: (RequirementSet) -> List[InstallRequirement] + """Create the installation order. + + The installation order is topological - requirements are installed + before the requiring thing. We break cycles at an arbitrary point, + and make no other guarantees. + """ + # The current implementation, which we may change at any point + # installs the user specified things in the order given, except when + # dependencies must come earlier to achieve topological order. + order = [] + ordered_reqs = set() # type: Set[InstallRequirement] + + def schedule(req): + if req.satisfied_by or req in ordered_reqs: + return + if req.constraint: + return + ordered_reqs.add(req) + for dep in self._discovered_dependencies[req.name]: + schedule(dep) + order.append(req) + + for install_req in req_set.requirements.values(): + schedule(install_req) + return order diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__init__.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/base.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/base.py new file mode 100644 index 0000000000..7eb8a178eb --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/base.py @@ -0,0 +1,156 @@ +from pip._vendor.packaging.specifiers import SpecifierSet +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.req.req_install import InstallRequirement +from pip._internal.utils.hashes import Hashes +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import FrozenSet, Iterable, Optional, Tuple + + from pip._vendor.packaging.version import _BaseVersion + + from pip._internal.models.link import Link + + CandidateLookup = Tuple[ + Optional["Candidate"], + Optional[InstallRequirement], + ] + + +def format_name(project, extras): + # type: (str, FrozenSet[str]) -> str + if not extras: + return project + canonical_extras = sorted(canonicalize_name(e) for e in extras) + return "{}[{}]".format(project, ",".join(canonical_extras)) + + +class Constraint(object): + def __init__(self, specifier, hashes): + # type: (SpecifierSet, Hashes) -> None + self.specifier = specifier + self.hashes = hashes + + @classmethod + def empty(cls): + # type: () -> Constraint + return Constraint(SpecifierSet(), Hashes()) + + @classmethod + def from_ireq(cls, ireq): + # type: (InstallRequirement) -> Constraint + return Constraint(ireq.specifier, ireq.hashes(trust_internet=False)) + + def __nonzero__(self): + # type: () -> bool + return bool(self.specifier) or bool(self.hashes) + + def __bool__(self): + # type: () -> bool + return self.__nonzero__() + + def __and__(self, other): + # type: (InstallRequirement) -> Constraint + if not isinstance(other, InstallRequirement): + return NotImplemented + specifier = self.specifier & other.specifier + hashes = self.hashes & other.hashes(trust_internet=False) + return Constraint(specifier, hashes) + + def is_satisfied_by(self, candidate): + # type: (Candidate) -> bool + # We can safely always allow prereleases here since PackageFinder + # already implements the prerelease logic, and would have filtered out + # prerelease candidates if the user does not expect them. + return self.specifier.contains(candidate.version, prereleases=True) + + +class Requirement(object): + @property + def project_name(self): + # type: () -> str + """The "project name" of a requirement. + + This is different from ``name`` if this requirement contains extras, + in which case ``name`` would contain the ``[...]`` part, while this + refers to the name of the project. + """ + raise NotImplementedError("Subclass should override") + + @property + def name(self): + # type: () -> str + """The name identifying this requirement in the resolver. + + This is different from ``project_name`` if this requirement contains + extras, where ``project_name`` would not contain the ``[...]`` part. + """ + raise NotImplementedError("Subclass should override") + + def is_satisfied_by(self, candidate): + # type: (Candidate) -> bool + return False + + def get_candidate_lookup(self): + # type: () -> CandidateLookup + raise NotImplementedError("Subclass should override") + + def format_for_error(self): + # type: () -> str + raise NotImplementedError("Subclass should override") + + +class Candidate(object): + @property + def project_name(self): + # type: () -> str + """The "project name" of the candidate. + + This is different from ``name`` if this candidate contains extras, + in which case ``name`` would contain the ``[...]`` part, while this + refers to the name of the project. + """ + raise NotImplementedError("Override in subclass") + + @property + def name(self): + # type: () -> str + """The name identifying this candidate in the resolver. + + This is different from ``project_name`` if this candidate contains + extras, where ``project_name`` would not contain the ``[...]`` part. + """ + raise NotImplementedError("Override in subclass") + + @property + def version(self): + # type: () -> _BaseVersion + raise NotImplementedError("Override in subclass") + + @property + def is_installed(self): + # type: () -> bool + raise NotImplementedError("Override in subclass") + + @property + def is_editable(self): + # type: () -> bool + raise NotImplementedError("Override in subclass") + + @property + def source_link(self): + # type: () -> Optional[Link] + raise NotImplementedError("Override in subclass") + + def iter_dependencies(self, with_requires): + # type: (bool) -> Iterable[Optional[Requirement]] + raise NotImplementedError("Override in subclass") + + def get_install_requirement(self): + # type: () -> Optional[InstallRequirement] + raise NotImplementedError("Override in subclass") + + def format_for_error(self): + # type: () -> str + raise NotImplementedError("Subclass should override") diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/candidates.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/candidates.py new file mode 100644 index 0000000000..5211a17113 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/candidates.py @@ -0,0 +1,604 @@ +import logging +import sys + +from pip._vendor.packaging.specifiers import InvalidSpecifier, SpecifierSet +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.packaging.version import Version, parse as parse_version + +from pip._internal.exceptions import HashError, MetadataInconsistent +from pip._internal.models.wheel import Wheel +from pip._internal.req.constructors import ( + install_req_from_editable, + install_req_from_line, +) +from pip._internal.req.req_install import InstallRequirement +from pip._internal.utils.misc import dist_is_editable, normalize_version_info +from pip._internal.utils.packaging import get_requires_python +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +from .base import Candidate, format_name + +if MYPY_CHECK_RUNNING: + from typing import Any, FrozenSet, Iterable, Optional, Tuple, Union + + from pip._vendor.packaging.version import _BaseVersion + from pip._vendor.pkg_resources import Distribution + + from pip._internal.models.link import Link + + from .base import Requirement + from .factory import Factory + + BaseCandidate = Union[ + "AlreadyInstalledCandidate", + "EditableCandidate", + "LinkCandidate", + ] + + +logger = logging.getLogger(__name__) + + +def make_install_req_from_link(link, template): + # type: (Link, InstallRequirement) -> InstallRequirement + assert not template.editable, "template is editable" + if template.req: + line = str(template.req) + else: + line = link.url + ireq = install_req_from_line( + line, + user_supplied=template.user_supplied, + comes_from=template.comes_from, + use_pep517=template.use_pep517, + isolated=template.isolated, + constraint=template.constraint, + options=dict( + install_options=template.install_options, + global_options=template.global_options, + hashes=template.hash_options + ), + ) + ireq.original_link = template.original_link + ireq.link = link + return ireq + + +def make_install_req_from_editable(link, template): + # type: (Link, InstallRequirement) -> InstallRequirement + assert template.editable, "template not editable" + return install_req_from_editable( + link.url, + user_supplied=template.user_supplied, + comes_from=template.comes_from, + use_pep517=template.use_pep517, + isolated=template.isolated, + constraint=template.constraint, + options=dict( + install_options=template.install_options, + global_options=template.global_options, + hashes=template.hash_options + ), + ) + + +def make_install_req_from_dist(dist, template): + # type: (Distribution, InstallRequirement) -> InstallRequirement + project_name = canonicalize_name(dist.project_name) + if template.req: + line = str(template.req) + elif template.link: + line = "{} @ {}".format(project_name, template.link.url) + else: + line = "{}=={}".format(project_name, dist.parsed_version) + ireq = install_req_from_line( + line, + user_supplied=template.user_supplied, + comes_from=template.comes_from, + use_pep517=template.use_pep517, + isolated=template.isolated, + constraint=template.constraint, + options=dict( + install_options=template.install_options, + global_options=template.global_options, + hashes=template.hash_options + ), + ) + ireq.satisfied_by = dist + return ireq + + +class _InstallRequirementBackedCandidate(Candidate): + """A candidate backed by an ``InstallRequirement``. + + This represents a package request with the target not being already + in the environment, and needs to be fetched and installed. The backing + ``InstallRequirement`` is responsible for most of the leg work; this + class exposes appropriate information to the resolver. + + :param link: The link passed to the ``InstallRequirement``. The backing + ``InstallRequirement`` will use this link to fetch the distribution. + :param source_link: The link this candidate "originates" from. This is + different from ``link`` when the link is found in the wheel cache. + ``link`` would point to the wheel cache, while this points to the + found remote link (e.g. from pypi.org). + """ + is_installed = False + + def __init__( + self, + link, # type: Link + source_link, # type: Link + ireq, # type: InstallRequirement + factory, # type: Factory + name=None, # type: Optional[str] + version=None, # type: Optional[_BaseVersion] + ): + # type: (...) -> None + self._link = link + self._source_link = source_link + self._factory = factory + self._ireq = ireq + self._name = name + self._version = version + self.dist = self._prepare() + + def __str__(self): + # type: () -> str + return "{} {}".format(self.name, self.version) + + def __repr__(self): + # type: () -> str + return "{class_name}({link!r})".format( + class_name=self.__class__.__name__, + link=str(self._link), + ) + + def __hash__(self): + # type: () -> int + return hash((self.__class__, self._link)) + + def __eq__(self, other): + # type: (Any) -> bool + if isinstance(other, self.__class__): + return self._link == other._link + return False + + # Needed for Python 2, which does not implement this by default + def __ne__(self, other): + # type: (Any) -> bool + return not self.__eq__(other) + + @property + def source_link(self): + # type: () -> Optional[Link] + return self._source_link + + @property + def project_name(self): + # type: () -> str + """The normalised name of the project the candidate refers to""" + if self._name is None: + self._name = canonicalize_name(self.dist.project_name) + return self._name + + @property + def name(self): + # type: () -> str + return self.project_name + + @property + def version(self): + # type: () -> _BaseVersion + if self._version is None: + self._version = parse_version(self.dist.version) + return self._version + + def format_for_error(self): + # type: () -> str + return "{} {} (from {})".format( + self.name, + self.version, + self._link.file_path if self._link.is_file else self._link + ) + + def _prepare_distribution(self): + # type: () -> Distribution + raise NotImplementedError("Override in subclass") + + def _check_metadata_consistency(self, dist): + # type: (Distribution) -> None + """Check for consistency of project name and version of dist.""" + name = canonicalize_name(dist.project_name) + if self._name is not None and self._name != name: + raise MetadataInconsistent(self._ireq, "name", dist.project_name) + version = parse_version(dist.version) + if self._version is not None and self._version != version: + raise MetadataInconsistent(self._ireq, "version", dist.version) + + def _prepare(self): + # type: () -> Distribution + try: + dist = self._prepare_distribution() + except HashError as e: + # Provide HashError the underlying ireq that caused it. This + # provides context for the resulting error message to show the + # offending line to the user. + e.req = self._ireq + raise + self._check_metadata_consistency(dist) + return dist + + def _get_requires_python_dependency(self): + # type: () -> Optional[Requirement] + requires_python = get_requires_python(self.dist) + if requires_python is None: + return None + try: + spec = SpecifierSet(requires_python) + except InvalidSpecifier as e: + message = "Package %r has an invalid Requires-Python: %s" + logger.warning(message, self.name, e) + return None + return self._factory.make_requires_python_requirement(spec) + + def iter_dependencies(self, with_requires): + # type: (bool) -> Iterable[Optional[Requirement]] + requires = self.dist.requires() if with_requires else () + for r in requires: + yield self._factory.make_requirement_from_spec(str(r), self._ireq) + yield self._get_requires_python_dependency() + + def get_install_requirement(self): + # type: () -> Optional[InstallRequirement] + return self._ireq + + +class LinkCandidate(_InstallRequirementBackedCandidate): + is_editable = False + + def __init__( + self, + link, # type: Link + template, # type: InstallRequirement + factory, # type: Factory + name=None, # type: Optional[str] + version=None, # type: Optional[_BaseVersion] + ): + # type: (...) -> None + source_link = link + cache_entry = factory.get_wheel_cache_entry(link, name) + if cache_entry is not None: + logger.debug("Using cached wheel link: %s", cache_entry.link) + link = cache_entry.link + ireq = make_install_req_from_link(link, template) + assert ireq.link == link + if ireq.link.is_wheel and not ireq.link.is_file: + wheel = Wheel(ireq.link.filename) + wheel_name = canonicalize_name(wheel.name) + assert name == wheel_name, ( + "{!r} != {!r} for wheel".format(name, wheel_name) + ) + # Version may not be present for PEP 508 direct URLs + if version is not None: + wheel_version = Version(wheel.version) + assert version == wheel_version, ( + "{!r} != {!r} for wheel {}".format( + version, wheel_version, name + ) + ) + + if (cache_entry is not None and + cache_entry.persistent and + template.link is template.original_link): + ireq.original_link_is_in_wheel_cache = True + + super(LinkCandidate, self).__init__( + link=link, + source_link=source_link, + ireq=ireq, + factory=factory, + name=name, + version=version, + ) + + def _prepare_distribution(self): + # type: () -> Distribution + return self._factory.preparer.prepare_linked_requirement( + self._ireq, parallel_builds=True, + ) + + +class EditableCandidate(_InstallRequirementBackedCandidate): + is_editable = True + + def __init__( + self, + link, # type: Link + template, # type: InstallRequirement + factory, # type: Factory + name=None, # type: Optional[str] + version=None, # type: Optional[_BaseVersion] + ): + # type: (...) -> None + super(EditableCandidate, self).__init__( + link=link, + source_link=link, + ireq=make_install_req_from_editable(link, template), + factory=factory, + name=name, + version=version, + ) + + def _prepare_distribution(self): + # type: () -> Distribution + return self._factory.preparer.prepare_editable_requirement(self._ireq) + + +class AlreadyInstalledCandidate(Candidate): + is_installed = True + source_link = None + + def __init__( + self, + dist, # type: Distribution + template, # type: InstallRequirement + factory, # type: Factory + ): + # type: (...) -> None + self.dist = dist + self._ireq = make_install_req_from_dist(dist, template) + self._factory = factory + + # This is just logging some messages, so we can do it eagerly. + # The returned dist would be exactly the same as self.dist because we + # set satisfied_by in make_install_req_from_dist. + # TODO: Supply reason based on force_reinstall and upgrade_strategy. + skip_reason = "already satisfied" + factory.preparer.prepare_installed_requirement(self._ireq, skip_reason) + + def __str__(self): + # type: () -> str + return str(self.dist) + + def __repr__(self): + # type: () -> str + return "{class_name}({distribution!r})".format( + class_name=self.__class__.__name__, + distribution=self.dist, + ) + + def __hash__(self): + # type: () -> int + return hash((self.__class__, self.name, self.version)) + + def __eq__(self, other): + # type: (Any) -> bool + if isinstance(other, self.__class__): + return self.name == other.name and self.version == other.version + return False + + # Needed for Python 2, which does not implement this by default + def __ne__(self, other): + # type: (Any) -> bool + return not self.__eq__(other) + + @property + def project_name(self): + # type: () -> str + return canonicalize_name(self.dist.project_name) + + @property + def name(self): + # type: () -> str + return self.project_name + + @property + def version(self): + # type: () -> _BaseVersion + return parse_version(self.dist.version) + + @property + def is_editable(self): + # type: () -> bool + return dist_is_editable(self.dist) + + def format_for_error(self): + # type: () -> str + return "{} {} (Installed)".format(self.name, self.version) + + def iter_dependencies(self, with_requires): + # type: (bool) -> Iterable[Optional[Requirement]] + if not with_requires: + return + for r in self.dist.requires(): + yield self._factory.make_requirement_from_spec(str(r), self._ireq) + + def get_install_requirement(self): + # type: () -> Optional[InstallRequirement] + return None + + +class ExtrasCandidate(Candidate): + """A candidate that has 'extras', indicating additional dependencies. + + Requirements can be for a project with dependencies, something like + foo[extra]. The extras don't affect the project/version being installed + directly, but indicate that we need additional dependencies. We model that + by having an artificial ExtrasCandidate that wraps the "base" candidate. + + The ExtrasCandidate differs from the base in the following ways: + + 1. It has a unique name, of the form foo[extra]. This causes the resolver + to treat it as a separate node in the dependency graph. + 2. When we're getting the candidate's dependencies, + a) We specify that we want the extra dependencies as well. + b) We add a dependency on the base candidate. + See below for why this is needed. + 3. We return None for the underlying InstallRequirement, as the base + candidate will provide it, and we don't want to end up with duplicates. + + The dependency on the base candidate is needed so that the resolver can't + decide that it should recommend foo[extra1] version 1.0 and foo[extra2] + version 2.0. Having those candidates depend on foo=1.0 and foo=2.0 + respectively forces the resolver to recognise that this is a conflict. + """ + def __init__( + self, + base, # type: BaseCandidate + extras, # type: FrozenSet[str] + ): + # type: (...) -> None + self.base = base + self.extras = extras + + def __str__(self): + # type: () -> str + name, rest = str(self.base).split(" ", 1) + return "{}[{}] {}".format(name, ",".join(self.extras), rest) + + def __repr__(self): + # type: () -> str + return "{class_name}(base={base!r}, extras={extras!r})".format( + class_name=self.__class__.__name__, + base=self.base, + extras=self.extras, + ) + + def __hash__(self): + # type: () -> int + return hash((self.base, self.extras)) + + def __eq__(self, other): + # type: (Any) -> bool + if isinstance(other, self.__class__): + return self.base == other.base and self.extras == other.extras + return False + + # Needed for Python 2, which does not implement this by default + def __ne__(self, other): + # type: (Any) -> bool + return not self.__eq__(other) + + @property + def project_name(self): + # type: () -> str + return self.base.project_name + + @property + def name(self): + # type: () -> str + """The normalised name of the project the candidate refers to""" + return format_name(self.base.project_name, self.extras) + + @property + def version(self): + # type: () -> _BaseVersion + return self.base.version + + def format_for_error(self): + # type: () -> str + return "{} [{}]".format( + self.base.format_for_error(), + ", ".join(sorted(self.extras)) + ) + + @property + def is_installed(self): + # type: () -> bool + return self.base.is_installed + + @property + def is_editable(self): + # type: () -> bool + return self.base.is_editable + + @property + def source_link(self): + # type: () -> Optional[Link] + return self.base.source_link + + def iter_dependencies(self, with_requires): + # type: (bool) -> Iterable[Optional[Requirement]] + factory = self.base._factory + + # Add a dependency on the exact base + # (See note 2b in the class docstring) + yield factory.make_requirement_from_candidate(self.base) + if not with_requires: + return + + # The user may have specified extras that the candidate doesn't + # support. We ignore any unsupported extras here. + valid_extras = self.extras.intersection(self.base.dist.extras) + invalid_extras = self.extras.difference(self.base.dist.extras) + for extra in sorted(invalid_extras): + logger.warning( + "%s %s does not provide the extra '%s'", + self.base.name, + self.version, + extra + ) + + for r in self.base.dist.requires(valid_extras): + requirement = factory.make_requirement_from_spec( + str(r), self.base._ireq, valid_extras, + ) + if requirement: + yield requirement + + def get_install_requirement(self): + # type: () -> Optional[InstallRequirement] + # We don't return anything here, because we always + # depend on the base candidate, and we'll get the + # install requirement from that. + return None + + +class RequiresPythonCandidate(Candidate): + is_installed = False + source_link = None + + def __init__(self, py_version_info): + # type: (Optional[Tuple[int, ...]]) -> None + if py_version_info is not None: + version_info = normalize_version_info(py_version_info) + else: + version_info = sys.version_info[:3] + self._version = Version(".".join(str(c) for c in version_info)) + + # We don't need to implement __eq__() and __ne__() since there is always + # only one RequiresPythonCandidate in a resolution, i.e. the host Python. + # The built-in object.__eq__() and object.__ne__() do exactly what we want. + + def __str__(self): + # type: () -> str + return "Python {}".format(self._version) + + @property + def project_name(self): + # type: () -> str + # Avoid conflicting with the PyPI package "Python". + return "" + + @property + def name(self): + # type: () -> str + return self.project_name + + @property + def version(self): + # type: () -> _BaseVersion + return self._version + + def format_for_error(self): + # type: () -> str + return "Python {}".format(self.version) + + def iter_dependencies(self, with_requires): + # type: (bool) -> Iterable[Optional[Requirement]] + return () + + def get_install_requirement(self): + # type: () -> Optional[InstallRequirement] + return None diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/factory.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/factory.py new file mode 100644 index 0000000000..e81595b8c9 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/factory.py @@ -0,0 +1,504 @@ +import logging + +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.exceptions import ( + DistributionNotFound, + InstallationError, + InstallationSubprocessError, + MetadataInconsistent, + UnsupportedPythonVersion, + UnsupportedWheel, +) +from pip._internal.models.wheel import Wheel +from pip._internal.req.req_install import InstallRequirement +from pip._internal.utils.compatibility_tags import get_supported +from pip._internal.utils.hashes import Hashes +from pip._internal.utils.misc import ( + dist_in_site_packages, + dist_in_usersite, + get_installed_distributions, +) +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.virtualenv import running_under_virtualenv + +from .base import Constraint +from .candidates import ( + AlreadyInstalledCandidate, + EditableCandidate, + ExtrasCandidate, + LinkCandidate, + RequiresPythonCandidate, +) +from .found_candidates import FoundCandidates +from .requirements import ( + ExplicitRequirement, + RequiresPythonRequirement, + SpecifierRequirement, + UnsatisfiableRequirement, +) + +if MYPY_CHECK_RUNNING: + from typing import ( + Dict, + FrozenSet, + Iterable, + Iterator, + List, + Optional, + Sequence, + Set, + Tuple, + TypeVar, + ) + + from pip._vendor.packaging.specifiers import SpecifierSet + from pip._vendor.packaging.version import _BaseVersion + from pip._vendor.pkg_resources import Distribution + from pip._vendor.resolvelib import ResolutionImpossible + + from pip._internal.cache import CacheEntry, WheelCache + from pip._internal.index.package_finder import PackageFinder + from pip._internal.models.link import Link + from pip._internal.operations.prepare import RequirementPreparer + from pip._internal.resolution.base import InstallRequirementProvider + + from .base import Candidate, Requirement + from .candidates import BaseCandidate + + C = TypeVar("C") + Cache = Dict[Link, C] + VersionCandidates = Dict[_BaseVersion, Candidate] + + +logger = logging.getLogger(__name__) + + +class Factory(object): + def __init__( + self, + finder, # type: PackageFinder + preparer, # type: RequirementPreparer + make_install_req, # type: InstallRequirementProvider + wheel_cache, # type: Optional[WheelCache] + use_user_site, # type: bool + force_reinstall, # type: bool + ignore_installed, # type: bool + ignore_requires_python, # type: bool + py_version_info=None, # type: Optional[Tuple[int, ...]] + ): + # type: (...) -> None + self._finder = finder + self.preparer = preparer + self._wheel_cache = wheel_cache + self._python_candidate = RequiresPythonCandidate(py_version_info) + self._make_install_req_from_spec = make_install_req + self._use_user_site = use_user_site + self._force_reinstall = force_reinstall + self._ignore_requires_python = ignore_requires_python + + self._build_failures = {} # type: Cache[InstallationError] + self._link_candidate_cache = {} # type: Cache[LinkCandidate] + self._editable_candidate_cache = {} # type: Cache[EditableCandidate] + self._installed_candidate_cache = { + } # type: Dict[str, AlreadyInstalledCandidate] + + if not ignore_installed: + self._installed_dists = { + canonicalize_name(dist.project_name): dist + for dist in get_installed_distributions(local_only=False) + } + else: + self._installed_dists = {} + + @property + def force_reinstall(self): + # type: () -> bool + return self._force_reinstall + + def _make_candidate_from_dist( + self, + dist, # type: Distribution + extras, # type: FrozenSet[str] + template, # type: InstallRequirement + ): + # type: (...) -> Candidate + try: + base = self._installed_candidate_cache[dist.key] + except KeyError: + base = AlreadyInstalledCandidate(dist, template, factory=self) + self._installed_candidate_cache[dist.key] = base + if extras: + return ExtrasCandidate(base, extras) + return base + + def _make_candidate_from_link( + self, + link, # type: Link + extras, # type: FrozenSet[str] + template, # type: InstallRequirement + name, # type: Optional[str] + version, # type: Optional[_BaseVersion] + ): + # type: (...) -> Optional[Candidate] + # TODO: Check already installed candidate, and use it if the link and + # editable flag match. + + if link in self._build_failures: + # We already tried this candidate before, and it does not build. + # Don't bother trying again. + return None + + if template.editable: + if link not in self._editable_candidate_cache: + try: + self._editable_candidate_cache[link] = EditableCandidate( + link, template, factory=self, + name=name, version=version, + ) + except (InstallationSubprocessError, MetadataInconsistent) as e: + logger.warning("Discarding %s. %s", link, e) + self._build_failures[link] = e + return None + base = self._editable_candidate_cache[link] # type: BaseCandidate + else: + if link not in self._link_candidate_cache: + try: + self._link_candidate_cache[link] = LinkCandidate( + link, template, factory=self, + name=name, version=version, + ) + except (InstallationSubprocessError, MetadataInconsistent) as e: + logger.warning("Discarding %s. %s", link, e) + self._build_failures[link] = e + return None + base = self._link_candidate_cache[link] + + if extras: + return ExtrasCandidate(base, extras) + return base + + def _iter_found_candidates( + self, + ireqs, # type: Sequence[InstallRequirement] + specifier, # type: SpecifierSet + hashes, # type: Hashes + prefers_installed, # type: bool + ): + # type: (...) -> Iterable[Candidate] + if not ireqs: + return () + + # The InstallRequirement implementation requires us to give it a + # "template". Here we just choose the first requirement to represent + # all of them. + # Hopefully the Project model can correct this mismatch in the future. + template = ireqs[0] + name = canonicalize_name(template.req.name) + + extras = frozenset() # type: FrozenSet[str] + for ireq in ireqs: + specifier &= ireq.req.specifier + hashes &= ireq.hashes(trust_internet=False) + extras |= frozenset(ireq.extras) + + # Get the installed version, if it matches, unless the user + # specified `--force-reinstall`, when we want the version from + # the index instead. + installed_candidate = None + if not self._force_reinstall and name in self._installed_dists: + installed_dist = self._installed_dists[name] + if specifier.contains(installed_dist.version, prereleases=True): + installed_candidate = self._make_candidate_from_dist( + dist=installed_dist, + extras=extras, + template=template, + ) + + def iter_index_candidates(): + # type: () -> Iterator[Candidate] + result = self._finder.find_best_candidate( + project_name=name, + specifier=specifier, + hashes=hashes, + ) + icans = list(result.iter_applicable()) + + # PEP 592: Yanked releases must be ignored unless only yanked + # releases can satisfy the version range. So if this is false, + # all yanked icans need to be skipped. + all_yanked = all(ican.link.is_yanked for ican in icans) + + # PackageFinder returns earlier versions first, so we reverse. + versions_found = set() # type: Set[_BaseVersion] + for ican in reversed(icans): + if not all_yanked and ican.link.is_yanked: + continue + if ican.version in versions_found: + continue + candidate = self._make_candidate_from_link( + link=ican.link, + extras=extras, + template=template, + name=name, + version=ican.version, + ) + if candidate is None: + continue + yield candidate + versions_found.add(ican.version) + + return FoundCandidates( + iter_index_candidates, + installed_candidate, + prefers_installed, + ) + + def find_candidates( + self, + requirements, # type: Sequence[Requirement] + constraint, # type: Constraint + prefers_installed, # type: bool + ): + # type: (...) -> Iterable[Candidate] + explicit_candidates = set() # type: Set[Candidate] + ireqs = [] # type: List[InstallRequirement] + for req in requirements: + cand, ireq = req.get_candidate_lookup() + if cand is not None: + explicit_candidates.add(cand) + if ireq is not None: + ireqs.append(ireq) + + # If none of the requirements want an explicit candidate, we can ask + # the finder for candidates. + if not explicit_candidates: + return self._iter_found_candidates( + ireqs, + constraint.specifier, + constraint.hashes, + prefers_installed, + ) + + return ( + c for c in explicit_candidates + if constraint.is_satisfied_by(c) + and all(req.is_satisfied_by(c) for req in requirements) + ) + + def make_requirement_from_install_req(self, ireq, requested_extras): + # type: (InstallRequirement, Iterable[str]) -> Optional[Requirement] + if not ireq.match_markers(requested_extras): + logger.info( + "Ignoring %s: markers '%s' don't match your environment", + ireq.name, ireq.markers, + ) + return None + if not ireq.link: + return SpecifierRequirement(ireq) + if ireq.link.is_wheel: + wheel = Wheel(ireq.link.filename) + if not wheel.supported(self._finder.target_python.get_tags()): + msg = "{} is not a supported wheel on this platform.".format( + wheel.filename, + ) + raise UnsupportedWheel(msg) + cand = self._make_candidate_from_link( + ireq.link, + extras=frozenset(ireq.extras), + template=ireq, + name=canonicalize_name(ireq.name) if ireq.name else None, + version=None, + ) + if cand is None: + # There's no way we can satisfy a URL requirement if the underlying + # candidate fails to build. An unnamed URL must be user-supplied, so + # we fail eagerly. If the URL is named, an unsatisfiable requirement + # can make the resolver do the right thing, either backtrack (and + # maybe find some other requirement that's buildable) or raise a + # ResolutionImpossible eventually. + if not ireq.name: + raise self._build_failures[ireq.link] + return UnsatisfiableRequirement(canonicalize_name(ireq.name)) + return self.make_requirement_from_candidate(cand) + + def make_requirement_from_candidate(self, candidate): + # type: (Candidate) -> ExplicitRequirement + return ExplicitRequirement(candidate) + + def make_requirement_from_spec( + self, + specifier, # type: str + comes_from, # type: InstallRequirement + requested_extras=(), # type: Iterable[str] + ): + # type: (...) -> Optional[Requirement] + ireq = self._make_install_req_from_spec(specifier, comes_from) + return self.make_requirement_from_install_req(ireq, requested_extras) + + def make_requires_python_requirement(self, specifier): + # type: (Optional[SpecifierSet]) -> Optional[Requirement] + if self._ignore_requires_python or specifier is None: + return None + return RequiresPythonRequirement(specifier, self._python_candidate) + + def get_wheel_cache_entry(self, link, name): + # type: (Link, Optional[str]) -> Optional[CacheEntry] + """Look up the link in the wheel cache. + + If ``preparer.require_hashes`` is True, don't use the wheel cache, + because cached wheels, always built locally, have different hashes + than the files downloaded from the index server and thus throw false + hash mismatches. Furthermore, cached wheels at present have + nondeterministic contents due to file modification times. + """ + if self._wheel_cache is None or self.preparer.require_hashes: + return None + return self._wheel_cache.get_cache_entry( + link=link, + package_name=name, + supported_tags=get_supported(), + ) + + def get_dist_to_uninstall(self, candidate): + # type: (Candidate) -> Optional[Distribution] + # TODO: Are there more cases this needs to return True? Editable? + dist = self._installed_dists.get(candidate.name) + if dist is None: # Not installed, no uninstallation required. + return None + + # We're installing into global site. The current installation must + # be uninstalled, no matter it's in global or user site, because the + # user site installation has precedence over global. + if not self._use_user_site: + return dist + + # We're installing into user site. Remove the user site installation. + if dist_in_usersite(dist): + return dist + + # We're installing into user site, but the installed incompatible + # package is in global site. We can't uninstall that, and would let + # the new user installation to "shadow" it. But shadowing won't work + # in virtual environments, so we error out. + if running_under_virtualenv() and dist_in_site_packages(dist): + raise InstallationError( + "Will not install to the user site because it will " + "lack sys.path precedence to {} in {}".format( + dist.project_name, dist.location, + ) + ) + return None + + def _report_requires_python_error( + self, + requirement, # type: RequiresPythonRequirement + template, # type: Candidate + ): + # type: (...) -> UnsupportedPythonVersion + message_format = ( + "Package {package!r} requires a different Python: " + "{version} not in {specifier!r}" + ) + message = message_format.format( + package=template.name, + version=self._python_candidate.version, + specifier=str(requirement.specifier), + ) + return UnsupportedPythonVersion(message) + + def get_installation_error(self, e): + # type: (ResolutionImpossible) -> InstallationError + + assert e.causes, "Installation error reported with no cause" + + # If one of the things we can't solve is "we need Python X.Y", + # that is what we report. + for cause in e.causes: + if isinstance(cause.requirement, RequiresPythonRequirement): + return self._report_requires_python_error( + cause.requirement, + cause.parent, + ) + + # Otherwise, we have a set of causes which can't all be satisfied + # at once. + + # The simplest case is when we have *one* cause that can't be + # satisfied. We just report that case. + if len(e.causes) == 1: + req, parent = e.causes[0] + if parent is None: + req_disp = str(req) + else: + req_disp = '{} (from {})'.format(req, parent.name) + logger.critical( + "Could not find a version that satisfies the requirement %s", + req_disp, + ) + return DistributionNotFound( + 'No matching distribution found for {}'.format(req) + ) + + # OK, we now have a list of requirements that can't all be + # satisfied at once. + + # A couple of formatting helpers + def text_join(parts): + # type: (List[str]) -> str + if len(parts) == 1: + return parts[0] + + return ", ".join(parts[:-1]) + " and " + parts[-1] + + def describe_trigger(parent): + # type: (Candidate) -> str + ireq = parent.get_install_requirement() + if not ireq or not ireq.comes_from: + return "{}=={}".format(parent.name, parent.version) + if isinstance(ireq.comes_from, InstallRequirement): + return str(ireq.comes_from.name) + return str(ireq.comes_from) + + triggers = set() + for req, parent in e.causes: + if parent is None: + # This is a root requirement, so we can report it directly + trigger = req.format_for_error() + else: + trigger = describe_trigger(parent) + triggers.add(trigger) + + if triggers: + info = text_join(sorted(triggers)) + else: + info = "the requested packages" + + msg = "Cannot install {} because these package versions " \ + "have conflicting dependencies.".format(info) + logger.critical(msg) + msg = "\nThe conflict is caused by:" + for req, parent in e.causes: + msg = msg + "\n " + if parent: + msg = msg + "{} {} depends on ".format( + parent.name, + parent.version + ) + else: + msg = msg + "The user requested " + msg = msg + req.format_for_error() + + msg = msg + "\n\n" + \ + "To fix this you could try to:\n" + \ + "1. loosen the range of package versions you've specified\n" + \ + "2. remove package versions to allow pip attempt to solve " + \ + "the dependency conflict\n" + + logger.info(msg) + + return DistributionNotFound( + "ResolutionImpossible: for help visit " + "https://pip.pypa.io/en/latest/user_guide/" + "#fixing-conflicting-dependencies" + ) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/found_candidates.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/found_candidates.py new file mode 100644 index 0000000000..50359b64fe --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/found_candidates.py @@ -0,0 +1,101 @@ +"""Utilities to lazily create and visit candidates found. + +Creating and visiting a candidate is a *very* costly operation. It involves +fetching, extracting, potentially building modules from source, and verifying +distribution metadata. It is therefore crucial for performance to keep +everything here lazy all the way down, so we only touch candidates that we +absolutely need, and not "download the world" when we only need one version of +something. +""" + +import itertools + +from pip._vendor.six.moves import collections_abc # type: ignore + +from pip._internal.utils.compat import lru_cache +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Callable, Iterator, Optional + + from .base import Candidate + + +def _insert_installed(installed, others): + # type: (Candidate, Iterator[Candidate]) -> Iterator[Candidate] + """Iterator for ``FoundCandidates``. + + This iterator is used when the resolver prefers to upgrade an + already-installed package. Candidates from index are returned in their + normal ordering, except replaced when the version is already installed. + + The implementation iterates through and yields other candidates, inserting + the installed candidate exactly once before we start yielding older or + equivalent candidates, or after all other candidates if they are all newer. + """ + installed_yielded = False + for candidate in others: + # If the installed candidate is better, yield it first. + if not installed_yielded and installed.version >= candidate.version: + yield installed + installed_yielded = True + yield candidate + + # If the installed candidate is older than all other candidates. + if not installed_yielded: + yield installed + + +class FoundCandidates(collections_abc.Sequence): + """A lazy sequence to provide candidates to the resolver. + + The intended usage is to return this from `find_matches()` so the resolver + can iterate through the sequence multiple times, but only access the index + page when remote packages are actually needed. This improve performances + when suitable candidates are already installed on disk. + """ + def __init__( + self, + get_others, # type: Callable[[], Iterator[Candidate]] + installed, # type: Optional[Candidate] + prefers_installed, # type: bool + ): + self._get_others = get_others + self._installed = installed + self._prefers_installed = prefers_installed + + def __getitem__(self, index): + # type: (int) -> Candidate + # Implemented to satisfy the ABC check. This is not needed by the + # resolver, and should not be used by the provider either (for + # performance reasons). + raise NotImplementedError("don't do this") + + def __iter__(self): + # type: () -> Iterator[Candidate] + if not self._installed: + return self._get_others() + others = ( + candidate + for candidate in self._get_others() + if candidate.version != self._installed.version + ) + if self._prefers_installed: + return itertools.chain([self._installed], others) + return _insert_installed(self._installed, others) + + def __len__(self): + # type: () -> int + # Implemented to satisfy the ABC check. This is not needed by the + # resolver, and should not be used by the provider either (for + # performance reasons). + raise NotImplementedError("don't do this") + + @lru_cache(maxsize=1) + def __bool__(self): + # type: () -> bool + if self._prefers_installed and self._installed: + return True + return any(self) + + __nonzero__ = __bool__ # XXX: Python 2. diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/provider.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/provider.py new file mode 100644 index 0000000000..40a641a2a4 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/provider.py @@ -0,0 +1,174 @@ +from pip._vendor.resolvelib.providers import AbstractProvider + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +from .base import Constraint + +if MYPY_CHECK_RUNNING: + from typing import Any, Dict, Iterable, Optional, Sequence, Set, Tuple, Union + + from .base import Candidate, Requirement + from .factory import Factory + +# Notes on the relationship between the provider, the factory, and the +# candidate and requirement classes. +# +# The provider is a direct implementation of the resolvelib class. Its role +# is to deliver the API that resolvelib expects. +# +# Rather than work with completely abstract "requirement" and "candidate" +# concepts as resolvelib does, pip has concrete classes implementing these two +# ideas. The API of Requirement and Candidate objects are defined in the base +# classes, but essentially map fairly directly to the equivalent provider +# methods. In particular, `find_matches` and `is_satisfied_by` are +# requirement methods, and `get_dependencies` is a candidate method. +# +# The factory is the interface to pip's internal mechanisms. It is stateless, +# and is created by the resolver and held as a property of the provider. It is +# responsible for creating Requirement and Candidate objects, and provides +# services to those objects (access to pip's finder and preparer). + + +class PipProvider(AbstractProvider): + """Pip's provider implementation for resolvelib. + + :params constraints: A mapping of constraints specified by the user. Keys + are canonicalized project names. + :params ignore_dependencies: Whether the user specified ``--no-deps``. + :params upgrade_strategy: The user-specified upgrade strategy. + :params user_requested: A set of canonicalized package names that the user + supplied for pip to install/upgrade. + """ + + def __init__( + self, + factory, # type: Factory + constraints, # type: Dict[str, Constraint] + ignore_dependencies, # type: bool + upgrade_strategy, # type: str + user_requested, # type: Set[str] + ): + # type: (...) -> None + self._factory = factory + self._constraints = constraints + self._ignore_dependencies = ignore_dependencies + self._upgrade_strategy = upgrade_strategy + self._user_requested = user_requested + + def identify(self, dependency): + # type: (Union[Requirement, Candidate]) -> str + return dependency.name + + def get_preference( + self, + resolution, # type: Optional[Candidate] + candidates, # type: Sequence[Candidate] + information # type: Sequence[Tuple[Requirement, Candidate]] + ): + # type: (...) -> Any + """Produce a sort key for given requirement based on preference. + + The lower the return value is, the more preferred this group of + arguments is. + + Currently pip considers the followings in order: + + * Prefer if any of the known requirements points to an explicit URL. + * If equal, prefer if any requirements contain ``===`` and ``==``. + * If equal, prefer if requirements include version constraints, e.g. + ``>=`` and ``<``. + * If equal, prefer user-specified (non-transitive) requirements. + * If equal, order alphabetically for consistency (helps debuggability). + """ + + def _get_restrictive_rating(requirements): + # type: (Iterable[Requirement]) -> int + """Rate how restrictive a set of requirements are. + + ``Requirement.get_candidate_lookup()`` returns a 2-tuple for + lookup. The first element is ``Optional[Candidate]`` and the + second ``Optional[InstallRequirement]``. + + * If the requirement is an explicit one, the explicitly-required + candidate is returned as the first element. + * If the requirement is based on a PEP 508 specifier, the backing + ``InstallRequirement`` is returned as the second element. + + We use the first element to check whether there is an explicit + requirement, and the second for equality operator. + """ + lookups = (r.get_candidate_lookup() for r in requirements) + cands, ireqs = zip(*lookups) + if any(cand is not None for cand in cands): + return 0 + spec_sets = (ireq.specifier for ireq in ireqs if ireq) + operators = [ + specifier.operator + for spec_set in spec_sets + for specifier in spec_set + ] + if any(op in ("==", "===") for op in operators): + return 1 + if operators: + return 2 + # A "bare" requirement without any version requirements. + return 3 + + restrictive = _get_restrictive_rating(req for req, _ in information) + transitive = all(parent is not None for _, parent in information) + key = next(iter(candidates)).name if candidates else "" + + # HACK: Setuptools have a very long and solid backward compatibility + # track record, and extremely few projects would request a narrow, + # non-recent version range of it since that would break a lot things. + # (Most projects specify it only to request for an installer feature, + # which does not work, but that's another topic.) Intentionally + # delaying Setuptools helps reduce branches the resolver has to check. + # This serves as a temporary fix for issues like "apache-airlfow[all]" + # while we work on "proper" branch pruning techniques. + delay_this = (key == "setuptools") + + return (delay_this, restrictive, transitive, key) + + def find_matches(self, requirements): + # type: (Sequence[Requirement]) -> Iterable[Candidate] + if not requirements: + return [] + name = requirements[0].project_name + + def _eligible_for_upgrade(name): + # type: (str) -> bool + """Are upgrades allowed for this project? + + This checks the upgrade strategy, and whether the project was one + that the user specified in the command line, in order to decide + whether we should upgrade if there's a newer version available. + + (Note that we don't need access to the `--upgrade` flag, because + an upgrade strategy of "to-satisfy-only" means that `--upgrade` + was not specified). + """ + if self._upgrade_strategy == "eager": + return True + elif self._upgrade_strategy == "only-if-needed": + return (name in self._user_requested) + return False + + return self._factory.find_candidates( + requirements, + constraint=self._constraints.get(name, Constraint.empty()), + prefers_installed=(not _eligible_for_upgrade(name)), + ) + + def is_satisfied_by(self, requirement, candidate): + # type: (Requirement, Candidate) -> bool + return requirement.is_satisfied_by(candidate) + + def get_dependencies(self, candidate): + # type: (Candidate) -> Sequence[Requirement] + with_requires = not self._ignore_dependencies + return [ + r + for r in candidate.iter_dependencies(with_requires) + if r is not None + ] diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/reporter.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/reporter.py new file mode 100644 index 0000000000..d0ef3fadc6 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/reporter.py @@ -0,0 +1,84 @@ +from collections import defaultdict +from logging import getLogger + +from pip._vendor.resolvelib.reporters import BaseReporter + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Any, DefaultDict + + from .base import Candidate, Requirement + + +logger = getLogger(__name__) + + +class PipReporter(BaseReporter): + + def __init__(self): + # type: () -> None + self.backtracks_by_package = defaultdict(int) # type: DefaultDict[str, int] + + self._messages_at_backtrack = { + 1: ( + "pip is looking at multiple versions of {package_name} to " + "determine which version is compatible with other " + "requirements. This could take a while." + ), + 8: ( + "pip is looking at multiple versions of {package_name} to " + "determine which version is compatible with other " + "requirements. This could take a while." + ), + 13: ( + "This is taking longer than usual. You might need to provide " + "the dependency resolver with stricter constraints to reduce " + "runtime. If you want to abort this run, you can press " + "Ctrl + C to do so. To improve how pip performs, tell us what " + "happened here: https://pip.pypa.io/surveys/backtracking" + ) + } + + def backtracking(self, candidate): + # type: (Candidate) -> None + self.backtracks_by_package[candidate.name] += 1 + + count = self.backtracks_by_package[candidate.name] + if count not in self._messages_at_backtrack: + return + + message = self._messages_at_backtrack[count] + logger.info("INFO: %s", message.format(package_name=candidate.name)) + + +class PipDebuggingReporter(BaseReporter): + """A reporter that does an info log for every event it sees.""" + + def starting(self): + # type: () -> None + logger.info("Reporter.starting()") + + def starting_round(self, index): + # type: (int) -> None + logger.info("Reporter.starting_round(%r)", index) + + def ending_round(self, index, state): + # type: (int, Any) -> None + logger.info("Reporter.ending_round(%r, state)", index) + + def ending(self, state): + # type: (Any) -> None + logger.info("Reporter.ending(%r)", state) + + def adding_requirement(self, requirement, parent): + # type: (Requirement, Candidate) -> None + logger.info("Reporter.adding_requirement(%r, %r)", requirement, parent) + + def backtracking(self, candidate): + # type: (Candidate) -> None + logger.info("Reporter.backtracking(%r)", candidate) + + def pinning(self, candidate): + # type: (Candidate) -> None + logger.info("Reporter.pinning(%r)", candidate) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/requirements.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/requirements.py new file mode 100644 index 0000000000..1229f35375 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/requirements.py @@ -0,0 +1,201 @@ +from pip._vendor.packaging.utils import canonicalize_name + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +from .base import Requirement, format_name + +if MYPY_CHECK_RUNNING: + from pip._vendor.packaging.specifiers import SpecifierSet + + from pip._internal.req.req_install import InstallRequirement + + from .base import Candidate, CandidateLookup + + +class ExplicitRequirement(Requirement): + def __init__(self, candidate): + # type: (Candidate) -> None + self.candidate = candidate + + def __str__(self): + # type: () -> str + return str(self.candidate) + + def __repr__(self): + # type: () -> str + return "{class_name}({candidate!r})".format( + class_name=self.__class__.__name__, + candidate=self.candidate, + ) + + @property + def project_name(self): + # type: () -> str + # No need to canonicalise - the candidate did this + return self.candidate.project_name + + @property + def name(self): + # type: () -> str + # No need to canonicalise - the candidate did this + return self.candidate.name + + def format_for_error(self): + # type: () -> str + return self.candidate.format_for_error() + + def get_candidate_lookup(self): + # type: () -> CandidateLookup + return self.candidate, None + + def is_satisfied_by(self, candidate): + # type: (Candidate) -> bool + return candidate == self.candidate + + +class SpecifierRequirement(Requirement): + def __init__(self, ireq): + # type: (InstallRequirement) -> None + assert ireq.link is None, "This is a link, not a specifier" + self._ireq = ireq + self._extras = frozenset(ireq.extras) + + def __str__(self): + # type: () -> str + return str(self._ireq.req) + + def __repr__(self): + # type: () -> str + return "{class_name}({requirement!r})".format( + class_name=self.__class__.__name__, + requirement=str(self._ireq.req), + ) + + @property + def project_name(self): + # type: () -> str + return canonicalize_name(self._ireq.req.name) + + @property + def name(self): + # type: () -> str + return format_name(self.project_name, self._extras) + + def format_for_error(self): + # type: () -> str + + # Convert comma-separated specifiers into "A, B, ..., F and G" + # This makes the specifier a bit more "human readable", without + # risking a change in meaning. (Hopefully! Not all edge cases have + # been checked) + parts = [s.strip() for s in str(self).split(",")] + if len(parts) == 0: + return "" + elif len(parts) == 1: + return parts[0] + + return ", ".join(parts[:-1]) + " and " + parts[-1] + + def get_candidate_lookup(self): + # type: () -> CandidateLookup + return None, self._ireq + + def is_satisfied_by(self, candidate): + # type: (Candidate) -> bool + assert candidate.name == self.name, \ + "Internal issue: Candidate is not for this requirement " \ + " {} vs {}".format(candidate.name, self.name) + # We can safely always allow prereleases here since PackageFinder + # already implements the prerelease logic, and would have filtered out + # prerelease candidates if the user does not expect them. + spec = self._ireq.req.specifier + return spec.contains(candidate.version, prereleases=True) + + +class RequiresPythonRequirement(Requirement): + """A requirement representing Requires-Python metadata. + """ + def __init__(self, specifier, match): + # type: (SpecifierSet, Candidate) -> None + self.specifier = specifier + self._candidate = match + + def __str__(self): + # type: () -> str + return "Python {}".format(self.specifier) + + def __repr__(self): + # type: () -> str + return "{class_name}({specifier!r})".format( + class_name=self.__class__.__name__, + specifier=str(self.specifier), + ) + + @property + def project_name(self): + # type: () -> str + return self._candidate.project_name + + @property + def name(self): + # type: () -> str + return self._candidate.name + + def format_for_error(self): + # type: () -> str + return str(self) + + def get_candidate_lookup(self): + # type: () -> CandidateLookup + if self.specifier.contains(self._candidate.version, prereleases=True): + return self._candidate, None + return None, None + + def is_satisfied_by(self, candidate): + # type: (Candidate) -> bool + assert candidate.name == self._candidate.name, "Not Python candidate" + # We can safely always allow prereleases here since PackageFinder + # already implements the prerelease logic, and would have filtered out + # prerelease candidates if the user does not expect them. + return self.specifier.contains(candidate.version, prereleases=True) + + +class UnsatisfiableRequirement(Requirement): + """A requirement that cannot be satisfied. + """ + def __init__(self, name): + # type: (str) -> None + self._name = name + + def __str__(self): + # type: () -> str + return "{} (unavailable)".format(self._name) + + def __repr__(self): + # type: () -> str + return "{class_name}({name!r})".format( + class_name=self.__class__.__name__, + name=str(self._name), + ) + + @property + def project_name(self): + # type: () -> str + return self._name + + @property + def name(self): + # type: () -> str + return self._name + + def format_for_error(self): + # type: () -> str + return str(self) + + def get_candidate_lookup(self): + # type: () -> CandidateLookup + return None, None + + def is_satisfied_by(self, candidate): + # type: (Candidate) -> bool + return False diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/resolver.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/resolver.py new file mode 100644 index 0000000000..84421d43f8 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/resolver.py @@ -0,0 +1,297 @@ +import functools +import logging +import os + +from pip._vendor import six +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.packaging.version import parse as parse_version +from pip._vendor.resolvelib import ResolutionImpossible +from pip._vendor.resolvelib import Resolver as RLResolver + +from pip._internal.exceptions import InstallationError +from pip._internal.req.req_install import check_invalid_constraint_type +from pip._internal.req.req_set import RequirementSet +from pip._internal.resolution.base import BaseResolver +from pip._internal.resolution.resolvelib.provider import PipProvider +from pip._internal.resolution.resolvelib.reporter import ( + PipDebuggingReporter, + PipReporter, +) +from pip._internal.utils.deprecation import deprecated +from pip._internal.utils.filetypes import is_archive_file +from pip._internal.utils.misc import dist_is_editable +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +from .base import Constraint +from .factory import Factory + +if MYPY_CHECK_RUNNING: + from typing import Dict, List, Optional, Set, Tuple + + from pip._vendor.resolvelib.resolvers import Result + from pip._vendor.resolvelib.structs import Graph + + from pip._internal.cache import WheelCache + from pip._internal.index.package_finder import PackageFinder + from pip._internal.operations.prepare import RequirementPreparer + from pip._internal.req.req_install import InstallRequirement + from pip._internal.resolution.base import InstallRequirementProvider + + +logger = logging.getLogger(__name__) + + +class Resolver(BaseResolver): + _allowed_strategies = {"eager", "only-if-needed", "to-satisfy-only"} + + def __init__( + self, + preparer, # type: RequirementPreparer + finder, # type: PackageFinder + wheel_cache, # type: Optional[WheelCache] + make_install_req, # type: InstallRequirementProvider + use_user_site, # type: bool + ignore_dependencies, # type: bool + ignore_installed, # type: bool + ignore_requires_python, # type: bool + force_reinstall, # type: bool + upgrade_strategy, # type: str + py_version_info=None, # type: Optional[Tuple[int, ...]] + ): + super(Resolver, self).__init__() + assert upgrade_strategy in self._allowed_strategies + + self.factory = Factory( + finder=finder, + preparer=preparer, + make_install_req=make_install_req, + wheel_cache=wheel_cache, + use_user_site=use_user_site, + force_reinstall=force_reinstall, + ignore_installed=ignore_installed, + ignore_requires_python=ignore_requires_python, + py_version_info=py_version_info, + ) + self.ignore_dependencies = ignore_dependencies + self.upgrade_strategy = upgrade_strategy + self._result = None # type: Optional[Result] + + def resolve(self, root_reqs, check_supported_wheels): + # type: (List[InstallRequirement], bool) -> RequirementSet + + constraints = {} # type: Dict[str, Constraint] + user_requested = set() # type: Set[str] + requirements = [] + for req in root_reqs: + if req.constraint: + # Ensure we only accept valid constraints + problem = check_invalid_constraint_type(req) + if problem: + raise InstallationError(problem) + if not req.match_markers(): + continue + name = canonicalize_name(req.name) + if name in constraints: + constraints[name] &= req + else: + constraints[name] = Constraint.from_ireq(req) + else: + if req.user_supplied and req.name: + user_requested.add(canonicalize_name(req.name)) + r = self.factory.make_requirement_from_install_req( + req, requested_extras=(), + ) + if r is not None: + requirements.append(r) + + provider = PipProvider( + factory=self.factory, + constraints=constraints, + ignore_dependencies=self.ignore_dependencies, + upgrade_strategy=self.upgrade_strategy, + user_requested=user_requested, + ) + if "PIP_RESOLVER_DEBUG" in os.environ: + reporter = PipDebuggingReporter() + else: + reporter = PipReporter() + resolver = RLResolver(provider, reporter) + + try: + try_to_avoid_resolution_too_deep = 2000000 + self._result = resolver.resolve( + requirements, max_rounds=try_to_avoid_resolution_too_deep, + ) + + except ResolutionImpossible as e: + error = self.factory.get_installation_error(e) + six.raise_from(error, e) + + req_set = RequirementSet(check_supported_wheels=check_supported_wheels) + for candidate in self._result.mapping.values(): + ireq = candidate.get_install_requirement() + if ireq is None: + continue + + # Check if there is already an installation under the same name, + # and set a flag for later stages to uninstall it, if needed. + installed_dist = self.factory.get_dist_to_uninstall(candidate) + if installed_dist is None: + # There is no existing installation -- nothing to uninstall. + ireq.should_reinstall = False + elif self.factory.force_reinstall: + # The --force-reinstall flag is set -- reinstall. + ireq.should_reinstall = True + elif parse_version(installed_dist.version) != candidate.version: + # The installation is different in version -- reinstall. + ireq.should_reinstall = True + elif candidate.is_editable or dist_is_editable(installed_dist): + # The incoming distribution is editable, or different in + # editable-ness to installation -- reinstall. + ireq.should_reinstall = True + elif candidate.source_link.is_file: + # The incoming distribution is under file:// + if candidate.source_link.is_wheel: + # is a local wheel -- do nothing. + logger.info( + "%s is already installed with the same version as the " + "provided wheel. Use --force-reinstall to force an " + "installation of the wheel.", + ireq.name, + ) + continue + + looks_like_sdist = ( + is_archive_file(candidate.source_link.file_path) + and candidate.source_link.ext != ".zip" + ) + if looks_like_sdist: + # is a local sdist -- show a deprecation warning! + reason = ( + "Source distribution is being reinstalled despite an " + "installed package having the same name and version as " + "the installed package." + ) + replacement = "use --force-reinstall" + deprecated( + reason=reason, + replacement=replacement, + gone_in="21.1", + issue=8711, + ) + + # is a local sdist or path -- reinstall + ireq.should_reinstall = True + else: + continue + + link = candidate.source_link + if link and link.is_yanked: + # The reason can contain non-ASCII characters, Unicode + # is required for Python 2. + msg = ( + u'The candidate selected for download or install is a ' + u'yanked version: {name!r} candidate (version {version} ' + u'at {link})\nReason for being yanked: {reason}' + ).format( + name=candidate.name, + version=candidate.version, + link=link, + reason=link.yanked_reason or u'', + ) + logger.warning(msg) + + req_set.add_named_requirement(ireq) + + reqs = req_set.all_requirements + self.factory.preparer.prepare_linked_requirements_more(reqs) + return req_set + + def get_installation_order(self, req_set): + # type: (RequirementSet) -> List[InstallRequirement] + """Get order for installation of requirements in RequirementSet. + + The returned list contains a requirement before another that depends on + it. This helps ensure that the environment is kept consistent as they + get installed one-by-one. + + The current implementation creates a topological ordering of the + dependency graph, while breaking any cycles in the graph at arbitrary + points. We make no guarantees about where the cycle would be broken, + other than they would be broken. + """ + assert self._result is not None, "must call resolve() first" + + graph = self._result.graph + weights = get_topological_weights( + graph, + expected_node_count=len(self._result.mapping) + 1, + ) + + sorted_items = sorted( + req_set.requirements.items(), + key=functools.partial(_req_set_item_sorter, weights=weights), + reverse=True, + ) + return [ireq for _, ireq in sorted_items] + + +def get_topological_weights(graph, expected_node_count): + # type: (Graph, int) -> Dict[Optional[str], int] + """Assign weights to each node based on how "deep" they are. + + This implementation may change at any point in the future without prior + notice. + + We take the length for the longest path to any node from root, ignoring any + paths that contain a single node twice (i.e. cycles). This is done through + a depth-first search through the graph, while keeping track of the path to + the node. + + Cycles in the graph result would result in node being revisited while also + being it's own path. In this case, take no action. This helps ensure we + don't get stuck in a cycle. + + When assigning weight, the longer path (i.e. larger length) is preferred. + """ + path = set() # type: Set[Optional[str]] + weights = {} # type: Dict[Optional[str], int] + + def visit(node): + # type: (Optional[str]) -> None + if node in path: + # We hit a cycle, so we'll break it here. + return + + # Time to visit the children! + path.add(node) + for child in graph.iter_children(node): + visit(child) + path.remove(node) + + last_known_parent_count = weights.get(node, 0) + weights[node] = max(last_known_parent_count, len(path)) + + # `None` is guaranteed to be the root node by resolvelib. + visit(None) + + # Sanity checks + assert weights[None] == 0 + assert len(weights) == expected_node_count + + return weights + + +def _req_set_item_sorter( + item, # type: Tuple[str, InstallRequirement] + weights, # type: Dict[Optional[str], int] +): + # type: (...) -> Tuple[int, str] + """Key function used to sort install requirements for installation. + + Based on the "weight" mapping calculated in ``get_installation_order()``. + The canonical package name is returned as the second member as a tie- + breaker to ensure the result is predictable, which is useful in tests. + """ + name = canonicalize_name(item[0]) + return weights[name], name diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/self_outdated_check.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/self_outdated_check.py new file mode 100644 index 0000000000..c2d166b184 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/self_outdated_check.py @@ -0,0 +1,197 @@ +from __future__ import absolute_import + +import datetime +import hashlib +import json +import logging +import os.path +import sys + +from pip._vendor.packaging import version as packaging_version +from pip._vendor.six import ensure_binary + +from pip._internal.index.collector import LinkCollector +from pip._internal.index.package_finder import PackageFinder +from pip._internal.models.selection_prefs import SelectionPreferences +from pip._internal.utils.filesystem import adjacent_tmp_file, check_path_owner, replace +from pip._internal.utils.misc import ensure_dir, get_distribution, get_installed_version +from pip._internal.utils.packaging import get_installer +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + import optparse + from typing import Any, Dict, Text, Union + + from pip._internal.network.session import PipSession + + +SELFCHECK_DATE_FMT = "%Y-%m-%dT%H:%M:%SZ" + + +logger = logging.getLogger(__name__) + + +def _get_statefile_name(key): + # type: (Union[str, Text]) -> str + key_bytes = ensure_binary(key) + name = hashlib.sha224(key_bytes).hexdigest() + return name + + +class SelfCheckState(object): + def __init__(self, cache_dir): + # type: (str) -> None + self.state = {} # type: Dict[str, Any] + self.statefile_path = None + + # Try to load the existing state + if cache_dir: + self.statefile_path = os.path.join( + cache_dir, "selfcheck", _get_statefile_name(self.key) + ) + try: + with open(self.statefile_path) as statefile: + self.state = json.load(statefile) + except (IOError, ValueError, KeyError): + # Explicitly suppressing exceptions, since we don't want to + # error out if the cache file is invalid. + pass + + @property + def key(self): + # type: () -> str + return sys.prefix + + def save(self, pypi_version, current_time): + # type: (str, datetime.datetime) -> None + # If we do not have a path to cache in, don't bother saving. + if not self.statefile_path: + return + + # Check to make sure that we own the directory + if not check_path_owner(os.path.dirname(self.statefile_path)): + return + + # Now that we've ensured the directory is owned by this user, we'll go + # ahead and make sure that all our directories are created. + ensure_dir(os.path.dirname(self.statefile_path)) + + state = { + # Include the key so it's easy to tell which pip wrote the + # file. + "key": self.key, + "last_check": current_time.strftime(SELFCHECK_DATE_FMT), + "pypi_version": pypi_version, + } + + text = json.dumps(state, sort_keys=True, separators=(",", ":")) + + with adjacent_tmp_file(self.statefile_path) as f: + f.write(ensure_binary(text)) + + try: + # Since we have a prefix-specific state file, we can just + # overwrite whatever is there, no need to check. + replace(f.name, self.statefile_path) + except OSError: + # Best effort. + pass + + +def was_installed_by_pip(pkg): + # type: (str) -> bool + """Checks whether pkg was installed by pip + + This is used not to display the upgrade message when pip is in fact + installed by system package manager, such as dnf on Fedora. + """ + dist = get_distribution(pkg) + if not dist: + return False + return "pip" == get_installer(dist) + + +def pip_self_version_check(session, options): + # type: (PipSession, optparse.Values) -> None + """Check for an update for pip. + + Limit the frequency of checks to once per week. State is stored either in + the active virtualenv or in the user's USER_CACHE_DIR keyed off the prefix + of the pip script path. + """ + installed_version = get_installed_version("pip") + if not installed_version: + return + + pip_version = packaging_version.parse(installed_version) + pypi_version = None + + try: + state = SelfCheckState(cache_dir=options.cache_dir) + + current_time = datetime.datetime.utcnow() + # Determine if we need to refresh the state + if "last_check" in state.state and "pypi_version" in state.state: + last_check = datetime.datetime.strptime( + state.state["last_check"], + SELFCHECK_DATE_FMT + ) + if (current_time - last_check).total_seconds() < 7 * 24 * 60 * 60: + pypi_version = state.state["pypi_version"] + + # Refresh the version if we need to or just see if we need to warn + if pypi_version is None: + # Lets use PackageFinder to see what the latest pip version is + link_collector = LinkCollector.create( + session, + options=options, + suppress_no_index=True, + ) + + # Pass allow_yanked=False so we don't suggest upgrading to a + # yanked version. + selection_prefs = SelectionPreferences( + allow_yanked=False, + allow_all_prereleases=False, # Explicitly set to False + ) + + finder = PackageFinder.create( + link_collector=link_collector, + selection_prefs=selection_prefs, + ) + best_candidate = finder.find_best_candidate("pip").best_candidate + if best_candidate is None: + return + pypi_version = str(best_candidate.version) + + # save that we've performed a check + state.save(pypi_version, current_time) + + remote_version = packaging_version.parse(pypi_version) + + local_version_is_older = ( + pip_version < remote_version and + pip_version.base_version != remote_version.base_version and + was_installed_by_pip('pip') + ) + + # Determine if our pypi_version is older + if not local_version_is_older: + return + + # We cannot tell how the current pip is available in the current + # command context, so be pragmatic here and suggest the command + # that's always available. This does not accommodate spaces in + # `sys.executable`. + pip_cmd = "{} -m pip".format(sys.executable) + logger.warning( + "You are using pip version %s; however, version %s is " + "available.\nYou should consider upgrading via the " + "'%s install --upgrade pip' command.", + pip_version, pypi_version, pip_cmd + ) + except Exception: + logger.debug( + "There was an error checking the latest version of pip", + exc_info=True, + ) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/__init__.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/appdirs.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/appdirs.py new file mode 100644 index 0000000000..3989ed31c3 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/appdirs.py @@ -0,0 +1,44 @@ +""" +This code wraps the vendored appdirs module to so the return values are +compatible for the current pip code base. + +The intention is to rewrite current usages gradually, keeping the tests pass, +and eventually drop this after all usages are changed. +""" + +from __future__ import absolute_import + +import os + +from pip._vendor import appdirs as _appdirs + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List + + +def user_cache_dir(appname): + # type: (str) -> str + return _appdirs.user_cache_dir(appname, appauthor=False) + + +def user_config_dir(appname, roaming=True): + # type: (str, bool) -> str + path = _appdirs.user_config_dir(appname, appauthor=False, roaming=roaming) + if _appdirs.system == "darwin" and not os.path.isdir(path): + path = os.path.expanduser('~/.config/') + if appname: + path = os.path.join(path, appname) + return path + + +# for the discussion regarding site_config_dir locations +# see +def site_config_dirs(appname): + # type: (str) -> List[str] + dirval = _appdirs.site_config_dir(appname, appauthor=False, multipath=True) + if _appdirs.system not in ["win32", "darwin"]: + # always look in /etc directly as well + return dirval.split(os.pathsep) + ['/etc'] + return [dirval] diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/compat.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/compat.py new file mode 100644 index 0000000000..2196e6e0ae --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/compat.py @@ -0,0 +1,293 @@ +"""Stuff that differs in different Python versions and platform +distributions.""" + +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False + +from __future__ import absolute_import, division + +import codecs +import functools +import locale +import logging +import os +import shutil +import sys + +from pip._vendor.six import PY2, text_type + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Callable, Optional, Protocol, Text, Tuple, TypeVar, Union + + # Used in the @lru_cache polyfill. + F = TypeVar('F') + + class LruCache(Protocol): + def __call__(self, maxsize=None): + # type: (Optional[int]) -> Callable[[F], F] + raise NotImplementedError + +try: + import ipaddress +except ImportError: + try: + from pip._vendor import ipaddress # type: ignore + except ImportError: + import ipaddr as ipaddress # type: ignore + ipaddress.ip_address = ipaddress.IPAddress # type: ignore + ipaddress.ip_network = ipaddress.IPNetwork # type: ignore + + +__all__ = [ + "ipaddress", "uses_pycache", "console_to_str", + "get_path_uid", "stdlib_pkgs", "WINDOWS", "samefile", "get_terminal_size", +] + + +logger = logging.getLogger(__name__) + +if PY2: + import imp + + try: + cache_from_source = imp.cache_from_source # type: ignore + except AttributeError: + # does not use __pycache__ + cache_from_source = None + + uses_pycache = cache_from_source is not None +else: + uses_pycache = True + from importlib.util import cache_from_source + + +if PY2: + # In Python 2.7, backslashreplace exists + # but does not support use for decoding. + # We implement our own replace handler for this + # situation, so that we can consistently use + # backslash replacement for all versions. + def backslashreplace_decode_fn(err): + raw_bytes = (err.object[i] for i in range(err.start, err.end)) + # Python 2 gave us characters - convert to numeric bytes + raw_bytes = (ord(b) for b in raw_bytes) + return u"".join(map(u"\\x{:x}".format, raw_bytes)), err.end + codecs.register_error( + "backslashreplace_decode", + backslashreplace_decode_fn, + ) + backslashreplace_decode = "backslashreplace_decode" +else: + backslashreplace_decode = "backslashreplace" + + +def has_tls(): + # type: () -> bool + try: + import _ssl # noqa: F401 # ignore unused + return True + except ImportError: + pass + + from pip._vendor.urllib3.util import IS_PYOPENSSL + return IS_PYOPENSSL + + +def str_to_display(data, desc=None): + # type: (Union[bytes, Text], Optional[str]) -> Text + """ + For display or logging purposes, convert a bytes object (or text) to + text (e.g. unicode in Python 2) safe for output. + + :param desc: An optional phrase describing the input data, for use in + the log message if a warning is logged. Defaults to "Bytes object". + + This function should never error out and so can take a best effort + approach. It is okay to be lossy if needed since the return value is + just for display. + + We assume the data is in the locale preferred encoding. If it won't + decode properly, we warn the user but decode as best we can. + + We also ensure that the output can be safely written to standard output + without encoding errors. + """ + if isinstance(data, text_type): + return data + + # Otherwise, data is a bytes object (str in Python 2). + # First, get the encoding we assume. This is the preferred + # encoding for the locale, unless that is not found, or + # it is ASCII, in which case assume UTF-8 + encoding = locale.getpreferredencoding() + if (not encoding) or codecs.lookup(encoding).name == "ascii": + encoding = "utf-8" + + # Now try to decode the data - if we fail, warn the user and + # decode with replacement. + try: + decoded_data = data.decode(encoding) + except UnicodeDecodeError: + logger.warning( + '%s does not appear to be encoded as %s', + desc or 'Bytes object', + encoding, + ) + decoded_data = data.decode(encoding, errors=backslashreplace_decode) + + # Make sure we can print the output, by encoding it to the output + # encoding with replacement of unencodable characters, and then + # decoding again. + # We use stderr's encoding because it's less likely to be + # redirected and if we don't find an encoding we skip this + # step (on the assumption that output is wrapped by something + # that won't fail). + # The double getattr is to deal with the possibility that we're + # being called in a situation where sys.__stderr__ doesn't exist, + # or doesn't have an encoding attribute. Neither of these cases + # should occur in normal pip use, but there's no harm in checking + # in case people use pip in (unsupported) unusual situations. + output_encoding = getattr(getattr(sys, "__stderr__", None), + "encoding", None) + + if output_encoding: + output_encoded = decoded_data.encode( + output_encoding, + errors="backslashreplace" + ) + decoded_data = output_encoded.decode(output_encoding) + + return decoded_data + + +def console_to_str(data): + # type: (bytes) -> Text + """Return a string, safe for output, of subprocess output. + """ + return str_to_display(data, desc='Subprocess output') + + +def get_path_uid(path): + # type: (str) -> int + """ + Return path's uid. + + Does not follow symlinks: + https://github.com/pypa/pip/pull/935#discussion_r5307003 + + Placed this function in compat due to differences on AIX and + Jython, that should eventually go away. + + :raises OSError: When path is a symlink or can't be read. + """ + if hasattr(os, 'O_NOFOLLOW'): + fd = os.open(path, os.O_RDONLY | os.O_NOFOLLOW) + file_uid = os.fstat(fd).st_uid + os.close(fd) + else: # AIX and Jython + # WARNING: time of check vulnerability, but best we can do w/o NOFOLLOW + if not os.path.islink(path): + # older versions of Jython don't have `os.fstat` + file_uid = os.stat(path).st_uid + else: + # raise OSError for parity with os.O_NOFOLLOW above + raise OSError( + "{} is a symlink; Will not return uid for symlinks".format( + path) + ) + return file_uid + + +def expanduser(path): + # type: (str) -> str + """ + Expand ~ and ~user constructions. + + Includes a workaround for https://bugs.python.org/issue14768 + """ + expanded = os.path.expanduser(path) + if path.startswith('~/') and expanded.startswith('//'): + expanded = expanded[1:] + return expanded + + +# packages in the stdlib that may have installation metadata, but should not be +# considered 'installed'. this theoretically could be determined based on +# dist.location (py27:`sysconfig.get_paths()['stdlib']`, +# py26:sysconfig.get_config_vars('LIBDEST')), but fear platform variation may +# make this ineffective, so hard-coding +stdlib_pkgs = {"python", "wsgiref", "argparse"} + + +# windows detection, covers cpython and ironpython +WINDOWS = (sys.platform.startswith("win") or + (sys.platform == 'cli' and os.name == 'nt')) + + +def samefile(file1, file2): + # type: (str, str) -> bool + """Provide an alternative for os.path.samefile on Windows/Python2""" + if hasattr(os.path, 'samefile'): + return os.path.samefile(file1, file2) + else: + path1 = os.path.normcase(os.path.abspath(file1)) + path2 = os.path.normcase(os.path.abspath(file2)) + return path1 == path2 + + +if hasattr(shutil, 'get_terminal_size'): + def get_terminal_size(): + # type: () -> Tuple[int, int] + """ + Returns a tuple (x, y) representing the width(x) and the height(y) + in characters of the terminal window. + """ + return tuple(shutil.get_terminal_size()) # type: ignore +else: + def get_terminal_size(): + # type: () -> Tuple[int, int] + """ + Returns a tuple (x, y) representing the width(x) and the height(y) + in characters of the terminal window. + """ + def ioctl_GWINSZ(fd): + try: + import fcntl + import struct + import termios + cr = struct.unpack_from( + 'hh', + fcntl.ioctl(fd, termios.TIOCGWINSZ, '12345678') + ) + except Exception: + return None + if cr == (0, 0): + return None + return cr + cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2) + if not cr: + if sys.platform != "win32": + try: + fd = os.open(os.ctermid(), os.O_RDONLY) + cr = ioctl_GWINSZ(fd) + os.close(fd) + except Exception: + pass + if not cr: + cr = (os.environ.get('LINES', 25), os.environ.get('COLUMNS', 80)) + return int(cr[1]), int(cr[0]) + + +# Fallback to noop_lru_cache in Python 2 +# TODO: this can be removed when python 2 support is dropped! +def noop_lru_cache(maxsize=None): + # type: (Optional[int]) -> Callable[[F], F] + def _wrapper(f): + # type: (F) -> F + return f + return _wrapper + + +lru_cache = getattr(functools, "lru_cache", noop_lru_cache) # type: LruCache diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/compatibility_tags.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/compatibility_tags.py new file mode 100644 index 0000000000..6780f9d9d6 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/compatibility_tags.py @@ -0,0 +1,178 @@ +"""Generate and work with PEP 425 Compatibility Tags. +""" + +from __future__ import absolute_import + +import re + +from pip._vendor.packaging.tags import ( + Tag, + compatible_tags, + cpython_tags, + generic_tags, + interpreter_name, + interpreter_version, + mac_platforms, +) + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List, Optional, Tuple + + from pip._vendor.packaging.tags import PythonVersion + +_osx_arch_pat = re.compile(r'(.+)_(\d+)_(\d+)_(.+)') + + +def version_info_to_nodot(version_info): + # type: (Tuple[int, ...]) -> str + # Only use up to the first two numbers. + return ''.join(map(str, version_info[:2])) + + +def _mac_platforms(arch): + # type: (str) -> List[str] + match = _osx_arch_pat.match(arch) + if match: + name, major, minor, actual_arch = match.groups() + mac_version = (int(major), int(minor)) + arches = [ + # Since we have always only checked that the platform starts + # with "macosx", for backwards-compatibility we extract the + # actual prefix provided by the user in case they provided + # something like "macosxcustom_". It may be good to remove + # this as undocumented or deprecate it in the future. + '{}_{}'.format(name, arch[len('macosx_'):]) + for arch in mac_platforms(mac_version, actual_arch) + ] + else: + # arch pattern didn't match (?!) + arches = [arch] + return arches + + +def _custom_manylinux_platforms(arch): + # type: (str) -> List[str] + arches = [arch] + arch_prefix, arch_sep, arch_suffix = arch.partition('_') + if arch_prefix == 'manylinux2014': + # manylinux1/manylinux2010 wheels run on most manylinux2014 systems + # with the exception of wheels depending on ncurses. PEP 599 states + # manylinux1/manylinux2010 wheels should be considered + # manylinux2014 wheels: + # https://www.python.org/dev/peps/pep-0599/#backwards-compatibility-with-manylinux2010-wheels + if arch_suffix in {'i686', 'x86_64'}: + arches.append('manylinux2010' + arch_sep + arch_suffix) + arches.append('manylinux1' + arch_sep + arch_suffix) + elif arch_prefix == 'manylinux2010': + # manylinux1 wheels run on most manylinux2010 systems with the + # exception of wheels depending on ncurses. PEP 571 states + # manylinux1 wheels should be considered manylinux2010 wheels: + # https://www.python.org/dev/peps/pep-0571/#backwards-compatibility-with-manylinux1-wheels + arches.append('manylinux1' + arch_sep + arch_suffix) + return arches + + +def _get_custom_platforms(arch): + # type: (str) -> List[str] + arch_prefix, arch_sep, arch_suffix = arch.partition('_') + if arch.startswith('macosx'): + arches = _mac_platforms(arch) + elif arch_prefix in ['manylinux2014', 'manylinux2010']: + arches = _custom_manylinux_platforms(arch) + else: + arches = [arch] + return arches + + +def _expand_allowed_platforms(platforms): + # type: (Optional[List[str]]) -> Optional[List[str]] + if not platforms: + return None + + seen = set() + result = [] + + for p in platforms: + if p in seen: + continue + additions = [c for c in _get_custom_platforms(p) if c not in seen] + seen.update(additions) + result.extend(additions) + + return result + + +def _get_python_version(version): + # type: (str) -> PythonVersion + if len(version) > 1: + return int(version[0]), int(version[1:]) + else: + return (int(version[0]),) + + +def _get_custom_interpreter(implementation=None, version=None): + # type: (Optional[str], Optional[str]) -> str + if implementation is None: + implementation = interpreter_name() + if version is None: + version = interpreter_version() + return "{}{}".format(implementation, version) + + +def get_supported( + version=None, # type: Optional[str] + platforms=None, # type: Optional[List[str]] + impl=None, # type: Optional[str] + abis=None # type: Optional[List[str]] +): + # type: (...) -> List[Tag] + """Return a list of supported tags for each version specified in + `versions`. + + :param version: a string version, of the form "33" or "32", + or None. The version will be assumed to support our ABI. + :param platform: specify a list of platforms you want valid + tags for, or None. If None, use the local system platform. + :param impl: specify the exact implementation you want valid + tags for, or None. If None, use the local interpreter impl. + :param abis: specify a list of abis you want valid + tags for, or None. If None, use the local interpreter abi. + """ + supported = [] # type: List[Tag] + + python_version = None # type: Optional[PythonVersion] + if version is not None: + python_version = _get_python_version(version) + + interpreter = _get_custom_interpreter(impl, version) + + platforms = _expand_allowed_platforms(platforms) + + is_cpython = (impl or interpreter_name()) == "cp" + if is_cpython: + supported.extend( + cpython_tags( + python_version=python_version, + abis=abis, + platforms=platforms, + ) + ) + else: + supported.extend( + generic_tags( + interpreter=interpreter, + abis=abis, + platforms=platforms, + ) + ) + supported.extend( + compatible_tags( + python_version=python_version, + interpreter=interpreter, + platforms=platforms, + ) + ) + + return supported diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/datetime.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/datetime.py new file mode 100644 index 0000000000..4d0503c2f3 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/datetime.py @@ -0,0 +1,14 @@ +"""For when pip wants to check the date or time. +""" + +from __future__ import absolute_import + +import datetime + + +def today_is_later_than(year, month, day): + # type: (int, int, int) -> bool + today = datetime.date.today() + given = datetime.date(year, month, day) + + return today > given diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/deprecation.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/deprecation.py new file mode 100644 index 0000000000..2f20cfd49d --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/deprecation.py @@ -0,0 +1,104 @@ +""" +A module that implements tooling to enable easy warnings about deprecations. +""" + +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False + +from __future__ import absolute_import + +import logging +import warnings + +from pip._vendor.packaging.version import parse + +from pip import __version__ as current_version +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Any, Optional + + +DEPRECATION_MSG_PREFIX = "DEPRECATION: " + + +class PipDeprecationWarning(Warning): + pass + + +_original_showwarning = None # type: Any + + +# Warnings <-> Logging Integration +def _showwarning(message, category, filename, lineno, file=None, line=None): + if file is not None: + if _original_showwarning is not None: + _original_showwarning( + message, category, filename, lineno, file, line, + ) + elif issubclass(category, PipDeprecationWarning): + # We use a specially named logger which will handle all of the + # deprecation messages for pip. + logger = logging.getLogger("pip._internal.deprecations") + logger.warning(message) + else: + _original_showwarning( + message, category, filename, lineno, file, line, + ) + + +def install_warning_logger(): + # type: () -> None + # Enable our Deprecation Warnings + warnings.simplefilter("default", PipDeprecationWarning, append=True) + + global _original_showwarning + + if _original_showwarning is None: + _original_showwarning = warnings.showwarning + warnings.showwarning = _showwarning + + +def deprecated(reason, replacement, gone_in, issue=None): + # type: (str, Optional[str], Optional[str], Optional[int]) -> None + """Helper to deprecate existing functionality. + + reason: + Textual reason shown to the user about why this functionality has + been deprecated. + replacement: + Textual suggestion shown to the user about what alternative + functionality they can use. + gone_in: + The version of pip does this functionality should get removed in. + Raises errors if pip's current version is greater than or equal to + this. + issue: + Issue number on the tracker that would serve as a useful place for + users to find related discussion and provide feedback. + + Always pass replacement, gone_in and issue as keyword arguments for clarity + at the call site. + """ + + # Construct a nice message. + # This is eagerly formatted as we want it to get logged as if someone + # typed this entire message out. + sentences = [ + (reason, DEPRECATION_MSG_PREFIX + "{}"), + (gone_in, "pip {} will remove support for this functionality."), + (replacement, "A possible replacement is {}."), + (issue, ( + "You can find discussion regarding this at " + "https://github.com/pypa/pip/issues/{}." + )), + ] + message = " ".join( + template.format(val) for val, template in sentences if val is not None + ) + + # Raise as an error if it has to be removed. + if gone_in is not None and parse(current_version) >= parse(gone_in): + raise PipDeprecationWarning(message) + + warnings.warn(message, category=PipDeprecationWarning, stacklevel=2) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/direct_url_helpers.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/direct_url_helpers.py new file mode 100644 index 0000000000..87bd61fa01 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/direct_url_helpers.py @@ -0,0 +1,126 @@ +import logging + +from pip._internal.models.direct_url import ( + DIRECT_URL_METADATA_NAME, + ArchiveInfo, + DirectUrl, + DirectUrlValidationError, + DirInfo, + VcsInfo, +) +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.vcs import vcs + +try: + from json import JSONDecodeError +except ImportError: + # PY2 + JSONDecodeError = ValueError # type: ignore + +if MYPY_CHECK_RUNNING: + from typing import Optional + + from pip._vendor.pkg_resources import Distribution + + from pip._internal.models.link import Link + +logger = logging.getLogger(__name__) + + +def direct_url_as_pep440_direct_reference(direct_url, name): + # type: (DirectUrl, str) -> str + """Convert a DirectUrl to a pip requirement string.""" + direct_url.validate() # if invalid, this is a pip bug + requirement = name + " @ " + fragments = [] + if isinstance(direct_url.info, VcsInfo): + requirement += "{}+{}@{}".format( + direct_url.info.vcs, direct_url.url, direct_url.info.commit_id + ) + elif isinstance(direct_url.info, ArchiveInfo): + requirement += direct_url.url + if direct_url.info.hash: + fragments.append(direct_url.info.hash) + else: + assert isinstance(direct_url.info, DirInfo) + requirement += direct_url.url + if direct_url.subdirectory: + fragments.append("subdirectory=" + direct_url.subdirectory) + if fragments: + requirement += "#" + "&".join(fragments) + return requirement + + +def direct_url_from_link(link, source_dir=None, link_is_in_wheel_cache=False): + # type: (Link, Optional[str], bool) -> DirectUrl + if link.is_vcs: + vcs_backend = vcs.get_backend_for_scheme(link.scheme) + assert vcs_backend + url, requested_revision, _ = ( + vcs_backend.get_url_rev_and_auth(link.url_without_fragment) + ) + # For VCS links, we need to find out and add commit_id. + if link_is_in_wheel_cache: + # If the requested VCS link corresponds to a cached + # wheel, it means the requested revision was an + # immutable commit hash, otherwise it would not have + # been cached. In that case we don't have a source_dir + # with the VCS checkout. + assert requested_revision + commit_id = requested_revision + else: + # If the wheel was not in cache, it means we have + # had to checkout from VCS to build and we have a source_dir + # which we can inspect to find out the commit id. + assert source_dir + commit_id = vcs_backend.get_revision(source_dir) + return DirectUrl( + url=url, + info=VcsInfo( + vcs=vcs_backend.name, + commit_id=commit_id, + requested_revision=requested_revision, + ), + subdirectory=link.subdirectory_fragment, + ) + elif link.is_existing_dir(): + return DirectUrl( + url=link.url_without_fragment, + info=DirInfo(), + subdirectory=link.subdirectory_fragment, + ) + else: + hash = None + hash_name = link.hash_name + if hash_name: + hash = "{}={}".format(hash_name, link.hash) + return DirectUrl( + url=link.url_without_fragment, + info=ArchiveInfo(hash=hash), + subdirectory=link.subdirectory_fragment, + ) + + +def dist_get_direct_url(dist): + # type: (Distribution) -> Optional[DirectUrl] + """Obtain a DirectUrl from a pkg_resource.Distribution. + + Returns None if the distribution has no `direct_url.json` metadata, + or if `direct_url.json` is invalid. + """ + if not dist.has_metadata(DIRECT_URL_METADATA_NAME): + return None + try: + return DirectUrl.from_json(dist.get_metadata(DIRECT_URL_METADATA_NAME)) + except ( + DirectUrlValidationError, + JSONDecodeError, + UnicodeDecodeError + ) as e: + logger.warning( + "Error parsing %s for %s: %s", + DIRECT_URL_METADATA_NAME, + dist.project_name, + e, + ) + return None diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/distutils_args.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/distutils_args.py new file mode 100644 index 0000000000..e38e402d73 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/distutils_args.py @@ -0,0 +1,48 @@ +from distutils.errors import DistutilsArgError +from distutils.fancy_getopt import FancyGetopt + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Dict, List + + +_options = [ + ("exec-prefix=", None, ""), + ("home=", None, ""), + ("install-base=", None, ""), + ("install-data=", None, ""), + ("install-headers=", None, ""), + ("install-lib=", None, ""), + ("install-platlib=", None, ""), + ("install-purelib=", None, ""), + ("install-scripts=", None, ""), + ("prefix=", None, ""), + ("root=", None, ""), + ("user", None, ""), +] + + +# typeshed doesn't permit Tuple[str, None, str], see python/typeshed#3469. +_distutils_getopt = FancyGetopt(_options) # type: ignore + + +def parse_distutils_args(args): + # type: (List[str]) -> Dict[str, str] + """Parse provided arguments, returning an object that has the + matched arguments. + + Any unknown arguments are ignored. + """ + result = {} + for arg in args: + try: + _, match = _distutils_getopt.getopt(args=[arg]) + except DistutilsArgError: + # We don't care about any other options, which here may be + # considered unrecognized since our option list is not + # exhaustive. + pass + else: + result.update(match.__dict__) + return result diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/encoding.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/encoding.py new file mode 100644 index 0000000000..42a57535af --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/encoding.py @@ -0,0 +1,41 @@ +import codecs +import locale +import re +import sys + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List, Text, Tuple + +BOMS = [ + (codecs.BOM_UTF8, 'utf-8'), + (codecs.BOM_UTF16, 'utf-16'), + (codecs.BOM_UTF16_BE, 'utf-16-be'), + (codecs.BOM_UTF16_LE, 'utf-16-le'), + (codecs.BOM_UTF32, 'utf-32'), + (codecs.BOM_UTF32_BE, 'utf-32-be'), + (codecs.BOM_UTF32_LE, 'utf-32-le'), +] # type: List[Tuple[bytes, Text]] + +ENCODING_RE = re.compile(br'coding[:=]\s*([-\w.]+)') + + +def auto_decode(data): + # type: (bytes) -> Text + """Check a bytes string for a BOM to correctly detect the encoding + + Fallback to locale.getpreferredencoding(False) like open() on Python3""" + for bom, encoding in BOMS: + if data.startswith(bom): + return data[len(bom):].decode(encoding) + # Lets check the first two lines as in PEP263 + for line in data.split(b'\n')[:2]: + if line[0:1] == b'#' and ENCODING_RE.search(line): + result = ENCODING_RE.search(line) + assert result is not None + encoding = result.groups()[0].decode('ascii') + return data.decode(encoding) + return data.decode( + locale.getpreferredencoding(False) or sys.getdefaultencoding(), + ) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/entrypoints.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/entrypoints.py new file mode 100644 index 0000000000..64d1cb2bd0 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/entrypoints.py @@ -0,0 +1,31 @@ +import sys + +from pip._internal.cli.main import main +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List, Optional + + +def _wrapper(args=None): + # type: (Optional[List[str]]) -> int + """Central wrapper for all old entrypoints. + + Historically pip has had several entrypoints defined. Because of issues + arising from PATH, sys.path, multiple Pythons, their interactions, and most + of them having a pip installed, users suffer every time an entrypoint gets + moved. + + To alleviate this pain, and provide a mechanism for warning users and + directing them to an appropriate place for help, we now define all of + our old entrypoints as wrappers for the current one. + """ + sys.stderr.write( + "WARNING: pip is being invoked by an old script wrapper. This will " + "fail in a future version of pip.\n" + "Please see https://github.com/pypa/pip/issues/5599 for advice on " + "fixing the underlying issue.\n" + "To avoid this problem you can invoke Python with '-m pip' instead of " + "running pip directly.\n" + ) + return main(args) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/filesystem.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/filesystem.py new file mode 100644 index 0000000000..303243fd22 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/filesystem.py @@ -0,0 +1,224 @@ +import errno +import fnmatch +import os +import os.path +import random +import shutil +import stat +import sys +from contextlib import contextmanager +from tempfile import NamedTemporaryFile + +# NOTE: retrying is not annotated in typeshed as on 2017-07-17, which is +# why we ignore the type on this import. +from pip._vendor.retrying import retry # type: ignore +from pip._vendor.six import PY2 + +from pip._internal.utils.compat import get_path_uid +from pip._internal.utils.misc import format_size +from pip._internal.utils.typing import MYPY_CHECK_RUNNING, cast + +if MYPY_CHECK_RUNNING: + from typing import Any, BinaryIO, Iterator, List, Union + + class NamedTemporaryFileResult(BinaryIO): + @property + def file(self): + # type: () -> BinaryIO + pass + + +def check_path_owner(path): + # type: (str) -> bool + # If we don't have a way to check the effective uid of this process, then + # we'll just assume that we own the directory. + if sys.platform == "win32" or not hasattr(os, "geteuid"): + return True + + assert os.path.isabs(path) + + previous = None + while path != previous: + if os.path.lexists(path): + # Check if path is writable by current user. + if os.geteuid() == 0: + # Special handling for root user in order to handle properly + # cases where users use sudo without -H flag. + try: + path_uid = get_path_uid(path) + except OSError: + return False + return path_uid == 0 + else: + return os.access(path, os.W_OK) + else: + previous, path = path, os.path.dirname(path) + return False # assume we don't own the path + + +def copy2_fixed(src, dest): + # type: (str, str) -> None + """Wrap shutil.copy2() but map errors copying socket files to + SpecialFileError as expected. + + See also https://bugs.python.org/issue37700. + """ + try: + shutil.copy2(src, dest) + except (OSError, IOError): + for f in [src, dest]: + try: + is_socket_file = is_socket(f) + except OSError: + # An error has already occurred. Another error here is not + # a problem and we can ignore it. + pass + else: + if is_socket_file: + raise shutil.SpecialFileError( + "`{f}` is a socket".format(**locals())) + + raise + + +def is_socket(path): + # type: (str) -> bool + return stat.S_ISSOCK(os.lstat(path).st_mode) + + +@contextmanager +def adjacent_tmp_file(path, **kwargs): + # type: (str, **Any) -> Iterator[NamedTemporaryFileResult] + """Return a file-like object pointing to a tmp file next to path. + + The file is created securely and is ensured to be written to disk + after the context reaches its end. + + kwargs will be passed to tempfile.NamedTemporaryFile to control + the way the temporary file will be opened. + """ + with NamedTemporaryFile( + delete=False, + dir=os.path.dirname(path), + prefix=os.path.basename(path), + suffix='.tmp', + **kwargs + ) as f: + result = cast('NamedTemporaryFileResult', f) + try: + yield result + finally: + result.file.flush() + os.fsync(result.file.fileno()) + + +_replace_retry = retry(stop_max_delay=1000, wait_fixed=250) + +if PY2: + @_replace_retry + def replace(src, dest): + # type: (str, str) -> None + try: + os.rename(src, dest) + except OSError: + os.remove(dest) + os.rename(src, dest) + +else: + replace = _replace_retry(os.replace) + + +# test_writable_dir and _test_writable_dir_win are copied from Flit, +# with the author's agreement to also place them under pip's license. +def test_writable_dir(path): + # type: (str) -> bool + """Check if a directory is writable. + + Uses os.access() on POSIX, tries creating files on Windows. + """ + # If the directory doesn't exist, find the closest parent that does. + while not os.path.isdir(path): + parent = os.path.dirname(path) + if parent == path: + break # Should never get here, but infinite loops are bad + path = parent + + if os.name == 'posix': + return os.access(path, os.W_OK) + + return _test_writable_dir_win(path) + + +def _test_writable_dir_win(path): + # type: (str) -> bool + # os.access doesn't work on Windows: http://bugs.python.org/issue2528 + # and we can't use tempfile: http://bugs.python.org/issue22107 + basename = 'accesstest_deleteme_fishfingers_custard_' + alphabet = 'abcdefghijklmnopqrstuvwxyz0123456789' + for _ in range(10): + name = basename + ''.join(random.choice(alphabet) for _ in range(6)) + file = os.path.join(path, name) + try: + fd = os.open(file, os.O_RDWR | os.O_CREAT | os.O_EXCL) + # Python 2 doesn't support FileExistsError and PermissionError. + except OSError as e: + # exception FileExistsError + if e.errno == errno.EEXIST: + continue + # exception PermissionError + if e.errno == errno.EPERM or e.errno == errno.EACCES: + # This could be because there's a directory with the same name. + # But it's highly unlikely there's a directory called that, + # so we'll assume it's because the parent dir is not writable. + # This could as well be because the parent dir is not readable, + # due to non-privileged user access. + return False + raise + else: + os.close(fd) + os.unlink(file) + return True + + # This should never be reached + raise EnvironmentError( + 'Unexpected condition testing for writable directory' + ) + + +def find_files(path, pattern): + # type: (str, str) -> List[str] + """Returns a list of absolute paths of files beneath path, recursively, + with filenames which match the UNIX-style shell glob pattern.""" + result = [] # type: List[str] + for root, _, files in os.walk(path): + matches = fnmatch.filter(files, pattern) + result.extend(os.path.join(root, f) for f in matches) + return result + + +def file_size(path): + # type: (str) -> Union[int, float] + # If it's a symlink, return 0. + if os.path.islink(path): + return 0 + return os.path.getsize(path) + + +def format_file_size(path): + # type: (str) -> str + return format_size(file_size(path)) + + +def directory_size(path): + # type: (str) -> Union[int, float] + size = 0.0 + for root, _dirs, files in os.walk(path): + for filename in files: + file_path = os.path.join(root, filename) + size += file_size(file_path) + return size + + +def format_directory_size(path): + # type: (str) -> str + return format_size(directory_size(path)) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/filetypes.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/filetypes.py new file mode 100644 index 0000000000..201c6ebbed --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/filetypes.py @@ -0,0 +1,26 @@ +"""Filetype information. +""" +from pip._internal.utils.misc import splitext +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Tuple + +WHEEL_EXTENSION = '.whl' +BZ2_EXTENSIONS = ('.tar.bz2', '.tbz') # type: Tuple[str, ...] +XZ_EXTENSIONS = ('.tar.xz', '.txz', '.tlz', + '.tar.lz', '.tar.lzma') # type: Tuple[str, ...] +ZIP_EXTENSIONS = ('.zip', WHEEL_EXTENSION) # type: Tuple[str, ...] +TAR_EXTENSIONS = ('.tar.gz', '.tgz', '.tar') # type: Tuple[str, ...] +ARCHIVE_EXTENSIONS = ( + ZIP_EXTENSIONS + BZ2_EXTENSIONS + TAR_EXTENSIONS + XZ_EXTENSIONS +) + + +def is_archive_file(name): + # type: (str) -> bool + """Return True if `name` is a considered as an archive file.""" + ext = splitext(name)[1].lower() + if ext in ARCHIVE_EXTENSIONS: + return True + return False diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/glibc.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/glibc.py new file mode 100644 index 0000000000..3610424413 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/glibc.py @@ -0,0 +1,98 @@ +# The following comment should be removed at some point in the future. +# mypy: strict-optional=False + +from __future__ import absolute_import + +import os +import sys + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Optional, Tuple + + +def glibc_version_string(): + # type: () -> Optional[str] + "Returns glibc version string, or None if not using glibc." + return glibc_version_string_confstr() or glibc_version_string_ctypes() + + +def glibc_version_string_confstr(): + # type: () -> Optional[str] + "Primary implementation of glibc_version_string using os.confstr." + # os.confstr is quite a bit faster than ctypes.DLL. It's also less likely + # to be broken or missing. This strategy is used in the standard library + # platform module: + # https://github.com/python/cpython/blob/fcf1d003bf4f0100c9d0921ff3d70e1127ca1b71/Lib/platform.py#L175-L183 + if sys.platform == "win32": + return None + try: + # os.confstr("CS_GNU_LIBC_VERSION") returns a string like "glibc 2.17": + _, version = os.confstr("CS_GNU_LIBC_VERSION").split() + except (AttributeError, OSError, ValueError): + # os.confstr() or CS_GNU_LIBC_VERSION not available (or a bad value)... + return None + return version + + +def glibc_version_string_ctypes(): + # type: () -> Optional[str] + "Fallback implementation of glibc_version_string using ctypes." + + try: + import ctypes + except ImportError: + return None + + # ctypes.CDLL(None) internally calls dlopen(NULL), and as the dlopen + # manpage says, "If filename is NULL, then the returned handle is for the + # main program". This way we can let the linker do the work to figure out + # which libc our process is actually using. + process_namespace = ctypes.CDLL(None) + try: + gnu_get_libc_version = process_namespace.gnu_get_libc_version + except AttributeError: + # Symbol doesn't exist -> therefore, we are not linked to + # glibc. + return None + + # Call gnu_get_libc_version, which returns a string like "2.5" + gnu_get_libc_version.restype = ctypes.c_char_p + version_str = gnu_get_libc_version() + # py2 / py3 compatibility: + if not isinstance(version_str, str): + version_str = version_str.decode("ascii") + + return version_str + + +# platform.libc_ver regularly returns completely nonsensical glibc +# versions. E.g. on my computer, platform says: +# +# ~$ python2.7 -c 'import platform; print(platform.libc_ver())' +# ('glibc', '2.7') +# ~$ python3.5 -c 'import platform; print(platform.libc_ver())' +# ('glibc', '2.9') +# +# But the truth is: +# +# ~$ ldd --version +# ldd (Debian GLIBC 2.22-11) 2.22 +# +# This is unfortunate, because it means that the linehaul data on libc +# versions that was generated by pip 8.1.2 and earlier is useless and +# misleading. Solution: instead of using platform, use our code that actually +# works. +def libc_ver(): + # type: () -> Tuple[str, str] + """Try to determine the glibc version + + Returns a tuple of strings (lib, version) which default to empty strings + in case the lookup fails. + """ + glibc_version = glibc_version_string() + if glibc_version is None: + return ("", "") + else: + return ("glibc", glibc_version) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/hashes.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/hashes.py new file mode 100644 index 0000000000..4d90f5bfda --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/hashes.py @@ -0,0 +1,169 @@ +from __future__ import absolute_import + +import hashlib + +from pip._vendor.six import iteritems, iterkeys, itervalues + +from pip._internal.exceptions import HashMismatch, HashMissing, InstallationError +from pip._internal.utils.misc import read_chunks +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import BinaryIO, Dict, Iterator, List, NoReturn + + from pip._vendor.six import PY3 + if PY3: + from hashlib import _Hash + else: + from hashlib import _hash as _Hash + + +# The recommended hash algo of the moment. Change this whenever the state of +# the art changes; it won't hurt backward compatibility. +FAVORITE_HASH = 'sha256' + + +# Names of hashlib algorithms allowed by the --hash option and ``pip hash`` +# Currently, those are the ones at least as collision-resistant as sha256. +STRONG_HASHES = ['sha256', 'sha384', 'sha512'] + + +class Hashes(object): + """A wrapper that builds multiple hashes at once and checks them against + known-good values + + """ + def __init__(self, hashes=None): + # type: (Dict[str, List[str]]) -> None + """ + :param hashes: A dict of algorithm names pointing to lists of allowed + hex digests + """ + allowed = {} + if hashes is not None: + for alg, keys in hashes.items(): + # Make sure values are always sorted (to ease equality checks) + allowed[alg] = sorted(keys) + self._allowed = allowed + + def __and__(self, other): + # type: (Hashes) -> Hashes + if not isinstance(other, Hashes): + return NotImplemented + + # If either of the Hashes object is entirely empty (i.e. no hash + # specified at all), all hashes from the other object are allowed. + if not other: + return self + if not self: + return other + + # Otherwise only hashes that present in both objects are allowed. + new = {} + for alg, values in iteritems(other._allowed): + if alg not in self._allowed: + continue + new[alg] = [v for v in values if v in self._allowed[alg]] + return Hashes(new) + + @property + def digest_count(self): + # type: () -> int + return sum(len(digests) for digests in self._allowed.values()) + + def is_hash_allowed( + self, + hash_name, # type: str + hex_digest, # type: str + ): + # type: (...) -> bool + """Return whether the given hex digest is allowed.""" + return hex_digest in self._allowed.get(hash_name, []) + + def check_against_chunks(self, chunks): + # type: (Iterator[bytes]) -> None + """Check good hashes against ones built from iterable of chunks of + data. + + Raise HashMismatch if none match. + + """ + gots = {} + for hash_name in iterkeys(self._allowed): + try: + gots[hash_name] = hashlib.new(hash_name) + except (ValueError, TypeError): + raise InstallationError( + 'Unknown hash name: {}'.format(hash_name) + ) + + for chunk in chunks: + for hash in itervalues(gots): + hash.update(chunk) + + for hash_name, got in iteritems(gots): + if got.hexdigest() in self._allowed[hash_name]: + return + self._raise(gots) + + def _raise(self, gots): + # type: (Dict[str, _Hash]) -> NoReturn + raise HashMismatch(self._allowed, gots) + + def check_against_file(self, file): + # type: (BinaryIO) -> None + """Check good hashes against a file-like object + + Raise HashMismatch if none match. + + """ + return self.check_against_chunks(read_chunks(file)) + + def check_against_path(self, path): + # type: (str) -> None + with open(path, 'rb') as file: + return self.check_against_file(file) + + def __nonzero__(self): + # type: () -> bool + """Return whether I know any known-good hashes.""" + return bool(self._allowed) + + def __bool__(self): + # type: () -> bool + return self.__nonzero__() + + def __eq__(self, other): + # type: (object) -> bool + if not isinstance(other, Hashes): + return NotImplemented + return self._allowed == other._allowed + + def __hash__(self): + # type: () -> int + return hash( + ",".join(sorted( + ":".join((alg, digest)) + for alg, digest_list in self._allowed.items() + for digest in digest_list + )) + ) + + +class MissingHashes(Hashes): + """A workalike for Hashes used when we're missing a hash for a requirement + + It computes the actual hash of the requirement and raises a HashMissing + exception showing it to the user. + + """ + def __init__(self): + # type: () -> None + """Don't offer the ``hashes`` kwarg.""" + # Pass our favorite hash in to generate a "gotten hash". With the + # empty list, it will never match, so an error will always raise. + super(MissingHashes, self).__init__(hashes={FAVORITE_HASH: []}) + + def _raise(self, gots): + # type: (Dict[str, _Hash]) -> NoReturn + raise HashMissing(gots[FAVORITE_HASH].hexdigest()) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/inject_securetransport.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/inject_securetransport.py new file mode 100644 index 0000000000..5b93b1d673 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/inject_securetransport.py @@ -0,0 +1,36 @@ +"""A helper module that injects SecureTransport, on import. + +The import should be done as early as possible, to ensure all requests and +sessions (or whatever) are created after injecting SecureTransport. + +Note that we only do the injection on macOS, when the linked OpenSSL is too +old to handle TLSv1.2. +""" + +import sys + + +def inject_securetransport(): + # type: () -> None + # Only relevant on macOS + if sys.platform != "darwin": + return + + try: + import ssl + except ImportError: + return + + # Checks for OpenSSL 1.0.1 + if ssl.OPENSSL_VERSION_NUMBER >= 0x1000100f: + return + + try: + from pip._vendor.urllib3.contrib import securetransport + except (ImportError, OSError): + return + + securetransport.inject_into_urllib3() + + +inject_securetransport() diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/logging.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/logging.py new file mode 100644 index 0000000000..9a017cf7e3 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/logging.py @@ -0,0 +1,399 @@ +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False + +from __future__ import absolute_import + +import contextlib +import errno +import logging +import logging.handlers +import os +import sys +from logging import Filter, getLogger + +from pip._vendor.six import PY2 + +from pip._internal.utils.compat import WINDOWS +from pip._internal.utils.deprecation import DEPRECATION_MSG_PREFIX +from pip._internal.utils.misc import ensure_dir + +try: + import threading +except ImportError: + import dummy_threading as threading # type: ignore + + +try: + # Use "import as" and set colorama in the else clause to avoid mypy + # errors and get the following correct revealed type for colorama: + # `Union[_importlib_modulespec.ModuleType, None]` + # Otherwise, we get an error like the following in the except block: + # > Incompatible types in assignment (expression has type "None", + # variable has type Module) + # TODO: eliminate the need to use "import as" once mypy addresses some + # of its issues with conditional imports. Here is an umbrella issue: + # https://github.com/python/mypy/issues/1297 + from pip._vendor import colorama as _colorama +# Lots of different errors can come from this, including SystemError and +# ImportError. +except Exception: + colorama = None +else: + # Import Fore explicitly rather than accessing below as colorama.Fore + # to avoid the following error running mypy: + # > Module has no attribute "Fore" + # TODO: eliminate the need to import Fore once mypy addresses some of its + # issues with conditional imports. This particular case could be an + # instance of the following issue (but also see the umbrella issue above): + # https://github.com/python/mypy/issues/3500 + from pip._vendor.colorama import Fore + + colorama = _colorama + + +_log_state = threading.local() +subprocess_logger = getLogger('pip.subprocessor') + + +class BrokenStdoutLoggingError(Exception): + """ + Raised if BrokenPipeError occurs for the stdout stream while logging. + """ + pass + + +# BrokenPipeError does not exist in Python 2 and, in addition, manifests +# differently in Windows and non-Windows. +if WINDOWS: + # In Windows, a broken pipe can show up as EINVAL rather than EPIPE: + # https://bugs.python.org/issue19612 + # https://bugs.python.org/issue30418 + if PY2: + def _is_broken_pipe_error(exc_class, exc): + """See the docstring for non-Windows Python 3 below.""" + return (exc_class is IOError and + exc.errno in (errno.EINVAL, errno.EPIPE)) + else: + # In Windows, a broken pipe IOError became OSError in Python 3. + def _is_broken_pipe_error(exc_class, exc): + """See the docstring for non-Windows Python 3 below.""" + return ((exc_class is BrokenPipeError) or # noqa: F821 + (exc_class is OSError and + exc.errno in (errno.EINVAL, errno.EPIPE))) +elif PY2: + def _is_broken_pipe_error(exc_class, exc): + """See the docstring for non-Windows Python 3 below.""" + return (exc_class is IOError and exc.errno == errno.EPIPE) +else: + # Then we are in the non-Windows Python 3 case. + def _is_broken_pipe_error(exc_class, exc): + """ + Return whether an exception is a broken pipe error. + + Args: + exc_class: an exception class. + exc: an exception instance. + """ + return (exc_class is BrokenPipeError) # noqa: F821 + + +@contextlib.contextmanager +def indent_log(num=2): + """ + A context manager which will cause the log output to be indented for any + log messages emitted inside it. + """ + # For thread-safety + _log_state.indentation = get_indentation() + _log_state.indentation += num + try: + yield + finally: + _log_state.indentation -= num + + +def get_indentation(): + return getattr(_log_state, 'indentation', 0) + + +class IndentingFormatter(logging.Formatter): + + def __init__(self, *args, **kwargs): + """ + A logging.Formatter that obeys the indent_log() context manager. + + :param add_timestamp: A bool indicating output lines should be prefixed + with their record's timestamp. + """ + self.add_timestamp = kwargs.pop("add_timestamp", False) + super(IndentingFormatter, self).__init__(*args, **kwargs) + + def get_message_start(self, formatted, levelno): + """ + Return the start of the formatted log message (not counting the + prefix to add to each line). + """ + if levelno < logging.WARNING: + return '' + if formatted.startswith(DEPRECATION_MSG_PREFIX): + # Then the message already has a prefix. We don't want it to + # look like "WARNING: DEPRECATION: ...." + return '' + if levelno < logging.ERROR: + return 'WARNING: ' + + return 'ERROR: ' + + def format(self, record): + """ + Calls the standard formatter, but will indent all of the log message + lines by our current indentation level. + """ + formatted = super(IndentingFormatter, self).format(record) + message_start = self.get_message_start(formatted, record.levelno) + formatted = message_start + formatted + + prefix = '' + if self.add_timestamp: + # TODO: Use Formatter.default_time_format after dropping PY2. + t = self.formatTime(record, "%Y-%m-%dT%H:%M:%S") + prefix = '{t},{record.msecs:03.0f} '.format(**locals()) + prefix += " " * get_indentation() + formatted = "".join([ + prefix + line + for line in formatted.splitlines(True) + ]) + return formatted + + +def _color_wrap(*colors): + def wrapped(inp): + return "".join(list(colors) + [inp, colorama.Style.RESET_ALL]) + return wrapped + + +class ColorizedStreamHandler(logging.StreamHandler): + + # Don't build up a list of colors if we don't have colorama + if colorama: + COLORS = [ + # This needs to be in order from highest logging level to lowest. + (logging.ERROR, _color_wrap(Fore.RED)), + (logging.WARNING, _color_wrap(Fore.YELLOW)), + ] + else: + COLORS = [] + + def __init__(self, stream=None, no_color=None): + logging.StreamHandler.__init__(self, stream) + self._no_color = no_color + + if WINDOWS and colorama: + self.stream = colorama.AnsiToWin32(self.stream) + + def _using_stdout(self): + """ + Return whether the handler is using sys.stdout. + """ + if WINDOWS and colorama: + # Then self.stream is an AnsiToWin32 object. + return self.stream.wrapped is sys.stdout + + return self.stream is sys.stdout + + def should_color(self): + # Don't colorize things if we do not have colorama or if told not to + if not colorama or self._no_color: + return False + + real_stream = ( + self.stream if not isinstance(self.stream, colorama.AnsiToWin32) + else self.stream.wrapped + ) + + # If the stream is a tty we should color it + if hasattr(real_stream, "isatty") and real_stream.isatty(): + return True + + # If we have an ANSI term we should color it + if os.environ.get("TERM") == "ANSI": + return True + + # If anything else we should not color it + return False + + def format(self, record): + msg = logging.StreamHandler.format(self, record) + + if self.should_color(): + for level, color in self.COLORS: + if record.levelno >= level: + msg = color(msg) + break + + return msg + + # The logging module says handleError() can be customized. + def handleError(self, record): + exc_class, exc = sys.exc_info()[:2] + # If a broken pipe occurred while calling write() or flush() on the + # stdout stream in logging's Handler.emit(), then raise our special + # exception so we can handle it in main() instead of logging the + # broken pipe error and continuing. + if (exc_class and self._using_stdout() and + _is_broken_pipe_error(exc_class, exc)): + raise BrokenStdoutLoggingError() + + return super(ColorizedStreamHandler, self).handleError(record) + + +class BetterRotatingFileHandler(logging.handlers.RotatingFileHandler): + + def _open(self): + ensure_dir(os.path.dirname(self.baseFilename)) + return logging.handlers.RotatingFileHandler._open(self) + + +class MaxLevelFilter(Filter): + + def __init__(self, level): + self.level = level + + def filter(self, record): + return record.levelno < self.level + + +class ExcludeLoggerFilter(Filter): + + """ + A logging Filter that excludes records from a logger (or its children). + """ + + def filter(self, record): + # The base Filter class allows only records from a logger (or its + # children). + return not super(ExcludeLoggerFilter, self).filter(record) + + +def setup_logging(verbosity, no_color, user_log_file): + """Configures and sets up all of the logging + + Returns the requested logging level, as its integer value. + """ + + # Determine the level to be logging at. + if verbosity >= 1: + level = "DEBUG" + elif verbosity == -1: + level = "WARNING" + elif verbosity == -2: + level = "ERROR" + elif verbosity <= -3: + level = "CRITICAL" + else: + level = "INFO" + + level_number = getattr(logging, level) + + # The "root" logger should match the "console" level *unless* we also need + # to log to a user log file. + include_user_log = user_log_file is not None + if include_user_log: + additional_log_file = user_log_file + root_level = "DEBUG" + else: + additional_log_file = "/dev/null" + root_level = level + + # Disable any logging besides WARNING unless we have DEBUG level logging + # enabled for vendored libraries. + vendored_log_level = "WARNING" if level in ["INFO", "ERROR"] else "DEBUG" + + # Shorthands for clarity + log_streams = { + "stdout": "ext://sys.stdout", + "stderr": "ext://sys.stderr", + } + handler_classes = { + "stream": "pip._internal.utils.logging.ColorizedStreamHandler", + "file": "pip._internal.utils.logging.BetterRotatingFileHandler", + } + handlers = ["console", "console_errors", "console_subprocess"] + ( + ["user_log"] if include_user_log else [] + ) + + logging.config.dictConfig({ + "version": 1, + "disable_existing_loggers": False, + "filters": { + "exclude_warnings": { + "()": "pip._internal.utils.logging.MaxLevelFilter", + "level": logging.WARNING, + }, + "restrict_to_subprocess": { + "()": "logging.Filter", + "name": subprocess_logger.name, + }, + "exclude_subprocess": { + "()": "pip._internal.utils.logging.ExcludeLoggerFilter", + "name": subprocess_logger.name, + }, + }, + "formatters": { + "indent": { + "()": IndentingFormatter, + "format": "%(message)s", + }, + "indent_with_timestamp": { + "()": IndentingFormatter, + "format": "%(message)s", + "add_timestamp": True, + }, + }, + "handlers": { + "console": { + "level": level, + "class": handler_classes["stream"], + "no_color": no_color, + "stream": log_streams["stdout"], + "filters": ["exclude_subprocess", "exclude_warnings"], + "formatter": "indent", + }, + "console_errors": { + "level": "WARNING", + "class": handler_classes["stream"], + "no_color": no_color, + "stream": log_streams["stderr"], + "filters": ["exclude_subprocess"], + "formatter": "indent", + }, + # A handler responsible for logging to the console messages + # from the "subprocessor" logger. + "console_subprocess": { + "level": level, + "class": handler_classes["stream"], + "no_color": no_color, + "stream": log_streams["stderr"], + "filters": ["restrict_to_subprocess"], + "formatter": "indent", + }, + "user_log": { + "level": "DEBUG", + "class": handler_classes["file"], + "filename": additional_log_file, + "delay": True, + "formatter": "indent_with_timestamp", + }, + }, + "root": { + "level": root_level, + "handlers": handlers, + }, + "loggers": { + "pip._vendor": { + "level": vendored_log_level + } + }, + }) + + return level_number diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/misc.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/misc.py new file mode 100644 index 0000000000..b8795ccf79 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/misc.py @@ -0,0 +1,977 @@ +# The following comment should be removed at some point in the future. +# mypy: strict-optional=False +# mypy: disallow-untyped-defs=False + +from __future__ import absolute_import + +import contextlib +import errno +import getpass +import hashlib +import io +import logging +import os +import posixpath +import shutil +import stat +import sys +from collections import deque +from itertools import tee + +from pip._vendor import pkg_resources +from pip._vendor.packaging.utils import canonicalize_name + +# NOTE: retrying is not annotated in typeshed as on 2017-07-17, which is +# why we ignore the type on this import. +from pip._vendor.retrying import retry # type: ignore +from pip._vendor.six import PY2, text_type +from pip._vendor.six.moves import filter, filterfalse, input, map, zip_longest +from pip._vendor.six.moves.urllib import parse as urllib_parse +from pip._vendor.six.moves.urllib.parse import unquote as urllib_unquote + +from pip import __version__ +from pip._internal.exceptions import CommandError +from pip._internal.locations import get_major_minor_version, site_packages, user_site +from pip._internal.utils.compat import WINDOWS, expanduser, stdlib_pkgs, str_to_display +from pip._internal.utils.typing import MYPY_CHECK_RUNNING, cast +from pip._internal.utils.virtualenv import ( + running_under_virtualenv, + virtualenv_no_global, +) + +if PY2: + from io import BytesIO as StringIO +else: + from io import StringIO + +if MYPY_CHECK_RUNNING: + from typing import ( + Any, + AnyStr, + Callable, + Container, + Iterable, + Iterator, + List, + Optional, + Text, + Tuple, + TypeVar, + Union, + ) + + from pip._vendor.pkg_resources import Distribution + + VersionInfo = Tuple[int, int, int] + T = TypeVar("T") + + +__all__ = ['rmtree', 'display_path', 'backup_dir', + 'ask', 'splitext', + 'format_size', 'is_installable_dir', + 'normalize_path', + 'renames', 'get_prog', + 'captured_stdout', 'ensure_dir', + 'get_installed_version', 'remove_auth_from_url'] + + +logger = logging.getLogger(__name__) + + +def get_pip_version(): + # type: () -> str + pip_pkg_dir = os.path.join(os.path.dirname(__file__), "..", "..") + pip_pkg_dir = os.path.abspath(pip_pkg_dir) + + return ( + 'pip {} from {} (python {})'.format( + __version__, pip_pkg_dir, get_major_minor_version(), + ) + ) + + +def normalize_version_info(py_version_info): + # type: (Tuple[int, ...]) -> Tuple[int, int, int] + """ + Convert a tuple of ints representing a Python version to one of length + three. + + :param py_version_info: a tuple of ints representing a Python version, + or None to specify no version. The tuple can have any length. + + :return: a tuple of length three if `py_version_info` is non-None. + Otherwise, return `py_version_info` unchanged (i.e. None). + """ + if len(py_version_info) < 3: + py_version_info += (3 - len(py_version_info)) * (0,) + elif len(py_version_info) > 3: + py_version_info = py_version_info[:3] + + return cast('VersionInfo', py_version_info) + + +def ensure_dir(path): + # type: (AnyStr) -> None + """os.path.makedirs without EEXIST.""" + try: + os.makedirs(path) + except OSError as e: + # Windows can raise spurious ENOTEMPTY errors. See #6426. + if e.errno != errno.EEXIST and e.errno != errno.ENOTEMPTY: + raise + + +def get_prog(): + # type: () -> str + try: + prog = os.path.basename(sys.argv[0]) + if prog in ('__main__.py', '-c'): + return "{} -m pip".format(sys.executable) + else: + return prog + except (AttributeError, TypeError, IndexError): + pass + return 'pip' + + +# Retry every half second for up to 3 seconds +@retry(stop_max_delay=3000, wait_fixed=500) +def rmtree(dir, ignore_errors=False): + # type: (AnyStr, bool) -> None + shutil.rmtree(dir, ignore_errors=ignore_errors, + onerror=rmtree_errorhandler) + + +def rmtree_errorhandler(func, path, exc_info): + """On Windows, the files in .svn are read-only, so when rmtree() tries to + remove them, an exception is thrown. We catch that here, remove the + read-only attribute, and hopefully continue without problems.""" + try: + has_attr_readonly = not (os.stat(path).st_mode & stat.S_IWRITE) + except (IOError, OSError): + # it's equivalent to os.path.exists + return + + if has_attr_readonly: + # convert to read/write + os.chmod(path, stat.S_IWRITE) + # use the original function to repeat the operation + func(path) + return + else: + raise + + +def path_to_display(path): + # type: (Optional[Union[str, Text]]) -> Optional[Text] + """ + Convert a bytes (or text) path to text (unicode in Python 2) for display + and logging purposes. + + This function should never error out. Also, this function is mainly needed + for Python 2 since in Python 3 str paths are already text. + """ + if path is None: + return None + if isinstance(path, text_type): + return path + # Otherwise, path is a bytes object (str in Python 2). + try: + display_path = path.decode(sys.getfilesystemencoding(), 'strict') + except UnicodeDecodeError: + # Include the full bytes to make troubleshooting easier, even though + # it may not be very human readable. + if PY2: + # Convert the bytes to a readable str representation using + # repr(), and then convert the str to unicode. + # Also, we add the prefix "b" to the repr() return value both + # to make the Python 2 output look like the Python 3 output, and + # to signal to the user that this is a bytes representation. + display_path = str_to_display('b{!r}'.format(path)) + else: + # Silence the "F821 undefined name 'ascii'" flake8 error since + # in Python 3 ascii() is a built-in. + display_path = ascii(path) # noqa: F821 + + return display_path + + +def display_path(path): + # type: (Union[str, Text]) -> str + """Gives the display value for a given path, making it relative to cwd + if possible.""" + path = os.path.normcase(os.path.abspath(path)) + if sys.version_info[0] == 2: + path = path.decode(sys.getfilesystemencoding(), 'replace') + path = path.encode(sys.getdefaultencoding(), 'replace') + if path.startswith(os.getcwd() + os.path.sep): + path = '.' + path[len(os.getcwd()):] + return path + + +def backup_dir(dir, ext='.bak'): + # type: (str, str) -> str + """Figure out the name of a directory to back up the given dir to + (adding .bak, .bak2, etc)""" + n = 1 + extension = ext + while os.path.exists(dir + extension): + n += 1 + extension = ext + str(n) + return dir + extension + + +def ask_path_exists(message, options): + # type: (str, Iterable[str]) -> str + for action in os.environ.get('PIP_EXISTS_ACTION', '').split(): + if action in options: + return action + return ask(message, options) + + +def _check_no_input(message): + # type: (str) -> None + """Raise an error if no input is allowed.""" + if os.environ.get('PIP_NO_INPUT'): + raise Exception( + 'No input was expected ($PIP_NO_INPUT set); question: {}'.format( + message) + ) + + +def ask(message, options): + # type: (str, Iterable[str]) -> str + """Ask the message interactively, with the given possible responses""" + while 1: + _check_no_input(message) + response = input(message) + response = response.strip().lower() + if response not in options: + print( + 'Your response ({!r}) was not one of the expected responses: ' + '{}'.format(response, ', '.join(options)) + ) + else: + return response + + +def ask_input(message): + # type: (str) -> str + """Ask for input interactively.""" + _check_no_input(message) + return input(message) + + +def ask_password(message): + # type: (str) -> str + """Ask for a password interactively.""" + _check_no_input(message) + return getpass.getpass(message) + + +def format_size(bytes): + # type: (float) -> str + if bytes > 1000 * 1000: + return '{:.1f} MB'.format(bytes / 1000.0 / 1000) + elif bytes > 10 * 1000: + return '{} kB'.format(int(bytes / 1000)) + elif bytes > 1000: + return '{:.1f} kB'.format(bytes / 1000.0) + else: + return '{} bytes'.format(int(bytes)) + + +def tabulate(rows): + # type: (Iterable[Iterable[Any]]) -> Tuple[List[str], List[int]] + """Return a list of formatted rows and a list of column sizes. + + For example:: + + >>> tabulate([['foobar', 2000], [0xdeadbeef]]) + (['foobar 2000', '3735928559'], [10, 4]) + """ + rows = [tuple(map(str, row)) for row in rows] + sizes = [max(map(len, col)) for col in zip_longest(*rows, fillvalue='')] + table = [" ".join(map(str.ljust, row, sizes)).rstrip() for row in rows] + return table, sizes + + +def is_installable_dir(path): + # type: (str) -> bool + """Is path is a directory containing setup.py or pyproject.toml? + """ + if not os.path.isdir(path): + return False + setup_py = os.path.join(path, 'setup.py') + if os.path.isfile(setup_py): + return True + pyproject_toml = os.path.join(path, 'pyproject.toml') + if os.path.isfile(pyproject_toml): + return True + return False + + +def read_chunks(file, size=io.DEFAULT_BUFFER_SIZE): + """Yield pieces of data from a file-like object until EOF.""" + while True: + chunk = file.read(size) + if not chunk: + break + yield chunk + + +def normalize_path(path, resolve_symlinks=True): + # type: (str, bool) -> str + """ + Convert a path to its canonical, case-normalized, absolute version. + + """ + path = expanduser(path) + if resolve_symlinks: + path = os.path.realpath(path) + else: + path = os.path.abspath(path) + return os.path.normcase(path) + + +def splitext(path): + # type: (str) -> Tuple[str, str] + """Like os.path.splitext, but take off .tar too""" + base, ext = posixpath.splitext(path) + if base.lower().endswith('.tar'): + ext = base[-4:] + ext + base = base[:-4] + return base, ext + + +def renames(old, new): + # type: (str, str) -> None + """Like os.renames(), but handles renaming across devices.""" + # Implementation borrowed from os.renames(). + head, tail = os.path.split(new) + if head and tail and not os.path.exists(head): + os.makedirs(head) + + shutil.move(old, new) + + head, tail = os.path.split(old) + if head and tail: + try: + os.removedirs(head) + except OSError: + pass + + +def is_local(path): + # type: (str) -> bool + """ + Return True if this is a path pip is allowed to modify. + + If we're in a virtualenv, sys.prefix points to the virtualenv's + prefix; only sys.prefix is considered local. + + If we're not in a virtualenv, in general we can modify anything. + However, if the OS vendor has configured distutils to install + somewhere other than sys.prefix (which could be a subdirectory of + sys.prefix, e.g. /usr/local), we consider sys.prefix itself nonlocal + and the domain of the OS vendor. (In other words, everything _other + than_ sys.prefix is considered local.) + + Caution: this function assumes the head of path has been normalized + with normalize_path. + """ + + path = normalize_path(path) + # Hard-coded becouse PyPy uses a different sys.prefix on Debian + prefix = '/usr' + + if running_under_virtualenv(): + return path.startswith(normalize_path(sys.prefix)) + else: + from pip._internal.locations import distutils_scheme + if path.startswith(prefix): + for local_path in distutils_scheme("").values(): + if path.startswith(normalize_path(local_path)): + return True + return False + else: + return True + + +def dist_is_local(dist): + # type: (Distribution) -> bool + """ + Return True if given Distribution object is installed somewhere pip + is allowed to modify. + + """ + return is_local(dist_location(dist)) + + +def dist_in_usersite(dist): + # type: (Distribution) -> bool + """ + Return True if given Distribution is installed in user site. + """ + return dist_location(dist).startswith(normalize_path(user_site)) + + +def dist_in_site_packages(dist): + # type: (Distribution) -> bool + """ + Return True if given Distribution is installed in + sysconfig.get_python_lib(). + """ + return dist_location(dist).startswith(normalize_path(site_packages)) + + +def dist_is_editable(dist): + # type: (Distribution) -> bool + """ + Return True if given Distribution is an editable install. + """ + return bool(egg_link_path(dist)) + + +def get_installed_distributions( + local_only=True, # type: bool + skip=stdlib_pkgs, # type: Container[str] + include_editables=True, # type: bool + editables_only=False, # type: bool + user_only=False, # type: bool + paths=None # type: Optional[List[str]] +): + # type: (...) -> List[Distribution] + """ + Return a list of installed Distribution objects. + + If ``local_only`` is True (default), only return installations + local to the current virtualenv, if in a virtualenv. + + ``skip`` argument is an iterable of lower-case project names to + ignore; defaults to stdlib_pkgs + + If ``include_editables`` is False, don't report editables. + + If ``editables_only`` is True , only report editables. + + If ``user_only`` is True , only report installations in the user + site directory. + + If ``paths`` is set, only report the distributions present at the + specified list of locations. + """ + if paths: + working_set = pkg_resources.WorkingSet(paths) + else: + working_set = pkg_resources.working_set + + if local_only: + local_test = dist_is_local + else: + def local_test(d): + return True + + if include_editables: + def editable_test(d): + return True + else: + def editable_test(d): + return not dist_is_editable(d) + + if editables_only: + def editables_only_test(d): + return dist_is_editable(d) + else: + def editables_only_test(d): + return True + + if user_only: + user_test = dist_in_usersite + else: + def user_test(d): + return True + + return [d for d in working_set + if local_test(d) and + d.key not in skip and + editable_test(d) and + editables_only_test(d) and + user_test(d) + ] + + +def _search_distribution(req_name): + # type: (str) -> Optional[Distribution] + """Find a distribution matching the ``req_name`` in the environment. + + This searches from *all* distributions available in the environment, to + match the behavior of ``pkg_resources.get_distribution()``. + """ + # Canonicalize the name before searching in the list of + # installed distributions and also while creating the package + # dictionary to get the Distribution object + req_name = canonicalize_name(req_name) + packages = get_installed_distributions( + local_only=False, + skip=(), + include_editables=True, + editables_only=False, + user_only=False, + paths=None, + ) + pkg_dict = {canonicalize_name(p.key): p for p in packages} + return pkg_dict.get(req_name) + + +def get_distribution(req_name): + # type: (str) -> Optional[Distribution] + """Given a requirement name, return the installed Distribution object. + + This searches from *all* distributions available in the environment, to + match the behavior of ``pkg_resources.get_distribution()``. + """ + + # Search the distribution by looking through the working set + dist = _search_distribution(req_name) + + # If distribution could not be found, call working_set.require + # to update the working set, and try to find the distribution + # again. + # This might happen for e.g. when you install a package + # twice, once using setup.py develop and again using setup.py install. + # Now when run pip uninstall twice, the package gets removed + # from the working set in the first uninstall, so we have to populate + # the working set again so that pip knows about it and the packages + # gets picked up and is successfully uninstalled the second time too. + if not dist: + try: + pkg_resources.working_set.require(req_name) + except pkg_resources.DistributionNotFound: + return None + return _search_distribution(req_name) + + +def egg_link_path(dist): + # type: (Distribution) -> Optional[str] + """ + Return the path for the .egg-link file if it exists, otherwise, None. + + There's 3 scenarios: + 1) not in a virtualenv + try to find in site.USER_SITE, then site_packages + 2) in a no-global virtualenv + try to find in site_packages + 3) in a yes-global virtualenv + try to find in site_packages, then site.USER_SITE + (don't look in global location) + + For #1 and #3, there could be odd cases, where there's an egg-link in 2 + locations. + + This method will just return the first one found. + """ + sites = [] + if running_under_virtualenv(): + sites.append(site_packages) + if not virtualenv_no_global() and user_site: + sites.append(user_site) + else: + if user_site: + sites.append(user_site) + sites.append(site_packages) + + for site in sites: + egglink = os.path.join(site, dist.project_name) + '.egg-link' + if os.path.isfile(egglink): + return egglink + return None + + +def dist_location(dist): + # type: (Distribution) -> str + """ + Get the site-packages location of this distribution. Generally + this is dist.location, except in the case of develop-installed + packages, where dist.location is the source code location, and we + want to know where the egg-link file is. + + The returned location is normalized (in particular, with symlinks removed). + """ + egg_link = egg_link_path(dist) + if egg_link: + return normalize_path(egg_link) + return normalize_path(dist.location) + + +def write_output(msg, *args): + # type: (Any, Any) -> None + logger.info(msg, *args) + + +class FakeFile(object): + """Wrap a list of lines in an object with readline() to make + ConfigParser happy.""" + def __init__(self, lines): + self._gen = iter(lines) + + def readline(self): + try: + return next(self._gen) + except StopIteration: + return '' + + def __iter__(self): + return self._gen + + +class StreamWrapper(StringIO): + + @classmethod + def from_stream(cls, orig_stream): + cls.orig_stream = orig_stream + return cls() + + # compileall.compile_dir() needs stdout.encoding to print to stdout + @property + def encoding(self): + return self.orig_stream.encoding + + +@contextlib.contextmanager +def captured_output(stream_name): + """Return a context manager used by captured_stdout/stdin/stderr + that temporarily replaces the sys stream *stream_name* with a StringIO. + + Taken from Lib/support/__init__.py in the CPython repo. + """ + orig_stdout = getattr(sys, stream_name) + setattr(sys, stream_name, StreamWrapper.from_stream(orig_stdout)) + try: + yield getattr(sys, stream_name) + finally: + setattr(sys, stream_name, orig_stdout) + + +def captured_stdout(): + """Capture the output of sys.stdout: + + with captured_stdout() as stdout: + print('hello') + self.assertEqual(stdout.getvalue(), 'hello\n') + + Taken from Lib/support/__init__.py in the CPython repo. + """ + return captured_output('stdout') + + +def captured_stderr(): + """ + See captured_stdout(). + """ + return captured_output('stderr') + + +def get_installed_version(dist_name, working_set=None): + """Get the installed version of dist_name avoiding pkg_resources cache""" + # Create a requirement that we'll look for inside of setuptools. + req = pkg_resources.Requirement.parse(dist_name) + + if working_set is None: + # We want to avoid having this cached, so we need to construct a new + # working set each time. + working_set = pkg_resources.WorkingSet() + + # Get the installed distribution from our working set + dist = working_set.find(req) + + # Check to see if we got an installed distribution or not, if we did + # we want to return it's version. + return dist.version if dist else None + + +def consume(iterator): + """Consume an iterable at C speed.""" + deque(iterator, maxlen=0) + + +# Simulates an enum +def enum(*sequential, **named): + enums = dict(zip(sequential, range(len(sequential))), **named) + reverse = {value: key for key, value in enums.items()} + enums['reverse_mapping'] = reverse + return type('Enum', (), enums) + + +def build_netloc(host, port): + # type: (str, Optional[int]) -> str + """ + Build a netloc from a host-port pair + """ + if port is None: + return host + if ':' in host: + # Only wrap host with square brackets when it is IPv6 + host = '[{}]'.format(host) + return '{}:{}'.format(host, port) + + +def build_url_from_netloc(netloc, scheme='https'): + # type: (str, str) -> str + """ + Build a full URL from a netloc. + """ + if netloc.count(':') >= 2 and '@' not in netloc and '[' not in netloc: + # It must be a bare IPv6 address, so wrap it with brackets. + netloc = '[{}]'.format(netloc) + return '{}://{}'.format(scheme, netloc) + + +def parse_netloc(netloc): + # type: (str) -> Tuple[str, Optional[int]] + """ + Return the host-port pair from a netloc. + """ + url = build_url_from_netloc(netloc) + parsed = urllib_parse.urlparse(url) + return parsed.hostname, parsed.port + + +def split_auth_from_netloc(netloc): + """ + Parse out and remove the auth information from a netloc. + + Returns: (netloc, (username, password)). + """ + if '@' not in netloc: + return netloc, (None, None) + + # Split from the right because that's how urllib.parse.urlsplit() + # behaves if more than one @ is present (which can be checked using + # the password attribute of urlsplit()'s return value). + auth, netloc = netloc.rsplit('@', 1) + if ':' in auth: + # Split from the left because that's how urllib.parse.urlsplit() + # behaves if more than one : is present (which again can be checked + # using the password attribute of the return value) + user_pass = auth.split(':', 1) + else: + user_pass = auth, None + + user_pass = tuple( + None if x is None else urllib_unquote(x) for x in user_pass + ) + + return netloc, user_pass + + +def redact_netloc(netloc): + # type: (str) -> str + """ + Replace the sensitive data in a netloc with "****", if it exists. + + For example: + - "user:pass@example.com" returns "user:****@example.com" + - "accesstoken@example.com" returns "****@example.com" + """ + netloc, (user, password) = split_auth_from_netloc(netloc) + if user is None: + return netloc + if password is None: + user = '****' + password = '' + else: + user = urllib_parse.quote(user) + password = ':****' + return '{user}{password}@{netloc}'.format(user=user, + password=password, + netloc=netloc) + + +def _transform_url(url, transform_netloc): + """Transform and replace netloc in a url. + + transform_netloc is a function taking the netloc and returning a + tuple. The first element of this tuple is the new netloc. The + entire tuple is returned. + + Returns a tuple containing the transformed url as item 0 and the + original tuple returned by transform_netloc as item 1. + """ + purl = urllib_parse.urlsplit(url) + netloc_tuple = transform_netloc(purl.netloc) + # stripped url + url_pieces = ( + purl.scheme, netloc_tuple[0], purl.path, purl.query, purl.fragment + ) + surl = urllib_parse.urlunsplit(url_pieces) + return surl, netloc_tuple + + +def _get_netloc(netloc): + return split_auth_from_netloc(netloc) + + +def _redact_netloc(netloc): + return (redact_netloc(netloc),) + + +def split_auth_netloc_from_url(url): + # type: (str) -> Tuple[str, str, Tuple[str, str]] + """ + Parse a url into separate netloc, auth, and url with no auth. + + Returns: (url_without_auth, netloc, (username, password)) + """ + url_without_auth, (netloc, auth) = _transform_url(url, _get_netloc) + return url_without_auth, netloc, auth + + +def remove_auth_from_url(url): + # type: (str) -> str + """Return a copy of url with 'username:password@' removed.""" + # username/pass params are passed to subversion through flags + # and are not recognized in the url. + return _transform_url(url, _get_netloc)[0] + + +def redact_auth_from_url(url): + # type: (str) -> str + """Replace the password in a given url with ****.""" + return _transform_url(url, _redact_netloc)[0] + + +class HiddenText(object): + def __init__( + self, + secret, # type: str + redacted, # type: str + ): + # type: (...) -> None + self.secret = secret + self.redacted = redacted + + def __repr__(self): + # type: (...) -> str + return ''.format(str(self)) + + def __str__(self): + # type: (...) -> str + return self.redacted + + # This is useful for testing. + def __eq__(self, other): + # type: (Any) -> bool + if type(self) != type(other): + return False + + # The string being used for redaction doesn't also have to match, + # just the raw, original string. + return (self.secret == other.secret) + + # We need to provide an explicit __ne__ implementation for Python 2. + # TODO: remove this when we drop PY2 support. + def __ne__(self, other): + # type: (Any) -> bool + return not self == other + + +def hide_value(value): + # type: (str) -> HiddenText + return HiddenText(value, redacted='****') + + +def hide_url(url): + # type: (str) -> HiddenText + redacted = redact_auth_from_url(url) + return HiddenText(url, redacted=redacted) + + +def protect_pip_from_modification_on_windows(modifying_pip): + # type: (bool) -> None + """Protection of pip.exe from modification on Windows + + On Windows, any operation modifying pip should be run as: + python -m pip ... + """ + pip_names = [ + "pip.exe", + "pip{}.exe".format(sys.version_info[0]), + "pip{}.{}.exe".format(*sys.version_info[:2]) + ] + + # See https://github.com/pypa/pip/issues/1299 for more discussion + should_show_use_python_msg = ( + modifying_pip and + WINDOWS and + os.path.basename(sys.argv[0]) in pip_names + ) + + if should_show_use_python_msg: + new_command = [ + sys.executable, "-m", "pip" + ] + sys.argv[1:] + raise CommandError( + 'To modify pip, please run the following command:\n{}' + .format(" ".join(new_command)) + ) + + +def is_console_interactive(): + # type: () -> bool + """Is this console interactive? + """ + return sys.stdin is not None and sys.stdin.isatty() + + +def hash_file(path, blocksize=1 << 20): + # type: (Text, int) -> Tuple[Any, int] + """Return (hash, length) for path using hashlib.sha256() + """ + + h = hashlib.sha256() + length = 0 + with open(path, 'rb') as f: + for block in read_chunks(f, size=blocksize): + length += len(block) + h.update(block) + return h, length + + +def is_wheel_installed(): + """ + Return whether the wheel package is installed. + """ + try: + import wheel # noqa: F401 + except ImportError: + return False + + return True + + +def pairwise(iterable): + # type: (Iterable[Any]) -> Iterator[Tuple[Any, Any]] + """ + Return paired elements. + + For example: + s -> (s0, s1), (s2, s3), (s4, s5), ... + """ + iterable = iter(iterable) + return zip_longest(iterable, iterable) + + +def partition( + pred, # type: Callable[[T], bool] + iterable, # type: Iterable[T] +): + # type: (...) -> Tuple[Iterable[T], Iterable[T]] + """ + Use a predicate to partition entries into false entries and true entries, + like + + partition(is_odd, range(10)) --> 0 2 4 6 8 and 1 3 5 7 9 + """ + t1, t2 = tee(iterable) + return filterfalse(pred, t1), filter(pred, t2) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/models.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/models.py new file mode 100644 index 0000000000..d1c2f22679 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/models.py @@ -0,0 +1,44 @@ +"""Utilities for defining models +""" +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False + +import operator + + +class KeyBasedCompareMixin(object): + """Provides comparison capabilities that is based on a key + """ + + __slots__ = ['_compare_key', '_defining_class'] + + def __init__(self, key, defining_class): + self._compare_key = key + self._defining_class = defining_class + + def __hash__(self): + return hash(self._compare_key) + + def __lt__(self, other): + return self._compare(other, operator.__lt__) + + def __le__(self, other): + return self._compare(other, operator.__le__) + + def __gt__(self, other): + return self._compare(other, operator.__gt__) + + def __ge__(self, other): + return self._compare(other, operator.__ge__) + + def __eq__(self, other): + return self._compare(other, operator.__eq__) + + def __ne__(self, other): + return self._compare(other, operator.__ne__) + + def _compare(self, other, method): + if not isinstance(other, self._defining_class): + return NotImplemented + + return method(self._compare_key, other._compare_key) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/packaging.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/packaging.py new file mode 100644 index 0000000000..27fd204234 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/packaging.py @@ -0,0 +1,95 @@ +from __future__ import absolute_import + +import logging +from email.parser import FeedParser + +from pip._vendor import pkg_resources +from pip._vendor.packaging import specifiers, version + +from pip._internal.exceptions import NoneMetadataError +from pip._internal.utils.misc import display_path +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from email.message import Message + from typing import Optional, Tuple + + from pip._vendor.pkg_resources import Distribution + + +logger = logging.getLogger(__name__) + + +def check_requires_python(requires_python, version_info): + # type: (Optional[str], Tuple[int, ...]) -> bool + """ + Check if the given Python version matches a "Requires-Python" specifier. + + :param version_info: A 3-tuple of ints representing a Python + major-minor-micro version to check (e.g. `sys.version_info[:3]`). + + :return: `True` if the given Python version satisfies the requirement. + Otherwise, return `False`. + + :raises InvalidSpecifier: If `requires_python` has an invalid format. + """ + if requires_python is None: + # The package provides no information + return True + requires_python_specifier = specifiers.SpecifierSet(requires_python) + + python_version = version.parse('.'.join(map(str, version_info))) + return python_version in requires_python_specifier + + +def get_metadata(dist): + # type: (Distribution) -> Message + """ + :raises NoneMetadataError: if the distribution reports `has_metadata()` + True but `get_metadata()` returns None. + """ + metadata_name = 'METADATA' + if (isinstance(dist, pkg_resources.DistInfoDistribution) and + dist.has_metadata(metadata_name)): + metadata = dist.get_metadata(metadata_name) + elif dist.has_metadata('PKG-INFO'): + metadata_name = 'PKG-INFO' + metadata = dist.get_metadata(metadata_name) + else: + logger.warning("No metadata found in %s", display_path(dist.location)) + metadata = '' + + if metadata is None: + raise NoneMetadataError(dist, metadata_name) + + feed_parser = FeedParser() + # The following line errors out if with a "NoneType" TypeError if + # passed metadata=None. + feed_parser.feed(metadata) + return feed_parser.close() + + +def get_requires_python(dist): + # type: (pkg_resources.Distribution) -> Optional[str] + """ + Return the "Requires-Python" metadata for a distribution, or None + if not present. + """ + pkg_info_dict = get_metadata(dist) + requires_python = pkg_info_dict.get('Requires-Python') + + if requires_python is not None: + # Convert to a str to satisfy the type checker, since requires_python + # can be a Header object. + requires_python = str(requires_python) + + return requires_python + + +def get_installer(dist): + # type: (Distribution) -> str + if dist.has_metadata('INSTALLER'): + for line in dist.get_metadata_lines('INSTALLER'): + if line.strip(): + return line.strip() + return '' diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/parallel.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/parallel.py new file mode 100644 index 0000000000..d4113bdc28 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/parallel.py @@ -0,0 +1,107 @@ +"""Convenient parallelization of higher order functions. + +This module provides two helper functions, with appropriate fallbacks on +Python 2 and on systems lacking support for synchronization mechanisms: + +- map_multiprocess +- map_multithread + +These helpers work like Python 3's map, with two differences: + +- They don't guarantee the order of processing of + the elements of the iterable. +- The underlying process/thread pools chop the iterable into + a number of chunks, so that for very long iterables using + a large value for chunksize can make the job complete much faster + than using the default value of 1. +""" + +__all__ = ['map_multiprocess', 'map_multithread'] + +from contextlib import contextmanager +from multiprocessing import Pool as ProcessPool +from multiprocessing.dummy import Pool as ThreadPool + +from pip._vendor.requests.adapters import DEFAULT_POOLSIZE +from pip._vendor.six import PY2 +from pip._vendor.six.moves import map + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from multiprocessing import pool + from typing import Callable, Iterable, Iterator, TypeVar, Union + + Pool = Union[pool.Pool, pool.ThreadPool] + S = TypeVar('S') + T = TypeVar('T') + +# On platforms without sem_open, multiprocessing[.dummy] Pool +# cannot be created. +try: + import multiprocessing.synchronize # noqa +except ImportError: + LACK_SEM_OPEN = True +else: + LACK_SEM_OPEN = False + +# Incredibly large timeout to work around bpo-8296 on Python 2. +TIMEOUT = 2000000 + + +@contextmanager +def closing(pool): + # type: (Pool) -> Iterator[Pool] + """Return a context manager making sure the pool closes properly.""" + try: + yield pool + finally: + # For Pool.imap*, close and join are needed + # for the returned iterator to begin yielding. + pool.close() + pool.join() + pool.terminate() + + +def _map_fallback(func, iterable, chunksize=1): + # type: (Callable[[S], T], Iterable[S], int) -> Iterator[T] + """Make an iterator applying func to each element in iterable. + + This function is the sequential fallback either on Python 2 + where Pool.imap* doesn't react to KeyboardInterrupt + or when sem_open is unavailable. + """ + return map(func, iterable) + + +def _map_multiprocess(func, iterable, chunksize=1): + # type: (Callable[[S], T], Iterable[S], int) -> Iterator[T] + """Chop iterable into chunks and submit them to a process pool. + + For very long iterables using a large value for chunksize can make + the job complete much faster than using the default value of 1. + + Return an unordered iterator of the results. + """ + with closing(ProcessPool()) as pool: + return pool.imap_unordered(func, iterable, chunksize) + + +def _map_multithread(func, iterable, chunksize=1): + # type: (Callable[[S], T], Iterable[S], int) -> Iterator[T] + """Chop iterable into chunks and submit them to a thread pool. + + For very long iterables using a large value for chunksize can make + the job complete much faster than using the default value of 1. + + Return an unordered iterator of the results. + """ + with closing(ThreadPool(DEFAULT_POOLSIZE)) as pool: + return pool.imap_unordered(func, iterable, chunksize) + + +if LACK_SEM_OPEN or PY2: + map_multiprocess = map_multithread = _map_fallback +else: + map_multiprocess = _map_multiprocess + map_multithread = _map_multithread diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/pkg_resources.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/pkg_resources.py new file mode 100644 index 0000000000..0bc129acc6 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/pkg_resources.py @@ -0,0 +1,44 @@ +from pip._vendor.pkg_resources import yield_lines +from pip._vendor.six import ensure_str + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Dict, Iterable, List + + +class DictMetadata(object): + """IMetadataProvider that reads metadata files from a dictionary. + """ + def __init__(self, metadata): + # type: (Dict[str, bytes]) -> None + self._metadata = metadata + + def has_metadata(self, name): + # type: (str) -> bool + return name in self._metadata + + def get_metadata(self, name): + # type: (str) -> str + try: + return ensure_str(self._metadata[name]) + except UnicodeDecodeError as e: + # Mirrors handling done in pkg_resources.NullProvider. + e.reason += " in {} file".format(name) + raise + + def get_metadata_lines(self, name): + # type: (str) -> Iterable[str] + return yield_lines(self.get_metadata(name)) + + def metadata_isdir(self, name): + # type: (str) -> bool + return False + + def metadata_listdir(self, name): + # type: (str) -> List[str] + return [] + + def run_script(self, script_name, namespace): + # type: (str, str) -> None + pass diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/setuptools_build.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/setuptools_build.py new file mode 100644 index 0000000000..2a664b0070 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/setuptools_build.py @@ -0,0 +1,181 @@ +import sys + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List, Optional, Sequence + +# Shim to wrap setup.py invocation with setuptools +# +# We set sys.argv[0] to the path to the underlying setup.py file so +# setuptools / distutils don't take the path to the setup.py to be "-c" when +# invoking via the shim. This avoids e.g. the following manifest_maker +# warning: "warning: manifest_maker: standard file '-c' not found". +_SETUPTOOLS_SHIM = ( + "import sys, setuptools, tokenize; sys.argv[0] = {0!r}; __file__={0!r};" + "f=getattr(tokenize, 'open', open)(__file__);" + "code=f.read().replace('\\r\\n', '\\n');" + "f.close();" + "exec(compile(code, __file__, 'exec'))" +) + + +def make_setuptools_shim_args( + setup_py_path, # type: str + global_options=None, # type: Sequence[str] + no_user_config=False, # type: bool + unbuffered_output=False # type: bool +): + # type: (...) -> List[str] + """ + Get setuptools command arguments with shim wrapped setup file invocation. + + :param setup_py_path: The path to setup.py to be wrapped. + :param global_options: Additional global options. + :param no_user_config: If True, disables personal user configuration. + :param unbuffered_output: If True, adds the unbuffered switch to the + argument list. + """ + args = [sys.executable] + if unbuffered_output: + args += ["-u"] + args += ["-c", _SETUPTOOLS_SHIM.format(setup_py_path)] + if global_options: + args += global_options + if no_user_config: + args += ["--no-user-cfg"] + return args + + +def make_setuptools_bdist_wheel_args( + setup_py_path, # type: str + global_options, # type: Sequence[str] + build_options, # type: Sequence[str] + destination_dir, # type: str +): + # type: (...) -> List[str] + # NOTE: Eventually, we'd want to also -S to the flags here, when we're + # isolating. Currently, it breaks Python in virtualenvs, because it + # relies on site.py to find parts of the standard library outside the + # virtualenv. + args = make_setuptools_shim_args( + setup_py_path, + global_options=global_options, + unbuffered_output=True + ) + args += ["bdist_wheel", "-d", destination_dir] + args += build_options + return args + + +def make_setuptools_clean_args( + setup_py_path, # type: str + global_options, # type: Sequence[str] +): + # type: (...) -> List[str] + args = make_setuptools_shim_args( + setup_py_path, + global_options=global_options, + unbuffered_output=True + ) + args += ["clean", "--all"] + return args + + +def make_setuptools_develop_args( + setup_py_path, # type: str + global_options, # type: Sequence[str] + install_options, # type: Sequence[str] + no_user_config, # type: bool + prefix, # type: Optional[str] + home, # type: Optional[str] + use_user_site, # type: bool +): + # type: (...) -> List[str] + assert not (use_user_site and prefix) + + args = make_setuptools_shim_args( + setup_py_path, + global_options=global_options, + no_user_config=no_user_config, + ) + + args += ["develop", "--no-deps"] + + args += install_options + + if prefix: + args += ["--prefix", prefix] + if home is not None: + args += ["--home", home] + + if use_user_site: + args += ["--user", "--prefix="] + + return args + + +def make_setuptools_egg_info_args( + setup_py_path, # type: str + egg_info_dir, # type: Optional[str] + no_user_config, # type: bool +): + # type: (...) -> List[str] + args = make_setuptools_shim_args( + setup_py_path, no_user_config=no_user_config + ) + + args += ["egg_info"] + + if egg_info_dir: + args += ["--egg-base", egg_info_dir] + + return args + + +def make_setuptools_install_args( + setup_py_path, # type: str + global_options, # type: Sequence[str] + install_options, # type: Sequence[str] + record_filename, # type: str + root, # type: Optional[str] + prefix, # type: Optional[str] + header_dir, # type: Optional[str] + home, # type: Optional[str] + use_user_site, # type: bool + no_user_config, # type: bool + pycompile # type: bool +): + # type: (...) -> List[str] + assert not (use_user_site and prefix) + assert not (use_user_site and root) + + args = make_setuptools_shim_args( + setup_py_path, + global_options=global_options, + no_user_config=no_user_config, + unbuffered_output=True + ) + args += ["install", "--record", record_filename] + args += ["--single-version-externally-managed"] + + if root is not None: + args += ["--root", root] + if prefix is not None: + args += ["--prefix", prefix] + if home is not None: + args += ["--home", home] + if use_user_site: + args += ["--user", "--prefix="] + + if pycompile: + args += ["--compile"] + else: + args += ["--no-compile"] + + if header_dir: + args += ["--install-headers", header_dir] + + args += install_options + + return args diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/subprocess.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/subprocess.py new file mode 100644 index 0000000000..3cd8b01f73 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/subprocess.py @@ -0,0 +1,299 @@ +from __future__ import absolute_import + +import logging +import os +import subprocess + +from pip._vendor.six.moves import shlex_quote + +from pip._internal.cli.spinners import SpinnerInterface, open_spinner +from pip._internal.exceptions import InstallationSubprocessError +from pip._internal.utils.compat import console_to_str, str_to_display +from pip._internal.utils.logging import subprocess_logger +from pip._internal.utils.misc import HiddenText, path_to_display +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Any, Callable, Iterable, List, Mapping, Optional, Text, Union + + CommandArgs = List[Union[str, HiddenText]] + + +LOG_DIVIDER = '----------------------------------------' + + +def make_command(*args): + # type: (Union[str, HiddenText, CommandArgs]) -> CommandArgs + """ + Create a CommandArgs object. + """ + command_args = [] # type: CommandArgs + for arg in args: + # Check for list instead of CommandArgs since CommandArgs is + # only known during type-checking. + if isinstance(arg, list): + command_args.extend(arg) + else: + # Otherwise, arg is str or HiddenText. + command_args.append(arg) + + return command_args + + +def format_command_args(args): + # type: (Union[List[str], CommandArgs]) -> str + """ + Format command arguments for display. + """ + # For HiddenText arguments, display the redacted form by calling str(). + # Also, we don't apply str() to arguments that aren't HiddenText since + # this can trigger a UnicodeDecodeError in Python 2 if the argument + # has type unicode and includes a non-ascii character. (The type + # checker doesn't ensure the annotations are correct in all cases.) + return ' '.join( + shlex_quote(str(arg)) if isinstance(arg, HiddenText) + else shlex_quote(arg) for arg in args + ) + + +def reveal_command_args(args): + # type: (Union[List[str], CommandArgs]) -> List[str] + """ + Return the arguments in their raw, unredacted form. + """ + return [ + arg.secret if isinstance(arg, HiddenText) else arg for arg in args + ] + + +def make_subprocess_output_error( + cmd_args, # type: Union[List[str], CommandArgs] + cwd, # type: Optional[str] + lines, # type: List[Text] + exit_status, # type: int +): + # type: (...) -> Text + """ + Create and return the error message to use to log a subprocess error + with command output. + + :param lines: A list of lines, each ending with a newline. + """ + command = format_command_args(cmd_args) + # Convert `command` and `cwd` to text (unicode in Python 2) so we can use + # them as arguments in the unicode format string below. This avoids + # "UnicodeDecodeError: 'ascii' codec can't decode byte ..." in Python 2 + # if either contains a non-ascii character. + command_display = str_to_display(command, desc='command bytes') + cwd_display = path_to_display(cwd) + + # We know the joined output value ends in a newline. + output = ''.join(lines) + msg = ( + # Use a unicode string to avoid "UnicodeEncodeError: 'ascii' + # codec can't encode character ..." in Python 2 when a format + # argument (e.g. `output`) has a non-ascii character. + u'Command errored out with exit status {exit_status}:\n' + ' command: {command_display}\n' + ' cwd: {cwd_display}\n' + 'Complete output ({line_count} lines):\n{output}{divider}' + ).format( + exit_status=exit_status, + command_display=command_display, + cwd_display=cwd_display, + line_count=len(lines), + output=output, + divider=LOG_DIVIDER, + ) + return msg + + +def call_subprocess( + cmd, # type: Union[List[str], CommandArgs] + show_stdout=False, # type: bool + cwd=None, # type: Optional[str] + on_returncode='raise', # type: str + extra_ok_returncodes=None, # type: Optional[Iterable[int]] + command_desc=None, # type: Optional[str] + extra_environ=None, # type: Optional[Mapping[str, Any]] + unset_environ=None, # type: Optional[Iterable[str]] + spinner=None, # type: Optional[SpinnerInterface] + log_failed_cmd=True, # type: Optional[bool] + stdout_only=False, # type: Optional[bool] +): + # type: (...) -> Text + """ + Args: + show_stdout: if true, use INFO to log the subprocess's stderr and + stdout streams. Otherwise, use DEBUG. Defaults to False. + extra_ok_returncodes: an iterable of integer return codes that are + acceptable, in addition to 0. Defaults to None, which means []. + unset_environ: an iterable of environment variable names to unset + prior to calling subprocess.Popen(). + log_failed_cmd: if false, failed commands are not logged, only raised. + stdout_only: if true, return only stdout, else return both. When true, + logging of both stdout and stderr occurs when the subprocess has + terminated, else logging occurs as subprocess output is produced. + """ + if extra_ok_returncodes is None: + extra_ok_returncodes = [] + if unset_environ is None: + unset_environ = [] + # Most places in pip use show_stdout=False. What this means is-- + # + # - We connect the child's output (combined stderr and stdout) to a + # single pipe, which we read. + # - We log this output to stderr at DEBUG level as it is received. + # - If DEBUG logging isn't enabled (e.g. if --verbose logging wasn't + # requested), then we show a spinner so the user can still see the + # subprocess is in progress. + # - If the subprocess exits with an error, we log the output to stderr + # at ERROR level if it hasn't already been displayed to the console + # (e.g. if --verbose logging wasn't enabled). This way we don't log + # the output to the console twice. + # + # If show_stdout=True, then the above is still done, but with DEBUG + # replaced by INFO. + if show_stdout: + # Then log the subprocess output at INFO level. + log_subprocess = subprocess_logger.info + used_level = logging.INFO + else: + # Then log the subprocess output using DEBUG. This also ensures + # it will be logged to the log file (aka user_log), if enabled. + log_subprocess = subprocess_logger.debug + used_level = logging.DEBUG + + # Whether the subprocess will be visible in the console. + showing_subprocess = subprocess_logger.getEffectiveLevel() <= used_level + + # Only use the spinner if we're not showing the subprocess output + # and we have a spinner. + use_spinner = not showing_subprocess and spinner is not None + + if command_desc is None: + command_desc = format_command_args(cmd) + + log_subprocess("Running command %s", command_desc) + env = os.environ.copy() + if extra_environ: + env.update(extra_environ) + for name in unset_environ: + env.pop(name, None) + try: + proc = subprocess.Popen( + # Convert HiddenText objects to the underlying str. + reveal_command_args(cmd), + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT if not stdout_only else subprocess.PIPE, + cwd=cwd, + env=env, + ) + except Exception as exc: + if log_failed_cmd: + subprocess_logger.critical( + "Error %s while executing command %s", exc, command_desc, + ) + raise + all_output = [] + if not stdout_only: + assert proc.stdout + assert proc.stdin + proc.stdin.close() + # In this mode, stdout and stderr are in the same pipe. + while True: + # The "line" value is a unicode string in Python 2. + line = console_to_str(proc.stdout.readline()) + if not line: + break + line = line.rstrip() + all_output.append(line + '\n') + + # Show the line immediately. + log_subprocess(line) + # Update the spinner. + if use_spinner: + assert spinner + spinner.spin() + try: + proc.wait() + finally: + if proc.stdout: + proc.stdout.close() + output = ''.join(all_output) + else: + # In this mode, stdout and stderr are in different pipes. + # We must use communicate() which is the only safe way to read both. + out_bytes, err_bytes = proc.communicate() + # log line by line to preserve pip log indenting + out = console_to_str(out_bytes) + for out_line in out.splitlines(): + log_subprocess(out_line) + all_output.append(out) + err = console_to_str(err_bytes) + for err_line in err.splitlines(): + log_subprocess(err_line) + all_output.append(err) + output = out + + proc_had_error = ( + proc.returncode and proc.returncode not in extra_ok_returncodes + ) + if use_spinner: + assert spinner + if proc_had_error: + spinner.finish("error") + else: + spinner.finish("done") + if proc_had_error: + if on_returncode == 'raise': + if not showing_subprocess and log_failed_cmd: + # Then the subprocess streams haven't been logged to the + # console yet. + msg = make_subprocess_output_error( + cmd_args=cmd, + cwd=cwd, + lines=all_output, + exit_status=proc.returncode, + ) + subprocess_logger.error(msg) + raise InstallationSubprocessError(proc.returncode, command_desc) + elif on_returncode == 'warn': + subprocess_logger.warning( + 'Command "%s" had error code %s in %s', + command_desc, + proc.returncode, + cwd, + ) + elif on_returncode == 'ignore': + pass + else: + raise ValueError('Invalid value: on_returncode={!r}'.format( + on_returncode)) + return output + + +def runner_with_spinner_message(message): + # type: (str) -> Callable[..., None] + """Provide a subprocess_runner that shows a spinner message. + + Intended for use with for pep517's Pep517HookCaller. Thus, the runner has + an API that matches what's expected by Pep517HookCaller.subprocess_runner. + """ + + def runner( + cmd, # type: List[str] + cwd=None, # type: Optional[str] + extra_environ=None # type: Optional[Mapping[str, Any]] + ): + # type: (...) -> None + with open_spinner(message) as spinner: + call_subprocess( + cmd, + cwd=cwd, + extra_environ=extra_environ, + spinner=spinner, + ) + + return runner diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/temp_dir.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/temp_dir.py new file mode 100644 index 0000000000..371958c931 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/temp_dir.py @@ -0,0 +1,284 @@ +from __future__ import absolute_import + +import errno +import itertools +import logging +import os.path +import tempfile +from contextlib import contextmanager + +from pip._vendor.contextlib2 import ExitStack +from pip._vendor.six import ensure_text + +from pip._internal.utils.compat import WINDOWS +from pip._internal.utils.misc import enum, rmtree +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Any, Dict, Iterator, Optional, TypeVar, Union + + _T = TypeVar('_T', bound='TempDirectory') + + +logger = logging.getLogger(__name__) + + +# Kinds of temporary directories. Only needed for ones that are +# globally-managed. +tempdir_kinds = enum( + BUILD_ENV="build-env", + EPHEM_WHEEL_CACHE="ephem-wheel-cache", + REQ_BUILD="req-build", +) + + +_tempdir_manager = None # type: Optional[ExitStack] + + +@contextmanager +def global_tempdir_manager(): + # type: () -> Iterator[None] + global _tempdir_manager + with ExitStack() as stack: + old_tempdir_manager, _tempdir_manager = _tempdir_manager, stack + try: + yield + finally: + _tempdir_manager = old_tempdir_manager + + +class TempDirectoryTypeRegistry(object): + """Manages temp directory behavior + """ + + def __init__(self): + # type: () -> None + self._should_delete = {} # type: Dict[str, bool] + + def set_delete(self, kind, value): + # type: (str, bool) -> None + """Indicate whether a TempDirectory of the given kind should be + auto-deleted. + """ + self._should_delete[kind] = value + + def get_delete(self, kind): + # type: (str) -> bool + """Get configured auto-delete flag for a given TempDirectory type, + default True. + """ + return self._should_delete.get(kind, True) + + +_tempdir_registry = None # type: Optional[TempDirectoryTypeRegistry] + + +@contextmanager +def tempdir_registry(): + # type: () -> Iterator[TempDirectoryTypeRegistry] + """Provides a scoped global tempdir registry that can be used to dictate + whether directories should be deleted. + """ + global _tempdir_registry + old_tempdir_registry = _tempdir_registry + _tempdir_registry = TempDirectoryTypeRegistry() + try: + yield _tempdir_registry + finally: + _tempdir_registry = old_tempdir_registry + + +class _Default(object): + pass + + +_default = _Default() + + +class TempDirectory(object): + """Helper class that owns and cleans up a temporary directory. + + This class can be used as a context manager or as an OO representation of a + temporary directory. + + Attributes: + path + Location to the created temporary directory + delete + Whether the directory should be deleted when exiting + (when used as a contextmanager) + + Methods: + cleanup() + Deletes the temporary directory + + When used as a context manager, if the delete attribute is True, on + exiting the context the temporary directory is deleted. + """ + + def __init__( + self, + path=None, # type: Optional[str] + delete=_default, # type: Union[bool, None, _Default] + kind="temp", # type: str + globally_managed=False, # type: bool + ): + super(TempDirectory, self).__init__() + + if delete is _default: + if path is not None: + # If we were given an explicit directory, resolve delete option + # now. + delete = False + else: + # Otherwise, we wait until cleanup and see what + # tempdir_registry says. + delete = None + + # The only time we specify path is in for editables where it + # is the value of the --src option. + if path is None: + path = self._create(kind) + + self._path = path + self._deleted = False + self.delete = delete + self.kind = kind + + if globally_managed: + assert _tempdir_manager is not None + _tempdir_manager.enter_context(self) + + @property + def path(self): + # type: () -> str + assert not self._deleted, ( + "Attempted to access deleted path: {}".format(self._path) + ) + return self._path + + def __repr__(self): + # type: () -> str + return "<{} {!r}>".format(self.__class__.__name__, self.path) + + def __enter__(self): + # type: (_T) -> _T + return self + + def __exit__(self, exc, value, tb): + # type: (Any, Any, Any) -> None + if self.delete is not None: + delete = self.delete + elif _tempdir_registry: + delete = _tempdir_registry.get_delete(self.kind) + else: + delete = True + + if delete: + self.cleanup() + + def _create(self, kind): + # type: (str) -> str + """Create a temporary directory and store its path in self.path + """ + # We realpath here because some systems have their default tmpdir + # symlinked to another directory. This tends to confuse build + # scripts, so we canonicalize the path by traversing potential + # symlinks here. + path = os.path.realpath( + tempfile.mkdtemp(prefix="pip-{}-".format(kind)) + ) + logger.debug("Created temporary directory: %s", path) + return path + + def cleanup(self): + # type: () -> None + """Remove the temporary directory created and reset state + """ + self._deleted = True + if not os.path.exists(self._path): + return + # Make sure to pass unicode on Python 2 to make the contents also + # use unicode, ensuring non-ASCII names and can be represented. + # This is only done on Windows because POSIX platforms use bytes + # natively for paths, and the bytes-text conversion omission avoids + # errors caused by the environment configuring encodings incorrectly. + if WINDOWS: + rmtree(ensure_text(self._path)) + else: + rmtree(self._path) + + +class AdjacentTempDirectory(TempDirectory): + """Helper class that creates a temporary directory adjacent to a real one. + + Attributes: + original + The original directory to create a temp directory for. + path + After calling create() or entering, contains the full + path to the temporary directory. + delete + Whether the directory should be deleted when exiting + (when used as a contextmanager) + + """ + # The characters that may be used to name the temp directory + # We always prepend a ~ and then rotate through these until + # a usable name is found. + # pkg_resources raises a different error for .dist-info folder + # with leading '-' and invalid metadata + LEADING_CHARS = "-~.=%0123456789" + + def __init__(self, original, delete=None): + # type: (str, Optional[bool]) -> None + self.original = original.rstrip('/\\') + super(AdjacentTempDirectory, self).__init__(delete=delete) + + @classmethod + def _generate_names(cls, name): + # type: (str) -> Iterator[str] + """Generates a series of temporary names. + + The algorithm replaces the leading characters in the name + with ones that are valid filesystem characters, but are not + valid package names (for both Python and pip definitions of + package). + """ + for i in range(1, len(name)): + for candidate in itertools.combinations_with_replacement( + cls.LEADING_CHARS, i - 1): + new_name = '~' + ''.join(candidate) + name[i:] + if new_name != name: + yield new_name + + # If we make it this far, we will have to make a longer name + for i in range(len(cls.LEADING_CHARS)): + for candidate in itertools.combinations_with_replacement( + cls.LEADING_CHARS, i): + new_name = '~' + ''.join(candidate) + name + if new_name != name: + yield new_name + + def _create(self, kind): + # type: (str) -> str + root, name = os.path.split(self.original) + for candidate in self._generate_names(name): + path = os.path.join(root, candidate) + try: + os.mkdir(path) + except OSError as ex: + # Continue if the name exists already + if ex.errno != errno.EEXIST: + raise + else: + path = os.path.realpath(path) + break + else: + # Final fallback on the default behavior. + path = os.path.realpath( + tempfile.mkdtemp(prefix="pip-{}-".format(kind)) + ) + + logger.debug("Created temporary directory: %s", path) + return path diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/typing.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/typing.py new file mode 100644 index 0000000000..8505a29b15 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/typing.py @@ -0,0 +1,38 @@ +"""For neatly implementing static typing in pip. + +`mypy` - the static type analysis tool we use - uses the `typing` module, which +provides core functionality fundamental to mypy's functioning. + +Generally, `typing` would be imported at runtime and used in that fashion - +it acts as a no-op at runtime and does not have any run-time overhead by +design. + +As it turns out, `typing` is not vendorable - it uses separate sources for +Python 2/Python 3. Thus, this codebase can not expect it to be present. +To work around this, mypy allows the typing import to be behind a False-y +optional to prevent it from running at runtime and type-comments can be used +to remove the need for the types to be accessible directly during runtime. + +This module provides the False-y guard in a nicely named fashion so that a +curious maintainer can reach here to read this. + +In pip, all static-typing related imports should be guarded as follows: + + from pip._internal.utils.typing import MYPY_CHECK_RUNNING + + if MYPY_CHECK_RUNNING: + from typing import ... + +Ref: https://github.com/python/mypy/issues/3216 +""" + +MYPY_CHECK_RUNNING = False + + +if MYPY_CHECK_RUNNING: + from typing import cast +else: + # typing's cast() is needed at runtime, but we don't want to import typing. + # Thus, we use a dummy no-op version, which we tell mypy to ignore. + def cast(type_, value): # type: ignore + return value diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/unpacking.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/unpacking.py new file mode 100644 index 0000000000..620f31ebb7 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/unpacking.py @@ -0,0 +1,281 @@ +"""Utilities related archives. +""" + +from __future__ import absolute_import + +import logging +import os +import shutil +import stat +import tarfile +import zipfile + +from pip._internal.exceptions import InstallationError +from pip._internal.utils.filetypes import ( + BZ2_EXTENSIONS, + TAR_EXTENSIONS, + XZ_EXTENSIONS, + ZIP_EXTENSIONS, +) +from pip._internal.utils.misc import ensure_dir +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Iterable, List, Optional, Text, Union + from zipfile import ZipInfo + + +logger = logging.getLogger(__name__) + + +SUPPORTED_EXTENSIONS = ZIP_EXTENSIONS + TAR_EXTENSIONS + +try: + import bz2 # noqa + SUPPORTED_EXTENSIONS += BZ2_EXTENSIONS +except ImportError: + logger.debug('bz2 module is not available') + +try: + # Only for Python 3.3+ + import lzma # noqa + SUPPORTED_EXTENSIONS += XZ_EXTENSIONS +except ImportError: + logger.debug('lzma module is not available') + + +def current_umask(): + # type: () -> int + """Get the current umask which involves having to set it temporarily.""" + mask = os.umask(0) + os.umask(mask) + return mask + + +def split_leading_dir(path): + # type: (Union[str, Text]) -> List[Union[str, Text]] + path = path.lstrip('/').lstrip('\\') + if ( + '/' in path and ( + ('\\' in path and path.find('/') < path.find('\\')) or + '\\' not in path + ) + ): + return path.split('/', 1) + elif '\\' in path: + return path.split('\\', 1) + else: + return [path, ''] + + +def has_leading_dir(paths): + # type: (Iterable[Union[str, Text]]) -> bool + """Returns true if all the paths have the same leading path name + (i.e., everything is in one subdirectory in an archive)""" + common_prefix = None + for path in paths: + prefix, rest = split_leading_dir(path) + if not prefix: + return False + elif common_prefix is None: + common_prefix = prefix + elif prefix != common_prefix: + return False + return True + + +def is_within_directory(directory, target): + # type: ((Union[str, Text]), (Union[str, Text])) -> bool + """ + Return true if the absolute path of target is within the directory + """ + abs_directory = os.path.abspath(directory) + abs_target = os.path.abspath(target) + + prefix = os.path.commonprefix([abs_directory, abs_target]) + return prefix == abs_directory + + +def set_extracted_file_to_default_mode_plus_executable(path): + # type: (Union[str, Text]) -> None + """ + Make file present at path have execute for user/group/world + (chmod +x) is no-op on windows per python docs + """ + os.chmod(path, (0o777 & ~current_umask() | 0o111)) + + +def zip_item_is_executable(info): + # type: (ZipInfo) -> bool + mode = info.external_attr >> 16 + # if mode and regular file and any execute permissions for + # user/group/world? + return bool(mode and stat.S_ISREG(mode) and mode & 0o111) + + +def unzip_file(filename, location, flatten=True): + # type: (str, str, bool) -> None + """ + Unzip the file (with path `filename`) to the destination `location`. All + files are written based on system defaults and umask (i.e. permissions are + not preserved), except that regular file members with any execute + permissions (user, group, or world) have "chmod +x" applied after being + written. Note that for windows, any execute changes using os.chmod are + no-ops per the python docs. + """ + ensure_dir(location) + zipfp = open(filename, 'rb') + try: + zip = zipfile.ZipFile(zipfp, allowZip64=True) + leading = has_leading_dir(zip.namelist()) and flatten + for info in zip.infolist(): + name = info.filename + fn = name + if leading: + fn = split_leading_dir(name)[1] + fn = os.path.join(location, fn) + dir = os.path.dirname(fn) + if not is_within_directory(location, fn): + message = ( + 'The zip file ({}) has a file ({}) trying to install ' + 'outside target directory ({})' + ) + raise InstallationError(message.format(filename, fn, location)) + if fn.endswith('/') or fn.endswith('\\'): + # A directory + ensure_dir(fn) + else: + ensure_dir(dir) + # Don't use read() to avoid allocating an arbitrarily large + # chunk of memory for the file's content + fp = zip.open(name) + try: + with open(fn, 'wb') as destfp: + shutil.copyfileobj(fp, destfp) + finally: + fp.close() + if zip_item_is_executable(info): + set_extracted_file_to_default_mode_plus_executable(fn) + finally: + zipfp.close() + + +def untar_file(filename, location): + # type: (str, str) -> None + """ + Untar the file (with path `filename`) to the destination `location`. + All files are written based on system defaults and umask (i.e. permissions + are not preserved), except that regular file members with any execute + permissions (user, group, or world) have "chmod +x" applied after being + written. Note that for windows, any execute changes using os.chmod are + no-ops per the python docs. + """ + ensure_dir(location) + if filename.lower().endswith('.gz') or filename.lower().endswith('.tgz'): + mode = 'r:gz' + elif filename.lower().endswith(BZ2_EXTENSIONS): + mode = 'r:bz2' + elif filename.lower().endswith(XZ_EXTENSIONS): + mode = 'r:xz' + elif filename.lower().endswith('.tar'): + mode = 'r' + else: + logger.warning( + 'Cannot determine compression type for file %s', filename, + ) + mode = 'r:*' + tar = tarfile.open(filename, mode) + try: + leading = has_leading_dir([ + member.name for member in tar.getmembers() + ]) + for member in tar.getmembers(): + fn = member.name + if leading: + # https://github.com/python/mypy/issues/1174 + fn = split_leading_dir(fn)[1] # type: ignore + path = os.path.join(location, fn) + if not is_within_directory(location, path): + message = ( + 'The tar file ({}) has a file ({}) trying to install ' + 'outside target directory ({})' + ) + raise InstallationError( + message.format(filename, path, location) + ) + if member.isdir(): + ensure_dir(path) + elif member.issym(): + try: + # https://github.com/python/typeshed/issues/2673 + tar._extract_member(member, path) # type: ignore + except Exception as exc: + # Some corrupt tar files seem to produce this + # (specifically bad symlinks) + logger.warning( + 'In the tar file %s the member %s is invalid: %s', + filename, member.name, exc, + ) + continue + else: + try: + fp = tar.extractfile(member) + except (KeyError, AttributeError) as exc: + # Some corrupt tar files seem to produce this + # (specifically bad symlinks) + logger.warning( + 'In the tar file %s the member %s is invalid: %s', + filename, member.name, exc, + ) + continue + ensure_dir(os.path.dirname(path)) + assert fp is not None + with open(path, 'wb') as destfp: + shutil.copyfileobj(fp, destfp) + fp.close() + # Update the timestamp (useful for cython compiled files) + # https://github.com/python/typeshed/issues/2673 + tar.utime(member, path) # type: ignore + # member have any execute permissions for user/group/world? + if member.mode & 0o111: + set_extracted_file_to_default_mode_plus_executable(path) + finally: + tar.close() + + +def unpack_file( + filename, # type: str + location, # type: str + content_type=None, # type: Optional[str] +): + # type: (...) -> None + filename = os.path.realpath(filename) + if ( + content_type == 'application/zip' or + filename.lower().endswith(ZIP_EXTENSIONS) or + zipfile.is_zipfile(filename) + ): + unzip_file( + filename, + location, + flatten=not filename.endswith('.whl') + ) + elif ( + content_type == 'application/x-gzip' or + tarfile.is_tarfile(filename) or + filename.lower().endswith( + TAR_EXTENSIONS + BZ2_EXTENSIONS + XZ_EXTENSIONS + ) + ): + untar_file(filename, location) + else: + # FIXME: handle? + # FIXME: magic signatures? + logger.critical( + 'Cannot unpack file %s (downloaded from %s, content-type: %s); ' + 'cannot detect archive format', + filename, location, content_type, + ) + raise InstallationError( + 'Cannot determine archive format of {}'.format(location) + ) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/urls.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/urls.py new file mode 100644 index 0000000000..f37bc8f90b --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/urls.py @@ -0,0 +1,55 @@ +import os +import sys + +from pip._vendor.six.moves.urllib import parse as urllib_parse +from pip._vendor.six.moves.urllib import request as urllib_request + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import Optional, Text, Union + + +def get_url_scheme(url): + # type: (Union[str, Text]) -> Optional[Text] + if ':' not in url: + return None + return url.split(':', 1)[0].lower() + + +def path_to_url(path): + # type: (Union[str, Text]) -> str + """ + Convert a path to a file: URL. The path will be made absolute and have + quoted path parts. + """ + path = os.path.normpath(os.path.abspath(path)) + url = urllib_parse.urljoin('file:', urllib_request.pathname2url(path)) + return url + + +def url_to_path(url): + # type: (str) -> str + """ + Convert a file: URL to a path. + """ + assert url.startswith('file:'), ( + "You can only turn file: urls into filenames (not {url!r})" + .format(**locals())) + + _, netloc, path, _, _ = urllib_parse.urlsplit(url) + + if not netloc or netloc == 'localhost': + # According to RFC 8089, same as empty authority. + netloc = '' + elif sys.platform == 'win32': + # If we have a UNC path, prepend UNC share notation. + netloc = '\\\\' + netloc + else: + raise ValueError( + 'non-local file URIs are not supported on this platform: {url!r}' + .format(**locals()) + ) + + path = urllib_request.url2pathname(netloc + path) + return path diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/virtualenv.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/virtualenv.py new file mode 100644 index 0000000000..4a7812873b --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/virtualenv.py @@ -0,0 +1,119 @@ +from __future__ import absolute_import + +import io +import logging +import os +import re +import site +import sys + +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from typing import List, Optional + +logger = logging.getLogger(__name__) +_INCLUDE_SYSTEM_SITE_PACKAGES_REGEX = re.compile( + r"include-system-site-packages\s*=\s*(?Ptrue|false)" +) + + +def _running_under_venv(): + # type: () -> bool + """Checks if sys.base_prefix and sys.prefix match. + + This handles PEP 405 compliant virtual environments. + """ + return sys.prefix != getattr(sys, "base_prefix", sys.prefix) + + +def _running_under_regular_virtualenv(): + # type: () -> bool + """Checks if sys.real_prefix is set. + + This handles virtual environments created with pypa's virtualenv. + """ + # pypa/virtualenv case + return hasattr(sys, 'real_prefix') + + +def running_under_virtualenv(): + # type: () -> bool + """Return True if we're running inside a virtualenv, False otherwise. + """ + return _running_under_venv() or _running_under_regular_virtualenv() + + +def _get_pyvenv_cfg_lines(): + # type: () -> Optional[List[str]] + """Reads {sys.prefix}/pyvenv.cfg and returns its contents as list of lines + + Returns None, if it could not read/access the file. + """ + pyvenv_cfg_file = os.path.join(sys.prefix, 'pyvenv.cfg') + try: + # Although PEP 405 does not specify, the built-in venv module always + # writes with UTF-8. (pypa/pip#8717) + with io.open(pyvenv_cfg_file, encoding='utf-8') as f: + return f.read().splitlines() # avoids trailing newlines + except IOError: + return None + + +def _no_global_under_venv(): + # type: () -> bool + """Check `{sys.prefix}/pyvenv.cfg` for system site-packages inclusion + + PEP 405 specifies that when system site-packages are not supposed to be + visible from a virtual environment, `pyvenv.cfg` must contain the following + line: + + include-system-site-packages = false + + Additionally, log a warning if accessing the file fails. + """ + cfg_lines = _get_pyvenv_cfg_lines() + if cfg_lines is None: + # We're not in a "sane" venv, so assume there is no system + # site-packages access (since that's PEP 405's default state). + logger.warning( + "Could not access 'pyvenv.cfg' despite a virtual environment " + "being active. Assuming global site-packages is not accessible " + "in this environment." + ) + return True + + for line in cfg_lines: + match = _INCLUDE_SYSTEM_SITE_PACKAGES_REGEX.match(line) + if match is not None and match.group('value') == 'false': + return True + return False + + +def _no_global_under_regular_virtualenv(): + # type: () -> bool + """Check if "no-global-site-packages.txt" exists beside site.py + + This mirrors logic in pypa/virtualenv for determining whether system + site-packages are visible in the virtual environment. + """ + site_mod_dir = os.path.dirname(os.path.abspath(site.__file__)) + no_global_site_packages_file = os.path.join( + site_mod_dir, 'no-global-site-packages.txt', + ) + return os.path.exists(no_global_site_packages_file) + + +def virtualenv_no_global(): + # type: () -> bool + """Returns a boolean, whether running in venv with no system site-packages. + """ + # PEP 405 compliance needs to be checked first since virtualenv >=20 would + # return True for both checks, but is only able to use the PEP 405 config. + if _running_under_venv(): + return _no_global_under_venv() + + if _running_under_regular_virtualenv(): + return _no_global_under_regular_virtualenv() + + return False diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/wheel.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/wheel.py new file mode 100644 index 0000000000..9ce371c76e --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/utils/wheel.py @@ -0,0 +1,225 @@ +"""Support functions for working with wheel files. +""" + +from __future__ import absolute_import + +import logging +from email.parser import Parser +from zipfile import ZipFile + +from pip._vendor.packaging.utils import canonicalize_name +from pip._vendor.pkg_resources import DistInfoDistribution +from pip._vendor.six import PY2, ensure_str + +from pip._internal.exceptions import UnsupportedWheel +from pip._internal.utils.pkg_resources import DictMetadata +from pip._internal.utils.typing import MYPY_CHECK_RUNNING + +if MYPY_CHECK_RUNNING: + from email.message import Message + from typing import Dict, Tuple + + from pip._vendor.pkg_resources import Distribution + +if PY2: + from zipfile import BadZipfile as BadZipFile +else: + from zipfile import BadZipFile + + +VERSION_COMPATIBLE = (1, 0) + + +logger = logging.getLogger(__name__) + + +class WheelMetadata(DictMetadata): + """Metadata provider that maps metadata decoding exceptions to our + internal exception type. + """ + def __init__(self, metadata, wheel_name): + # type: (Dict[str, bytes], str) -> None + super(WheelMetadata, self).__init__(metadata) + self._wheel_name = wheel_name + + def get_metadata(self, name): + # type: (str) -> str + try: + return super(WheelMetadata, self).get_metadata(name) + except UnicodeDecodeError as e: + # Augment the default error with the origin of the file. + raise UnsupportedWheel( + "Error decoding metadata for {}: {}".format( + self._wheel_name, e + ) + ) + + +def pkg_resources_distribution_for_wheel(wheel_zip, name, location): + # type: (ZipFile, str, str) -> Distribution + """Get a pkg_resources distribution given a wheel. + + :raises UnsupportedWheel: on any errors + """ + info_dir, _ = parse_wheel(wheel_zip, name) + + metadata_files = [ + p for p in wheel_zip.namelist() if p.startswith("{}/".format(info_dir)) + ] + + metadata_text = {} # type: Dict[str, bytes] + for path in metadata_files: + # If a flag is set, namelist entries may be unicode in Python 2. + # We coerce them to native str type to match the types used in the rest + # of the code. This cannot fail because unicode can always be encoded + # with UTF-8. + full_path = ensure_str(path) + _, metadata_name = full_path.split("/", 1) + + try: + metadata_text[metadata_name] = read_wheel_metadata_file( + wheel_zip, full_path + ) + except UnsupportedWheel as e: + raise UnsupportedWheel( + "{} has an invalid wheel, {}".format(name, str(e)) + ) + + metadata = WheelMetadata(metadata_text, location) + + return DistInfoDistribution( + location=location, metadata=metadata, project_name=name + ) + + +def parse_wheel(wheel_zip, name): + # type: (ZipFile, str) -> Tuple[str, Message] + """Extract information from the provided wheel, ensuring it meets basic + standards. + + Returns the name of the .dist-info directory and the parsed WHEEL metadata. + """ + try: + info_dir = wheel_dist_info_dir(wheel_zip, name) + metadata = wheel_metadata(wheel_zip, info_dir) + version = wheel_version(metadata) + except UnsupportedWheel as e: + raise UnsupportedWheel( + "{} has an invalid wheel, {}".format(name, str(e)) + ) + + check_compatibility(version, name) + + return info_dir, metadata + + +def wheel_dist_info_dir(source, name): + # type: (ZipFile, str) -> str + """Returns the name of the contained .dist-info directory. + + Raises AssertionError or UnsupportedWheel if not found, >1 found, or + it doesn't match the provided name. + """ + # Zip file path separators must be / + subdirs = set(p.split("/", 1)[0] for p in source.namelist()) + + info_dirs = [s for s in subdirs if s.endswith('.dist-info')] + + if not info_dirs: + raise UnsupportedWheel(".dist-info directory not found") + + if len(info_dirs) > 1: + raise UnsupportedWheel( + "multiple .dist-info directories found: {}".format( + ", ".join(info_dirs) + ) + ) + + info_dir = info_dirs[0] + + info_dir_name = canonicalize_name(info_dir) + canonical_name = canonicalize_name(name) + if not info_dir_name.startswith(canonical_name): + raise UnsupportedWheel( + ".dist-info directory {!r} does not start with {!r}".format( + info_dir, canonical_name + ) + ) + + # Zip file paths can be unicode or str depending on the zip entry flags, + # so normalize it. + return ensure_str(info_dir) + + +def read_wheel_metadata_file(source, path): + # type: (ZipFile, str) -> bytes + try: + return source.read(path) + # BadZipFile for general corruption, KeyError for missing entry, + # and RuntimeError for password-protected files + except (BadZipFile, KeyError, RuntimeError) as e: + raise UnsupportedWheel( + "could not read {!r} file: {!r}".format(path, e) + ) + + +def wheel_metadata(source, dist_info_dir): + # type: (ZipFile, str) -> Message + """Return the WHEEL metadata of an extracted wheel, if possible. + Otherwise, raise UnsupportedWheel. + """ + path = "{}/WHEEL".format(dist_info_dir) + # Zip file path separators must be / + wheel_contents = read_wheel_metadata_file(source, path) + + try: + wheel_text = ensure_str(wheel_contents) + except UnicodeDecodeError as e: + raise UnsupportedWheel("error decoding {!r}: {!r}".format(path, e)) + + # FeedParser (used by Parser) does not raise any exceptions. The returned + # message may have .defects populated, but for backwards-compatibility we + # currently ignore them. + return Parser().parsestr(wheel_text) + + +def wheel_version(wheel_data): + # type: (Message) -> Tuple[int, ...] + """Given WHEEL metadata, return the parsed Wheel-Version. + Otherwise, raise UnsupportedWheel. + """ + version_text = wheel_data["Wheel-Version"] + if version_text is None: + raise UnsupportedWheel("WHEEL is missing Wheel-Version") + + version = version_text.strip() + + try: + return tuple(map(int, version.split('.'))) + except ValueError: + raise UnsupportedWheel("invalid Wheel-Version: {!r}".format(version)) + + +def check_compatibility(version, name): + # type: (Tuple[int, ...], str) -> None + """Raises errors or warns if called with an incompatible Wheel-Version. + + pip should refuse to install a Wheel-Version that's a major series + ahead of what it's compatible with (e.g 2.0 > 1.1); and warn when + installing a version only minor version ahead (e.g 1.2 > 1.1). + + version: a 2-tuple representing a Wheel-Version (Major, Minor) + name: name of wheel or package to raise exception about + + :raises UnsupportedWheel: when an incompatible Wheel-Version is given + """ + if version[0] > VERSION_COMPATIBLE[0]: + raise UnsupportedWheel( + "{}'s Wheel-Version ({}) is not compatible with this version " + "of pip".format(name, '.'.join(map(str, version))) + ) + elif version > VERSION_COMPATIBLE: + logger.warning( + 'Installing from a newer Wheel-Version (%s)', + '.'.join(map(str, version)), + ) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/vcs/__init__.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/vcs/__init__.py new file mode 100644 index 0000000000..2a4eb13757 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/vcs/__init__.py @@ -0,0 +1,15 @@ +# Expose a limited set of classes and functions so callers outside of +# the vcs package don't need to import deeper than `pip._internal.vcs`. +# (The test directory and imports protected by MYPY_CHECK_RUNNING may +# still need to import from a vcs sub-package.) +# Import all vcs modules to register each VCS in the VcsSupport object. +import pip._internal.vcs.bazaar +import pip._internal.vcs.git +import pip._internal.vcs.mercurial +import pip._internal.vcs.subversion # noqa: F401 +from pip._internal.vcs.versioncontrol import ( # noqa: F401 + RemoteNotFoundError, + is_url, + make_vcs_requirement_url, + vcs, +) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/vcs/bazaar.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/vcs/bazaar.py new file mode 100644 index 0000000000..4a63d6faa5 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/vcs/bazaar.py @@ -0,0 +1,123 @@ +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False + +from __future__ import absolute_import + +import logging +import os + +from pip._vendor.six.moves.urllib import parse as urllib_parse + +from pip._internal.utils.misc import display_path, rmtree +from pip._internal.utils.subprocess import make_command +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.urls import path_to_url +from pip._internal.vcs.versioncontrol import VersionControl, vcs + +if MYPY_CHECK_RUNNING: + from typing import Optional, Tuple + + from pip._internal.utils.misc import HiddenText + from pip._internal.vcs.versioncontrol import AuthInfo, RevOptions + + +logger = logging.getLogger(__name__) + + +class Bazaar(VersionControl): + name = 'bzr' + dirname = '.bzr' + repo_name = 'branch' + schemes = ( + 'bzr', 'bzr+http', 'bzr+https', 'bzr+ssh', 'bzr+sftp', 'bzr+ftp', + 'bzr+lp', + ) + + def __init__(self, *args, **kwargs): + super(Bazaar, self).__init__(*args, **kwargs) + # This is only needed for python <2.7.5 + # Register lp but do not expose as a scheme to support bzr+lp. + if getattr(urllib_parse, 'uses_fragment', None): + urllib_parse.uses_fragment.extend(['lp']) + + @staticmethod + def get_base_rev_args(rev): + return ['-r', rev] + + def export(self, location, url): + # type: (str, HiddenText) -> None + """ + Export the Bazaar repository at the url to the destination location + """ + # Remove the location to make sure Bazaar can export it correctly + if os.path.exists(location): + rmtree(location) + + url, rev_options = self.get_url_rev_options(url) + self.run_command( + make_command('export', location, url, rev_options.to_args()), + show_stdout=False, + ) + + def fetch_new(self, dest, url, rev_options): + # type: (str, HiddenText, RevOptions) -> None + rev_display = rev_options.to_display() + logger.info( + 'Checking out %s%s to %s', + url, + rev_display, + display_path(dest), + ) + cmd_args = ( + make_command('branch', '-q', rev_options.to_args(), url, dest) + ) + self.run_command(cmd_args) + + def switch(self, dest, url, rev_options): + # type: (str, HiddenText, RevOptions) -> None + self.run_command(make_command('switch', url), cwd=dest) + + def update(self, dest, url, rev_options): + # type: (str, HiddenText, RevOptions) -> None + cmd_args = make_command('pull', '-q', rev_options.to_args()) + self.run_command(cmd_args, cwd=dest) + + @classmethod + def get_url_rev_and_auth(cls, url): + # type: (str) -> Tuple[str, Optional[str], AuthInfo] + # hotfix the URL scheme after removing bzr+ from bzr+ssh:// readd it + url, rev, user_pass = super(Bazaar, cls).get_url_rev_and_auth(url) + if url.startswith('ssh://'): + url = 'bzr+' + url + return url, rev, user_pass + + @classmethod + def get_remote_url(cls, location): + urls = cls.run_command( + ['info'], show_stdout=False, stdout_only=True, cwd=location + ) + for line in urls.splitlines(): + line = line.strip() + for x in ('checkout of branch: ', + 'parent branch: '): + if line.startswith(x): + repo = line.split(x)[1] + if cls._is_local_repository(repo): + return path_to_url(repo) + return repo + return None + + @classmethod + def get_revision(cls, location): + revision = cls.run_command( + ['revno'], show_stdout=False, stdout_only=True, cwd=location, + ) + return revision.splitlines()[-1] + + @classmethod + def is_commit_id_equal(cls, dest, name): + """Always assume the versions don't match""" + return False + + +vcs.register(Bazaar) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/vcs/git.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/vcs/git.py new file mode 100644 index 0000000000..4423a9182c --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/vcs/git.py @@ -0,0 +1,460 @@ +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False + +from __future__ import absolute_import + +import logging +import os.path +import re + +from pip._vendor.packaging.version import parse as parse_version +from pip._vendor.six.moves.urllib import parse as urllib_parse +from pip._vendor.six.moves.urllib import request as urllib_request + +from pip._internal.exceptions import BadCommand, InstallationError +from pip._internal.utils.misc import display_path, hide_url +from pip._internal.utils.subprocess import make_command +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.vcs.versioncontrol import ( + RemoteNotFoundError, + VersionControl, + find_path_to_setup_from_repo_root, + vcs, +) + +if MYPY_CHECK_RUNNING: + from typing import Optional, Tuple + + from pip._internal.utils.misc import HiddenText + from pip._internal.vcs.versioncontrol import AuthInfo, RevOptions + + +urlsplit = urllib_parse.urlsplit +urlunsplit = urllib_parse.urlunsplit + + +logger = logging.getLogger(__name__) + + +HASH_REGEX = re.compile('^[a-fA-F0-9]{40}$') + + +def looks_like_hash(sha): + return bool(HASH_REGEX.match(sha)) + + +class Git(VersionControl): + name = 'git' + dirname = '.git' + repo_name = 'clone' + schemes = ( + 'git', 'git+http', 'git+https', 'git+ssh', 'git+git', 'git+file', + ) + # Prevent the user's environment variables from interfering with pip: + # https://github.com/pypa/pip/issues/1130 + unset_environ = ('GIT_DIR', 'GIT_WORK_TREE') + default_arg_rev = 'HEAD' + + @staticmethod + def get_base_rev_args(rev): + return [rev] + + def is_immutable_rev_checkout(self, url, dest): + # type: (str, str) -> bool + _, rev_options = self.get_url_rev_options(hide_url(url)) + if not rev_options.rev: + return False + if not self.is_commit_id_equal(dest, rev_options.rev): + # the current commit is different from rev, + # which means rev was something else than a commit hash + return False + # return False in the rare case rev is both a commit hash + # and a tag or a branch; we don't want to cache in that case + # because that branch/tag could point to something else in the future + is_tag_or_branch = bool( + self.get_revision_sha(dest, rev_options.rev)[0] + ) + return not is_tag_or_branch + + def get_git_version(self): + VERSION_PFX = 'git version ' + version = self.run_command( + ['version'], show_stdout=False, stdout_only=True + ) + if version.startswith(VERSION_PFX): + version = version[len(VERSION_PFX):].split()[0] + else: + version = '' + # get first 3 positions of the git version because + # on windows it is x.y.z.windows.t, and this parses as + # LegacyVersion which always smaller than a Version. + version = '.'.join(version.split('.')[:3]) + return parse_version(version) + + @classmethod + def get_current_branch(cls, location): + """ + Return the current branch, or None if HEAD isn't at a branch + (e.g. detached HEAD). + """ + # git-symbolic-ref exits with empty stdout if "HEAD" is a detached + # HEAD rather than a symbolic ref. In addition, the -q causes the + # command to exit with status code 1 instead of 128 in this case + # and to suppress the message to stderr. + args = ['symbolic-ref', '-q', 'HEAD'] + output = cls.run_command( + args, + extra_ok_returncodes=(1, ), + show_stdout=False, + stdout_only=True, + cwd=location, + ) + ref = output.strip() + + if ref.startswith('refs/heads/'): + return ref[len('refs/heads/'):] + + return None + + def export(self, location, url): + # type: (str, HiddenText) -> None + """Export the Git repository at the url to the destination location""" + if not location.endswith('/'): + location = location + '/' + + with TempDirectory(kind="export") as temp_dir: + self.unpack(temp_dir.path, url=url) + self.run_command( + ['checkout-index', '-a', '-f', '--prefix', location], + show_stdout=False, cwd=temp_dir.path + ) + + @classmethod + def get_revision_sha(cls, dest, rev): + """ + Return (sha_or_none, is_branch), where sha_or_none is a commit hash + if the revision names a remote branch or tag, otherwise None. + + Args: + dest: the repository directory. + rev: the revision name. + """ + # Pass rev to pre-filter the list. + output = cls.run_command( + ['show-ref', rev], + cwd=dest, + show_stdout=False, + stdout_only=True, + on_returncode='ignore', + ) + refs = {} + # NOTE: We do not use splitlines here since that would split on other + # unicode separators, which can be maliciously used to install a + # different revision. + for line in output.strip().split("\n"): + line = line.rstrip("\r") + if not line: + continue + try: + sha, ref = line.split(" ", maxsplit=2) + except ValueError: + # Include the offending line to simplify troubleshooting if + # this error ever occurs. + raise ValueError('unexpected show-ref line: {!r}'.format(line)) + + refs[ref] = sha + + branch_ref = 'refs/remotes/origin/{}'.format(rev) + tag_ref = 'refs/tags/{}'.format(rev) + + sha = refs.get(branch_ref) + if sha is not None: + return (sha, True) + + sha = refs.get(tag_ref) + + return (sha, False) + + @classmethod + def _should_fetch(cls, dest, rev): + """ + Return true if rev is a ref or is a commit that we don't have locally. + + Branches and tags are not considered in this method because they are + assumed to be always available locally (which is a normal outcome of + ``git clone`` and ``git fetch --tags``). + """ + if rev.startswith("refs/"): + # Always fetch remote refs. + return True + + if not looks_like_hash(rev): + # Git fetch would fail with abbreviated commits. + return False + + if cls.has_commit(dest, rev): + # Don't fetch if we have the commit locally. + return False + + return True + + @classmethod + def resolve_revision(cls, dest, url, rev_options): + # type: (str, HiddenText, RevOptions) -> RevOptions + """ + Resolve a revision to a new RevOptions object with the SHA1 of the + branch, tag, or ref if found. + + Args: + rev_options: a RevOptions object. + """ + rev = rev_options.arg_rev + # The arg_rev property's implementation for Git ensures that the + # rev return value is always non-None. + assert rev is not None + + sha, is_branch = cls.get_revision_sha(dest, rev) + + if sha is not None: + rev_options = rev_options.make_new(sha) + rev_options.branch_name = rev if is_branch else None + + return rev_options + + # Do not show a warning for the common case of something that has + # the form of a Git commit hash. + if not looks_like_hash(rev): + logger.warning( + "Did not find branch or tag '%s', assuming revision or ref.", + rev, + ) + + if not cls._should_fetch(dest, rev): + return rev_options + + # fetch the requested revision + cls.run_command( + make_command('fetch', '-q', url, rev_options.to_args()), + cwd=dest, + ) + # Change the revision to the SHA of the ref we fetched + sha = cls.get_revision(dest, rev='FETCH_HEAD') + rev_options = rev_options.make_new(sha) + + return rev_options + + @classmethod + def is_commit_id_equal(cls, dest, name): + """ + Return whether the current commit hash equals the given name. + + Args: + dest: the repository directory. + name: a string name. + """ + if not name: + # Then avoid an unnecessary subprocess call. + return False + + return cls.get_revision(dest) == name + + def fetch_new(self, dest, url, rev_options): + # type: (str, HiddenText, RevOptions) -> None + rev_display = rev_options.to_display() + logger.info('Cloning %s%s to %s', url, rev_display, display_path(dest)) + self.run_command(make_command('clone', '-q', url, dest)) + + if rev_options.rev: + # Then a specific revision was requested. + rev_options = self.resolve_revision(dest, url, rev_options) + branch_name = getattr(rev_options, 'branch_name', None) + if branch_name is None: + # Only do a checkout if the current commit id doesn't match + # the requested revision. + if not self.is_commit_id_equal(dest, rev_options.rev): + cmd_args = make_command( + 'checkout', '-q', rev_options.to_args(), + ) + self.run_command(cmd_args, cwd=dest) + elif self.get_current_branch(dest) != branch_name: + # Then a specific branch was requested, and that branch + # is not yet checked out. + track_branch = 'origin/{}'.format(branch_name) + cmd_args = [ + 'checkout', '-b', branch_name, '--track', track_branch, + ] + self.run_command(cmd_args, cwd=dest) + + #: repo may contain submodules + self.update_submodules(dest) + + def switch(self, dest, url, rev_options): + # type: (str, HiddenText, RevOptions) -> None + self.run_command( + make_command('config', 'remote.origin.url', url), + cwd=dest, + ) + cmd_args = make_command('checkout', '-q', rev_options.to_args()) + self.run_command(cmd_args, cwd=dest) + + self.update_submodules(dest) + + def update(self, dest, url, rev_options): + # type: (str, HiddenText, RevOptions) -> None + # First fetch changes from the default remote + if self.get_git_version() >= parse_version('1.9.0'): + # fetch tags in addition to everything else + self.run_command(['fetch', '-q', '--tags'], cwd=dest) + else: + self.run_command(['fetch', '-q'], cwd=dest) + # Then reset to wanted revision (maybe even origin/master) + rev_options = self.resolve_revision(dest, url, rev_options) + cmd_args = make_command('reset', '--hard', '-q', rev_options.to_args()) + self.run_command(cmd_args, cwd=dest) + #: update submodules + self.update_submodules(dest) + + @classmethod + def get_remote_url(cls, location): + """ + Return URL of the first remote encountered. + + Raises RemoteNotFoundError if the repository does not have a remote + url configured. + """ + # We need to pass 1 for extra_ok_returncodes since the command + # exits with return code 1 if there are no matching lines. + stdout = cls.run_command( + ['config', '--get-regexp', r'remote\..*\.url'], + extra_ok_returncodes=(1, ), + show_stdout=False, + stdout_only=True, + cwd=location, + ) + remotes = stdout.splitlines() + try: + found_remote = remotes[0] + except IndexError: + raise RemoteNotFoundError + + for remote in remotes: + if remote.startswith('remote.origin.url '): + found_remote = remote + break + url = found_remote.split(' ')[1] + return url.strip() + + @classmethod + def has_commit(cls, location, rev): + """ + Check if rev is a commit that is available in the local repository. + """ + try: + cls.run_command( + ['rev-parse', '-q', '--verify', "sha^" + rev], + cwd=location, + log_failed_cmd=False, + ) + except InstallationError: + return False + else: + return True + + @classmethod + def get_revision(cls, location, rev=None): + if rev is None: + rev = 'HEAD' + current_rev = cls.run_command( + ['rev-parse', rev], + show_stdout=False, + stdout_only=True, + cwd=location, + ) + return current_rev.strip() + + @classmethod + def get_subdirectory(cls, location): + """ + Return the path to setup.py, relative to the repo root. + Return None if setup.py is in the repo root. + """ + # find the repo root + git_dir = cls.run_command( + ['rev-parse', '--git-dir'], + show_stdout=False, + stdout_only=True, + cwd=location, + ).strip() + if not os.path.isabs(git_dir): + git_dir = os.path.join(location, git_dir) + repo_root = os.path.abspath(os.path.join(git_dir, '..')) + return find_path_to_setup_from_repo_root(location, repo_root) + + @classmethod + def get_url_rev_and_auth(cls, url): + # type: (str) -> Tuple[str, Optional[str], AuthInfo] + """ + Prefixes stub URLs like 'user@hostname:user/repo.git' with 'ssh://'. + That's required because although they use SSH they sometimes don't + work with a ssh:// scheme (e.g. GitHub). But we need a scheme for + parsing. Hence we remove it again afterwards and return it as a stub. + """ + # Works around an apparent Git bug + # (see https://article.gmane.org/gmane.comp.version-control.git/146500) + scheme, netloc, path, query, fragment = urlsplit(url) + if scheme.endswith('file'): + initial_slashes = path[:-len(path.lstrip('/'))] + newpath = ( + initial_slashes + + urllib_request.url2pathname(path) + .replace('\\', '/').lstrip('/') + ) + after_plus = scheme.find('+') + 1 + url = scheme[:after_plus] + urlunsplit( + (scheme[after_plus:], netloc, newpath, query, fragment), + ) + + if '://' not in url: + assert 'file:' not in url + url = url.replace('git+', 'git+ssh://') + url, rev, user_pass = super(Git, cls).get_url_rev_and_auth(url) + url = url.replace('ssh://', '') + else: + url, rev, user_pass = super(Git, cls).get_url_rev_and_auth(url) + + return url, rev, user_pass + + @classmethod + def update_submodules(cls, location): + if not os.path.exists(os.path.join(location, '.gitmodules')): + return + cls.run_command( + ['submodule', 'update', '--init', '--recursive', '-q'], + cwd=location, + ) + + @classmethod + def get_repository_root(cls, location): + loc = super(Git, cls).get_repository_root(location) + if loc: + return loc + try: + r = cls.run_command( + ['rev-parse', '--show-toplevel'], + cwd=location, + show_stdout=False, + stdout_only=True, + on_returncode='raise', + log_failed_cmd=False, + ) + except BadCommand: + logger.debug("could not determine if %s is under git control " + "because git is not available", location) + return None + except InstallationError: + return None + return os.path.normpath(r.rstrip('\r\n')) + + +vcs.register(Git) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/vcs/mercurial.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/vcs/mercurial.py new file mode 100644 index 0000000000..d2d145f623 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/vcs/mercurial.py @@ -0,0 +1,172 @@ +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False + +from __future__ import absolute_import + +import logging +import os + +from pip._vendor.six.moves import configparser + +from pip._internal.exceptions import BadCommand, InstallationError +from pip._internal.utils.misc import display_path +from pip._internal.utils.subprocess import make_command +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.urls import path_to_url +from pip._internal.vcs.versioncontrol import ( + VersionControl, + find_path_to_setup_from_repo_root, + vcs, +) + +if MYPY_CHECK_RUNNING: + from pip._internal.utils.misc import HiddenText + from pip._internal.vcs.versioncontrol import RevOptions + + +logger = logging.getLogger(__name__) + + +class Mercurial(VersionControl): + name = 'hg' + dirname = '.hg' + repo_name = 'clone' + schemes = ( + 'hg', 'hg+file', 'hg+http', 'hg+https', 'hg+ssh', 'hg+static-http', + ) + + @staticmethod + def get_base_rev_args(rev): + return [rev] + + def export(self, location, url): + # type: (str, HiddenText) -> None + """Export the Hg repository at the url to the destination location""" + with TempDirectory(kind="export") as temp_dir: + self.unpack(temp_dir.path, url=url) + + self.run_command( + ['archive', location], show_stdout=False, cwd=temp_dir.path + ) + + def fetch_new(self, dest, url, rev_options): + # type: (str, HiddenText, RevOptions) -> None + rev_display = rev_options.to_display() + logger.info( + 'Cloning hg %s%s to %s', + url, + rev_display, + display_path(dest), + ) + self.run_command(make_command('clone', '--noupdate', '-q', url, dest)) + self.run_command( + make_command('update', '-q', rev_options.to_args()), + cwd=dest, + ) + + def switch(self, dest, url, rev_options): + # type: (str, HiddenText, RevOptions) -> None + repo_config = os.path.join(dest, self.dirname, 'hgrc') + config = configparser.RawConfigParser() + try: + config.read(repo_config) + config.set('paths', 'default', url.secret) + with open(repo_config, 'w') as config_file: + config.write(config_file) + except (OSError, configparser.NoSectionError) as exc: + logger.warning( + 'Could not switch Mercurial repository to %s: %s', url, exc, + ) + else: + cmd_args = make_command('update', '-q', rev_options.to_args()) + self.run_command(cmd_args, cwd=dest) + + def update(self, dest, url, rev_options): + # type: (str, HiddenText, RevOptions) -> None + self.run_command(['pull', '-q'], cwd=dest) + cmd_args = make_command('update', '-q', rev_options.to_args()) + self.run_command(cmd_args, cwd=dest) + + @classmethod + def get_remote_url(cls, location): + url = cls.run_command( + ['showconfig', 'paths.default'], + show_stdout=False, + stdout_only=True, + cwd=location, + ).strip() + if cls._is_local_repository(url): + url = path_to_url(url) + return url.strip() + + @classmethod + def get_revision(cls, location): + """ + Return the repository-local changeset revision number, as an integer. + """ + current_revision = cls.run_command( + ['parents', '--template={rev}'], + show_stdout=False, + stdout_only=True, + cwd=location, + ).strip() + return current_revision + + @classmethod + def get_requirement_revision(cls, location): + """ + Return the changeset identification hash, as a 40-character + hexadecimal string + """ + current_rev_hash = cls.run_command( + ['parents', '--template={node}'], + show_stdout=False, + stdout_only=True, + cwd=location, + ).strip() + return current_rev_hash + + @classmethod + def is_commit_id_equal(cls, dest, name): + """Always assume the versions don't match""" + return False + + @classmethod + def get_subdirectory(cls, location): + """ + Return the path to setup.py, relative to the repo root. + Return None if setup.py is in the repo root. + """ + # find the repo root + repo_root = cls.run_command( + ['root'], show_stdout=False, stdout_only=True, cwd=location + ).strip() + if not os.path.isabs(repo_root): + repo_root = os.path.abspath(os.path.join(location, repo_root)) + return find_path_to_setup_from_repo_root(location, repo_root) + + @classmethod + def get_repository_root(cls, location): + loc = super(Mercurial, cls).get_repository_root(location) + if loc: + return loc + try: + r = cls.run_command( + ['root'], + cwd=location, + show_stdout=False, + stdout_only=True, + on_returncode='raise', + log_failed_cmd=False, + ) + except BadCommand: + logger.debug("could not determine if %s is under hg control " + "because hg is not available", location) + return None + except InstallationError: + return None + return os.path.normpath(r.rstrip('\r\n')) + + +vcs.register(Mercurial) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/vcs/subversion.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/vcs/subversion.py new file mode 100644 index 0000000000..701f41db4b --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/vcs/subversion.py @@ -0,0 +1,340 @@ +# The following comment should be removed at some point in the future. +# mypy: disallow-untyped-defs=False + +from __future__ import absolute_import + +import logging +import os +import re + +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import ( + display_path, + is_console_interactive, + rmtree, + split_auth_from_netloc, +) +from pip._internal.utils.subprocess import make_command +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.vcs.versioncontrol import VersionControl, vcs + +_svn_xml_url_re = re.compile('url="([^"]+)"') +_svn_rev_re = re.compile(r'committed-rev="(\d+)"') +_svn_info_xml_rev_re = re.compile(r'\s*revision="(\d+)"') +_svn_info_xml_url_re = re.compile(r'(.*)') + + +if MYPY_CHECK_RUNNING: + from typing import Optional, Tuple + + from pip._internal.utils.misc import HiddenText + from pip._internal.utils.subprocess import CommandArgs + from pip._internal.vcs.versioncontrol import AuthInfo, RevOptions + + +logger = logging.getLogger(__name__) + + +class Subversion(VersionControl): + name = 'svn' + dirname = '.svn' + repo_name = 'checkout' + schemes = ('svn', 'svn+ssh', 'svn+http', 'svn+https', 'svn+svn') + + @classmethod + def should_add_vcs_url_prefix(cls, remote_url): + return True + + @staticmethod + def get_base_rev_args(rev): + return ['-r', rev] + + @classmethod + def get_revision(cls, location): + """ + Return the maximum revision for all files under a given location + """ + # Note: taken from setuptools.command.egg_info + revision = 0 + + for base, dirs, _ in os.walk(location): + if cls.dirname not in dirs: + dirs[:] = [] + continue # no sense walking uncontrolled subdirs + dirs.remove(cls.dirname) + entries_fn = os.path.join(base, cls.dirname, 'entries') + if not os.path.exists(entries_fn): + # FIXME: should we warn? + continue + + dirurl, localrev = cls._get_svn_url_rev(base) + + if base == location: + base = dirurl + '/' # save the root url + elif not dirurl or not dirurl.startswith(base): + dirs[:] = [] + continue # not part of the same svn tree, skip it + revision = max(revision, localrev) + return revision + + @classmethod + def get_netloc_and_auth(cls, netloc, scheme): + """ + This override allows the auth information to be passed to svn via the + --username and --password options instead of via the URL. + """ + if scheme == 'ssh': + # The --username and --password options can't be used for + # svn+ssh URLs, so keep the auth information in the URL. + return super(Subversion, cls).get_netloc_and_auth(netloc, scheme) + + return split_auth_from_netloc(netloc) + + @classmethod + def get_url_rev_and_auth(cls, url): + # type: (str) -> Tuple[str, Optional[str], AuthInfo] + # hotfix the URL scheme after removing svn+ from svn+ssh:// readd it + url, rev, user_pass = super(Subversion, cls).get_url_rev_and_auth(url) + if url.startswith('ssh://'): + url = 'svn+' + url + return url, rev, user_pass + + @staticmethod + def make_rev_args(username, password): + # type: (Optional[str], Optional[HiddenText]) -> CommandArgs + extra_args = [] # type: CommandArgs + if username: + extra_args += ['--username', username] + if password: + extra_args += ['--password', password] + + return extra_args + + @classmethod + def get_remote_url(cls, location): + # In cases where the source is in a subdirectory, not alongside + # setup.py we have to look up in the location until we find a real + # setup.py + orig_location = location + while not os.path.exists(os.path.join(location, 'setup.py')): + last_location = location + location = os.path.dirname(location) + if location == last_location: + # We've traversed up to the root of the filesystem without + # finding setup.py + logger.warning( + "Could not find setup.py for directory %s (tried all " + "parent directories)", + orig_location, + ) + return None + + return cls._get_svn_url_rev(location)[0] + + @classmethod + def _get_svn_url_rev(cls, location): + from pip._internal.exceptions import InstallationError + + entries_path = os.path.join(location, cls.dirname, 'entries') + if os.path.exists(entries_path): + with open(entries_path) as f: + data = f.read() + else: # subversion >= 1.7 does not have the 'entries' file + data = '' + + if (data.startswith('8') or + data.startswith('9') or + data.startswith('10')): + data = list(map(str.splitlines, data.split('\n\x0c\n'))) + del data[0][0] # get rid of the '8' + url = data[0][3] + revs = [int(d[9]) for d in data if len(d) > 9 and d[9]] + [0] + elif data.startswith('= 1.7 + # Note that using get_remote_call_options is not necessary here + # because `svn info` is being run against a local directory. + # We don't need to worry about making sure interactive mode + # is being used to prompt for passwords, because passwords + # are only potentially needed for remote server requests. + xml = cls.run_command( + ['info', '--xml', location], + show_stdout=False, + stdout_only=True, + ) + url = _svn_info_xml_url_re.search(xml).group(1) + revs = [ + int(m.group(1)) for m in _svn_info_xml_rev_re.finditer(xml) + ] + except InstallationError: + url, revs = None, [] + + if revs: + rev = max(revs) + else: + rev = 0 + + return url, rev + + @classmethod + def is_commit_id_equal(cls, dest, name): + """Always assume the versions don't match""" + return False + + def __init__(self, use_interactive=None): + # type: (bool) -> None + if use_interactive is None: + use_interactive = is_console_interactive() + self.use_interactive = use_interactive + + # This member is used to cache the fetched version of the current + # ``svn`` client. + # Special value definitions: + # None: Not evaluated yet. + # Empty tuple: Could not parse version. + self._vcs_version = None # type: Optional[Tuple[int, ...]] + + super(Subversion, self).__init__() + + def call_vcs_version(self): + # type: () -> Tuple[int, ...] + """Query the version of the currently installed Subversion client. + + :return: A tuple containing the parts of the version information or + ``()`` if the version returned from ``svn`` could not be parsed. + :raises: BadCommand: If ``svn`` is not installed. + """ + # Example versions: + # svn, version 1.10.3 (r1842928) + # compiled Feb 25 2019, 14:20:39 on x86_64-apple-darwin17.0.0 + # svn, version 1.7.14 (r1542130) + # compiled Mar 28 2018, 08:49:13 on x86_64-pc-linux-gnu + # svn, version 1.12.0-SlikSvn (SlikSvn/1.12.0) + # compiled May 28 2019, 13:44:56 on x86_64-microsoft-windows6.2 + version_prefix = 'svn, version ' + version = self.run_command( + ['--version'], show_stdout=False, stdout_only=True + ) + if not version.startswith(version_prefix): + return () + + version = version[len(version_prefix):].split()[0] + version_list = version.partition('-')[0].split('.') + try: + parsed_version = tuple(map(int, version_list)) + except ValueError: + return () + + return parsed_version + + def get_vcs_version(self): + # type: () -> Tuple[int, ...] + """Return the version of the currently installed Subversion client. + + If the version of the Subversion client has already been queried, + a cached value will be used. + + :return: A tuple containing the parts of the version information or + ``()`` if the version returned from ``svn`` could not be parsed. + :raises: BadCommand: If ``svn`` is not installed. + """ + if self._vcs_version is not None: + # Use cached version, if available. + # If parsing the version failed previously (empty tuple), + # do not attempt to parse it again. + return self._vcs_version + + vcs_version = self.call_vcs_version() + self._vcs_version = vcs_version + return vcs_version + + def get_remote_call_options(self): + # type: () -> CommandArgs + """Return options to be used on calls to Subversion that contact the server. + + These options are applicable for the following ``svn`` subcommands used + in this class. + + - checkout + - export + - switch + - update + + :return: A list of command line arguments to pass to ``svn``. + """ + if not self.use_interactive: + # --non-interactive switch is available since Subversion 0.14.4. + # Subversion < 1.8 runs in interactive mode by default. + return ['--non-interactive'] + + svn_version = self.get_vcs_version() + # By default, Subversion >= 1.8 runs in non-interactive mode if + # stdin is not a TTY. Since that is how pip invokes SVN, in + # call_subprocess(), pip must pass --force-interactive to ensure + # the user can be prompted for a password, if required. + # SVN added the --force-interactive option in SVN 1.8. Since + # e.g. RHEL/CentOS 7, which is supported until 2024, ships with + # SVN 1.7, pip should continue to support SVN 1.7. Therefore, pip + # can't safely add the option if the SVN version is < 1.8 (or unknown). + if svn_version >= (1, 8): + return ['--force-interactive'] + + return [] + + def export(self, location, url): + # type: (str, HiddenText) -> None + """Export the svn repository at the url to the destination location""" + url, rev_options = self.get_url_rev_options(url) + + logger.info('Exporting svn repository %s to %s', url, location) + with indent_log(): + if os.path.exists(location): + # Subversion doesn't like to check out over an existing + # directory --force fixes this, but was only added in svn 1.5 + rmtree(location) + cmd_args = make_command( + 'export', self.get_remote_call_options(), + rev_options.to_args(), url, location, + ) + self.run_command(cmd_args, show_stdout=False) + + def fetch_new(self, dest, url, rev_options): + # type: (str, HiddenText, RevOptions) -> None + rev_display = rev_options.to_display() + logger.info( + 'Checking out %s%s to %s', + url, + rev_display, + display_path(dest), + ) + cmd_args = make_command( + 'checkout', '-q', self.get_remote_call_options(), + rev_options.to_args(), url, dest, + ) + self.run_command(cmd_args) + + def switch(self, dest, url, rev_options): + # type: (str, HiddenText, RevOptions) -> None + cmd_args = make_command( + 'switch', self.get_remote_call_options(), rev_options.to_args(), + url, dest, + ) + self.run_command(cmd_args) + + def update(self, dest, url, rev_options): + # type: (str, HiddenText, RevOptions) -> None + cmd_args = make_command( + 'update', self.get_remote_call_options(), rev_options.to_args(), + dest, + ) + self.run_command(cmd_args) + + +vcs.register(Subversion) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/vcs/versioncontrol.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/vcs/versioncontrol.py new file mode 100644 index 0000000000..0e807a2fb0 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/vcs/versioncontrol.py @@ -0,0 +1,735 @@ +"""Handles all VCS (version control) support""" + +from __future__ import absolute_import + +import errno +import logging +import os +import shutil +import sys + +from pip._vendor import pkg_resources +from pip._vendor.six.moves.urllib import parse as urllib_parse + +from pip._internal.exceptions import BadCommand, InstallationError +from pip._internal.utils.compat import samefile +from pip._internal.utils.misc import ( + ask_path_exists, + backup_dir, + display_path, + hide_url, + hide_value, + rmtree, +) +from pip._internal.utils.subprocess import call_subprocess, make_command +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.urls import get_url_scheme + +if MYPY_CHECK_RUNNING: + from typing import ( + Any, + Dict, + Iterable, + Iterator, + List, + Mapping, + Optional, + Text, + Tuple, + Type, + Union, + ) + + from pip._internal.cli.spinners import SpinnerInterface + from pip._internal.utils.misc import HiddenText + from pip._internal.utils.subprocess import CommandArgs + + AuthInfo = Tuple[Optional[str], Optional[str]] + + +__all__ = ['vcs'] + + +logger = logging.getLogger(__name__) + + +def is_url(name): + # type: (Union[str, Text]) -> bool + """ + Return true if the name looks like a URL. + """ + scheme = get_url_scheme(name) + if scheme is None: + return False + return scheme in ['http', 'https', 'file', 'ftp'] + vcs.all_schemes + + +def make_vcs_requirement_url(repo_url, rev, project_name, subdir=None): + # type: (str, str, str, Optional[str]) -> str + """ + Return the URL for a VCS requirement. + + Args: + repo_url: the remote VCS url, with any needed VCS prefix (e.g. "git+"). + project_name: the (unescaped) project name. + """ + egg_project_name = pkg_resources.to_filename(project_name) + req = '{}@{}#egg={}'.format(repo_url, rev, egg_project_name) + if subdir: + req += '&subdirectory={}'.format(subdir) + + return req + + +def find_path_to_setup_from_repo_root(location, repo_root): + # type: (str, str) -> Optional[str] + """ + Find the path to `setup.py` by searching up the filesystem from `location`. + Return the path to `setup.py` relative to `repo_root`. + Return None if `setup.py` is in `repo_root` or cannot be found. + """ + # find setup.py + orig_location = location + while not os.path.exists(os.path.join(location, 'setup.py')): + last_location = location + location = os.path.dirname(location) + if location == last_location: + # We've traversed up to the root of the filesystem without + # finding setup.py + logger.warning( + "Could not find setup.py for directory %s (tried all " + "parent directories)", + orig_location, + ) + return None + + if samefile(repo_root, location): + return None + + return os.path.relpath(location, repo_root) + + +class RemoteNotFoundError(Exception): + pass + + +class RevOptions(object): + + """ + Encapsulates a VCS-specific revision to install, along with any VCS + install options. + + Instances of this class should be treated as if immutable. + """ + + def __init__( + self, + vc_class, # type: Type[VersionControl] + rev=None, # type: Optional[str] + extra_args=None, # type: Optional[CommandArgs] + ): + # type: (...) -> None + """ + Args: + vc_class: a VersionControl subclass. + rev: the name of the revision to install. + extra_args: a list of extra options. + """ + if extra_args is None: + extra_args = [] + + self.extra_args = extra_args + self.rev = rev + self.vc_class = vc_class + self.branch_name = None # type: Optional[str] + + def __repr__(self): + # type: () -> str + return ''.format(self.vc_class.name, self.rev) + + @property + def arg_rev(self): + # type: () -> Optional[str] + if self.rev is None: + return self.vc_class.default_arg_rev + + return self.rev + + def to_args(self): + # type: () -> CommandArgs + """ + Return the VCS-specific command arguments. + """ + args = [] # type: CommandArgs + rev = self.arg_rev + if rev is not None: + args += self.vc_class.get_base_rev_args(rev) + args += self.extra_args + + return args + + def to_display(self): + # type: () -> str + if not self.rev: + return '' + + return ' (to revision {})'.format(self.rev) + + def make_new(self, rev): + # type: (str) -> RevOptions + """ + Make a copy of the current instance, but with a new rev. + + Args: + rev: the name of the revision for the new object. + """ + return self.vc_class.make_rev_options(rev, extra_args=self.extra_args) + + +class VcsSupport(object): + _registry = {} # type: Dict[str, VersionControl] + schemes = ['ssh', 'git', 'hg', 'bzr', 'sftp', 'svn'] + + def __init__(self): + # type: () -> None + # Register more schemes with urlparse for various version control + # systems + urllib_parse.uses_netloc.extend(self.schemes) + # Python >= 2.7.4, 3.3 doesn't have uses_fragment + if getattr(urllib_parse, 'uses_fragment', None): + urllib_parse.uses_fragment.extend(self.schemes) + super(VcsSupport, self).__init__() + + def __iter__(self): + # type: () -> Iterator[str] + return self._registry.__iter__() + + @property + def backends(self): + # type: () -> List[VersionControl] + return list(self._registry.values()) + + @property + def dirnames(self): + # type: () -> List[str] + return [backend.dirname for backend in self.backends] + + @property + def all_schemes(self): + # type: () -> List[str] + schemes = [] # type: List[str] + for backend in self.backends: + schemes.extend(backend.schemes) + return schemes + + def register(self, cls): + # type: (Type[VersionControl]) -> None + if not hasattr(cls, 'name'): + logger.warning('Cannot register VCS %s', cls.__name__) + return + if cls.name not in self._registry: + self._registry[cls.name] = cls() + logger.debug('Registered VCS backend: %s', cls.name) + + def unregister(self, name): + # type: (str) -> None + if name in self._registry: + del self._registry[name] + + def get_backend_for_dir(self, location): + # type: (str) -> Optional[VersionControl] + """ + Return a VersionControl object if a repository of that type is found + at the given directory. + """ + vcs_backends = {} + for vcs_backend in self._registry.values(): + repo_path = vcs_backend.get_repository_root(location) + if not repo_path: + continue + logger.debug('Determine that %s uses VCS: %s', + location, vcs_backend.name) + vcs_backends[repo_path] = vcs_backend + + if not vcs_backends: + return None + + # Choose the VCS in the inner-most directory. Since all repository + # roots found here would be either `location` or one of its + # parents, the longest path should have the most path components, + # i.e. the backend representing the inner-most repository. + inner_most_repo_path = max(vcs_backends, key=len) + return vcs_backends[inner_most_repo_path] + + def get_backend_for_scheme(self, scheme): + # type: (str) -> Optional[VersionControl] + """ + Return a VersionControl object or None. + """ + for vcs_backend in self._registry.values(): + if scheme in vcs_backend.schemes: + return vcs_backend + return None + + def get_backend(self, name): + # type: (str) -> Optional[VersionControl] + """ + Return a VersionControl object or None. + """ + name = name.lower() + return self._registry.get(name) + + +vcs = VcsSupport() + + +class VersionControl(object): + name = '' + dirname = '' + repo_name = '' + # List of supported schemes for this Version Control + schemes = () # type: Tuple[str, ...] + # Iterable of environment variable names to pass to call_subprocess(). + unset_environ = () # type: Tuple[str, ...] + default_arg_rev = None # type: Optional[str] + + @classmethod + def should_add_vcs_url_prefix(cls, remote_url): + # type: (str) -> bool + """ + Return whether the vcs prefix (e.g. "git+") should be added to a + repository's remote url when used in a requirement. + """ + return not remote_url.lower().startswith('{}:'.format(cls.name)) + + @classmethod + def get_subdirectory(cls, location): + # type: (str) -> Optional[str] + """ + Return the path to setup.py, relative to the repo root. + Return None if setup.py is in the repo root. + """ + return None + + @classmethod + def get_requirement_revision(cls, repo_dir): + # type: (str) -> str + """ + Return the revision string that should be used in a requirement. + """ + return cls.get_revision(repo_dir) + + @classmethod + def get_src_requirement(cls, repo_dir, project_name): + # type: (str, str) -> Optional[str] + """ + Return the requirement string to use to redownload the files + currently at the given repository directory. + + Args: + project_name: the (unescaped) project name. + + The return value has a form similar to the following: + + {repository_url}@{revision}#egg={project_name} + """ + repo_url = cls.get_remote_url(repo_dir) + if repo_url is None: + return None + + if cls.should_add_vcs_url_prefix(repo_url): + repo_url = '{}+{}'.format(cls.name, repo_url) + + revision = cls.get_requirement_revision(repo_dir) + subdir = cls.get_subdirectory(repo_dir) + req = make_vcs_requirement_url(repo_url, revision, project_name, + subdir=subdir) + + return req + + @staticmethod + def get_base_rev_args(rev): + # type: (str) -> List[str] + """ + Return the base revision arguments for a vcs command. + + Args: + rev: the name of a revision to install. Cannot be None. + """ + raise NotImplementedError + + def is_immutable_rev_checkout(self, url, dest): + # type: (str, str) -> bool + """ + Return true if the commit hash checked out at dest matches + the revision in url. + + Always return False, if the VCS does not support immutable commit + hashes. + + This method does not check if there are local uncommitted changes + in dest after checkout, as pip currently has no use case for that. + """ + return False + + @classmethod + def make_rev_options(cls, rev=None, extra_args=None): + # type: (Optional[str], Optional[CommandArgs]) -> RevOptions + """ + Return a RevOptions object. + + Args: + rev: the name of a revision to install. + extra_args: a list of extra options. + """ + return RevOptions(cls, rev, extra_args=extra_args) + + @classmethod + def _is_local_repository(cls, repo): + # type: (str) -> bool + """ + posix absolute paths start with os.path.sep, + win32 ones start with drive (like c:\\folder) + """ + drive, tail = os.path.splitdrive(repo) + return repo.startswith(os.path.sep) or bool(drive) + + def export(self, location, url): + # type: (str, HiddenText) -> None + """ + Export the repository at the url to the destination location + i.e. only download the files, without vcs informations + + :param url: the repository URL starting with a vcs prefix. + """ + raise NotImplementedError + + @classmethod + def get_netloc_and_auth(cls, netloc, scheme): + # type: (str, str) -> Tuple[str, Tuple[Optional[str], Optional[str]]] + """ + Parse the repository URL's netloc, and return the new netloc to use + along with auth information. + + Args: + netloc: the original repository URL netloc. + scheme: the repository URL's scheme without the vcs prefix. + + This is mainly for the Subversion class to override, so that auth + information can be provided via the --username and --password options + instead of through the URL. For other subclasses like Git without + such an option, auth information must stay in the URL. + + Returns: (netloc, (username, password)). + """ + return netloc, (None, None) + + @classmethod + def get_url_rev_and_auth(cls, url): + # type: (str) -> Tuple[str, Optional[str], AuthInfo] + """ + Parse the repository URL to use, and return the URL, revision, + and auth info to use. + + Returns: (url, rev, (username, password)). + """ + scheme, netloc, path, query, frag = urllib_parse.urlsplit(url) + if '+' not in scheme: + raise ValueError( + "Sorry, {!r} is a malformed VCS url. " + "The format is +://, " + "e.g. svn+http://myrepo/svn/MyApp#egg=MyApp".format(url) + ) + # Remove the vcs prefix. + scheme = scheme.split('+', 1)[1] + netloc, user_pass = cls.get_netloc_and_auth(netloc, scheme) + rev = None + if '@' in path: + path, rev = path.rsplit('@', 1) + if not rev: + raise InstallationError( + "The URL {!r} has an empty revision (after @) " + "which is not supported. Include a revision after @ " + "or remove @ from the URL.".format(url) + ) + url = urllib_parse.urlunsplit((scheme, netloc, path, query, '')) + return url, rev, user_pass + + @staticmethod + def make_rev_args(username, password): + # type: (Optional[str], Optional[HiddenText]) -> CommandArgs + """ + Return the RevOptions "extra arguments" to use in obtain(). + """ + return [] + + def get_url_rev_options(self, url): + # type: (HiddenText) -> Tuple[HiddenText, RevOptions] + """ + Return the URL and RevOptions object to use in obtain() and in + some cases export(), as a tuple (url, rev_options). + """ + secret_url, rev, user_pass = self.get_url_rev_and_auth(url.secret) + username, secret_password = user_pass + password = None # type: Optional[HiddenText] + if secret_password is not None: + password = hide_value(secret_password) + extra_args = self.make_rev_args(username, password) + rev_options = self.make_rev_options(rev, extra_args=extra_args) + + return hide_url(secret_url), rev_options + + @staticmethod + def normalize_url(url): + # type: (str) -> str + """ + Normalize a URL for comparison by unquoting it and removing any + trailing slash. + """ + return urllib_parse.unquote(url).rstrip('/') + + @classmethod + def compare_urls(cls, url1, url2): + # type: (str, str) -> bool + """ + Compare two repo URLs for identity, ignoring incidental differences. + """ + return (cls.normalize_url(url1) == cls.normalize_url(url2)) + + def fetch_new(self, dest, url, rev_options): + # type: (str, HiddenText, RevOptions) -> None + """ + Fetch a revision from a repository, in the case that this is the + first fetch from the repository. + + Args: + dest: the directory to fetch the repository to. + rev_options: a RevOptions object. + """ + raise NotImplementedError + + def switch(self, dest, url, rev_options): + # type: (str, HiddenText, RevOptions) -> None + """ + Switch the repo at ``dest`` to point to ``URL``. + + Args: + rev_options: a RevOptions object. + """ + raise NotImplementedError + + def update(self, dest, url, rev_options): + # type: (str, HiddenText, RevOptions) -> None + """ + Update an already-existing repo to the given ``rev_options``. + + Args: + rev_options: a RevOptions object. + """ + raise NotImplementedError + + @classmethod + def is_commit_id_equal(cls, dest, name): + # type: (str, Optional[str]) -> bool + """ + Return whether the id of the current commit equals the given name. + + Args: + dest: the repository directory. + name: a string name. + """ + raise NotImplementedError + + def obtain(self, dest, url): + # type: (str, HiddenText) -> None + """ + Install or update in editable mode the package represented by this + VersionControl object. + + :param dest: the repository directory in which to install or update. + :param url: the repository URL starting with a vcs prefix. + """ + url, rev_options = self.get_url_rev_options(url) + + if not os.path.exists(dest): + self.fetch_new(dest, url, rev_options) + return + + rev_display = rev_options.to_display() + if self.is_repository_directory(dest): + existing_url = self.get_remote_url(dest) + if self.compare_urls(existing_url, url.secret): + logger.debug( + '%s in %s exists, and has correct URL (%s)', + self.repo_name.title(), + display_path(dest), + url, + ) + if not self.is_commit_id_equal(dest, rev_options.rev): + logger.info( + 'Updating %s %s%s', + display_path(dest), + self.repo_name, + rev_display, + ) + self.update(dest, url, rev_options) + else: + logger.info('Skipping because already up-to-date.') + return + + logger.warning( + '%s %s in %s exists with URL %s', + self.name, + self.repo_name, + display_path(dest), + existing_url, + ) + prompt = ('(s)witch, (i)gnore, (w)ipe, (b)ackup ', + ('s', 'i', 'w', 'b')) + else: + logger.warning( + 'Directory %s already exists, and is not a %s %s.', + dest, + self.name, + self.repo_name, + ) + # https://github.com/python/mypy/issues/1174 + prompt = ('(i)gnore, (w)ipe, (b)ackup ', # type: ignore + ('i', 'w', 'b')) + + logger.warning( + 'The plan is to install the %s repository %s', + self.name, + url, + ) + response = ask_path_exists('What to do? {}'.format( + prompt[0]), prompt[1]) + + if response == 'a': + sys.exit(-1) + + if response == 'w': + logger.warning('Deleting %s', display_path(dest)) + rmtree(dest) + self.fetch_new(dest, url, rev_options) + return + + if response == 'b': + dest_dir = backup_dir(dest) + logger.warning( + 'Backing up %s to %s', display_path(dest), dest_dir, + ) + shutil.move(dest, dest_dir) + self.fetch_new(dest, url, rev_options) + return + + # Do nothing if the response is "i". + if response == 's': + logger.info( + 'Switching %s %s to %s%s', + self.repo_name, + display_path(dest), + url, + rev_display, + ) + self.switch(dest, url, rev_options) + + def unpack(self, location, url): + # type: (str, HiddenText) -> None + """ + Clean up current location and download the url repository + (and vcs infos) into location + + :param url: the repository URL starting with a vcs prefix. + """ + if os.path.exists(location): + rmtree(location) + self.obtain(location, url=url) + + @classmethod + def get_remote_url(cls, location): + # type: (str) -> str + """ + Return the url used at location + + Raises RemoteNotFoundError if the repository does not have a remote + url configured. + """ + raise NotImplementedError + + @classmethod + def get_revision(cls, location): + # type: (str) -> str + """ + Return the current commit id of the files at the given location. + """ + raise NotImplementedError + + @classmethod + def run_command( + cls, + cmd, # type: Union[List[str], CommandArgs] + show_stdout=True, # type: bool + cwd=None, # type: Optional[str] + on_returncode='raise', # type: str + extra_ok_returncodes=None, # type: Optional[Iterable[int]] + command_desc=None, # type: Optional[str] + extra_environ=None, # type: Optional[Mapping[str, Any]] + spinner=None, # type: Optional[SpinnerInterface] + log_failed_cmd=True, # type: bool + stdout_only=False, # type: bool + ): + # type: (...) -> Text + """ + Run a VCS subcommand + This is simply a wrapper around call_subprocess that adds the VCS + command name, and checks that the VCS is available + """ + cmd = make_command(cls.name, *cmd) + try: + return call_subprocess(cmd, show_stdout, cwd, + on_returncode=on_returncode, + extra_ok_returncodes=extra_ok_returncodes, + command_desc=command_desc, + extra_environ=extra_environ, + unset_environ=cls.unset_environ, + spinner=spinner, + log_failed_cmd=log_failed_cmd, + stdout_only=stdout_only) + except OSError as e: + # errno.ENOENT = no such file or directory + # In other words, the VCS executable isn't available + if e.errno == errno.ENOENT: + raise BadCommand( + 'Cannot find command {cls.name!r} - do you have ' + '{cls.name!r} installed and in your ' + 'PATH?'.format(**locals())) + else: + raise # re-raise exception if a different error occurred + + @classmethod + def is_repository_directory(cls, path): + # type: (str) -> bool + """ + Return whether a directory path is a repository directory. + """ + logger.debug('Checking in %s for %s (%s)...', + path, cls.dirname, cls.name) + return os.path.exists(os.path.join(path, cls.dirname)) + + @classmethod + def get_repository_root(cls, location): + # type: (str) -> Optional[str] + """ + Return the "root" (top-level) directory controlled by the vcs, + or `None` if the directory is not in any. + + It is meant to be overridden to implement smarter detection + mechanisms for specific vcs. + + This can do more than is_repository_directory() alone. For + example, the Git override checks that Git is actually available. + """ + if cls.is_repository_directory(location): + return location + return None diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/wheel_builder.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/wheel_builder.py new file mode 100644 index 0000000000..f7e15afbca --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_internal/wheel_builder.py @@ -0,0 +1,363 @@ +"""Orchestrator for building wheels from InstallRequirements. +""" + +import logging +import os.path +import re +import shutil +import zipfile + +from pip._vendor.packaging.utils import canonicalize_name, canonicalize_version +from pip._vendor.packaging.version import InvalidVersion, Version +from pip._vendor.packaging.version import parse as parse_version +from pip._vendor.pkg_resources import Distribution + +from pip._internal.exceptions import InvalidWheelFilename, UnsupportedWheel +from pip._internal.models.link import Link +from pip._internal.models.wheel import Wheel +from pip._internal.operations.build.wheel import build_wheel_pep517 +from pip._internal.operations.build.wheel_legacy import build_wheel_legacy +from pip._internal.utils.logging import indent_log +from pip._internal.utils.misc import ensure_dir, hash_file, is_wheel_installed +from pip._internal.utils.setuptools_build import make_setuptools_clean_args +from pip._internal.utils.subprocess import call_subprocess +from pip._internal.utils.temp_dir import TempDirectory +from pip._internal.utils.typing import MYPY_CHECK_RUNNING +from pip._internal.utils.urls import path_to_url +from pip._internal.utils.wheel import pkg_resources_distribution_for_wheel +from pip._internal.vcs import vcs + +if MYPY_CHECK_RUNNING: + from typing import Any, Callable, Iterable, List, Optional, Tuple + + from pip._internal.cache import WheelCache + from pip._internal.req.req_install import InstallRequirement + + BinaryAllowedPredicate = Callable[[InstallRequirement], bool] + BuildResult = Tuple[List[InstallRequirement], List[InstallRequirement]] + +logger = logging.getLogger(__name__) + +_egg_info_re = re.compile(r'([a-z0-9_.]+)-([a-z0-9_.!+-]+)', re.IGNORECASE) + + +def _contains_egg_info(s): + # type: (str) -> bool + """Determine whether the string looks like an egg_info. + + :param s: The string to parse. E.g. foo-2.1 + """ + return bool(_egg_info_re.search(s)) + + +def _should_build( + req, # type: InstallRequirement + need_wheel, # type: bool + check_binary_allowed, # type: BinaryAllowedPredicate +): + # type: (...) -> bool + """Return whether an InstallRequirement should be built into a wheel.""" + if req.constraint: + # never build requirements that are merely constraints + return False + if req.is_wheel: + if need_wheel: + logger.info( + 'Skipping %s, due to already being wheel.', req.name, + ) + return False + + if need_wheel: + # i.e. pip wheel, not pip install + return True + + # From this point, this concerns the pip install command only + # (need_wheel=False). + + if req.editable or not req.source_dir: + return False + + if not check_binary_allowed(req): + logger.info( + "Skipping wheel build for %s, due to binaries " + "being disabled for it.", req.name, + ) + return False + + if not req.use_pep517 and not is_wheel_installed(): + # we don't build legacy requirements if wheel is not installed + logger.info( + "Using legacy 'setup.py install' for %s, " + "since package 'wheel' is not installed.", req.name, + ) + return False + + return True + + +def should_build_for_wheel_command( + req, # type: InstallRequirement +): + # type: (...) -> bool + return _should_build( + req, need_wheel=True, check_binary_allowed=_always_true + ) + + +def should_build_for_install_command( + req, # type: InstallRequirement + check_binary_allowed, # type: BinaryAllowedPredicate +): + # type: (...) -> bool + return _should_build( + req, need_wheel=False, check_binary_allowed=check_binary_allowed + ) + + +def _should_cache( + req, # type: InstallRequirement +): + # type: (...) -> Optional[bool] + """ + Return whether a built InstallRequirement can be stored in the persistent + wheel cache, assuming the wheel cache is available, and _should_build() + has determined a wheel needs to be built. + """ + if req.editable or not req.source_dir: + # never cache editable requirements + return False + + if req.link and req.link.is_vcs: + # VCS checkout. Do not cache + # unless it points to an immutable commit hash. + assert not req.editable + assert req.source_dir + vcs_backend = vcs.get_backend_for_scheme(req.link.scheme) + assert vcs_backend + if vcs_backend.is_immutable_rev_checkout(req.link.url, req.source_dir): + return True + return False + + assert req.link + base, ext = req.link.splitext() + if _contains_egg_info(base): + return True + + # Otherwise, do not cache. + return False + + +def _get_cache_dir( + req, # type: InstallRequirement + wheel_cache, # type: WheelCache +): + # type: (...) -> str + """Return the persistent or temporary cache directory where the built + wheel need to be stored. + """ + cache_available = bool(wheel_cache.cache_dir) + assert req.link + if cache_available and _should_cache(req): + cache_dir = wheel_cache.get_path_for_link(req.link) + else: + cache_dir = wheel_cache.get_ephem_path_for_link(req.link) + return cache_dir + + +def _always_true(_): + # type: (Any) -> bool + return True + + +def _get_metadata_version(dist): + # type: (Distribution) -> Optional[Version] + for line in dist.get_metadata_lines(dist.PKG_INFO): + if line.lower().startswith("metadata-version:"): + value = line.split(":", 1)[-1].strip() + try: + return Version(value) + except InvalidVersion: + msg = "Invalid Metadata-Version: {}".format(value) + raise UnsupportedWheel(msg) + raise UnsupportedWheel("Missing Metadata-Version") + + +def _verify_one(req, wheel_path): + # type: (InstallRequirement, str) -> None + canonical_name = canonicalize_name(req.name) + w = Wheel(os.path.basename(wheel_path)) + if canonicalize_name(w.name) != canonical_name: + raise InvalidWheelFilename( + "Wheel has unexpected file name: expected {!r}, " + "got {!r}".format(canonical_name, w.name), + ) + with zipfile.ZipFile(wheel_path, allowZip64=True) as zf: + dist = pkg_resources_distribution_for_wheel( + zf, canonical_name, wheel_path, + ) + if canonicalize_version(dist.version) != canonicalize_version(w.version): + raise InvalidWheelFilename( + "Wheel has unexpected file name: expected {!r}, " + "got {!r}".format(dist.version, w.version), + ) + if (_get_metadata_version(dist) >= Version("1.2") + and not isinstance(parse_version(dist.version), Version)): + raise UnsupportedWheel( + "Metadata 1.2 mandates PEP 440 version, " + "but {!r} is not".format(dist.version) + ) + + +def _build_one( + req, # type: InstallRequirement + output_dir, # type: str + verify, # type: bool + build_options, # type: List[str] + global_options, # type: List[str] +): + # type: (...) -> Optional[str] + """Build one wheel. + + :return: The filename of the built wheel, or None if the build failed. + """ + try: + ensure_dir(output_dir) + except OSError as e: + logger.warning( + "Building wheel for %s failed: %s", + req.name, e, + ) + return None + + # Install build deps into temporary directory (PEP 518) + with req.build_env: + wheel_path = _build_one_inside_env( + req, output_dir, build_options, global_options + ) + if wheel_path and verify: + try: + _verify_one(req, wheel_path) + except (InvalidWheelFilename, UnsupportedWheel) as e: + logger.warning("Built wheel for %s is invalid: %s", req.name, e) + return None + return wheel_path + + +def _build_one_inside_env( + req, # type: InstallRequirement + output_dir, # type: str + build_options, # type: List[str] + global_options, # type: List[str] +): + # type: (...) -> Optional[str] + with TempDirectory(kind="wheel") as temp_dir: + assert req.name + if req.use_pep517: + assert req.metadata_directory + wheel_path = build_wheel_pep517( + name=req.name, + backend=req.pep517_backend, + metadata_directory=req.metadata_directory, + build_options=build_options, + tempd=temp_dir.path, + ) + else: + wheel_path = build_wheel_legacy( + name=req.name, + setup_py_path=req.setup_py_path, + source_dir=req.unpacked_source_directory, + global_options=global_options, + build_options=build_options, + tempd=temp_dir.path, + ) + + if wheel_path is not None: + wheel_name = os.path.basename(wheel_path) + dest_path = os.path.join(output_dir, wheel_name) + try: + wheel_hash, length = hash_file(wheel_path) + shutil.move(wheel_path, dest_path) + logger.info('Created wheel for %s: ' + 'filename=%s size=%d sha256=%s', + req.name, wheel_name, length, + wheel_hash.hexdigest()) + logger.info('Stored in directory: %s', output_dir) + return dest_path + except Exception as e: + logger.warning( + "Building wheel for %s failed: %s", + req.name, e, + ) + # Ignore return, we can't do anything else useful. + if not req.use_pep517: + _clean_one_legacy(req, global_options) + return None + + +def _clean_one_legacy(req, global_options): + # type: (InstallRequirement, List[str]) -> bool + clean_args = make_setuptools_clean_args( + req.setup_py_path, + global_options=global_options, + ) + + logger.info('Running setup.py clean for %s', req.name) + try: + call_subprocess(clean_args, cwd=req.source_dir) + return True + except Exception: + logger.error('Failed cleaning build dir for %s', req.name) + return False + + +def build( + requirements, # type: Iterable[InstallRequirement] + wheel_cache, # type: WheelCache + verify, # type: bool + build_options, # type: List[str] + global_options, # type: List[str] +): + # type: (...) -> BuildResult + """Build wheels. + + :return: The list of InstallRequirement that succeeded to build and + the list of InstallRequirement that failed to build. + """ + if not requirements: + return [], [] + + # Build the wheels. + logger.info( + 'Building wheels for collected packages: %s', + ', '.join(req.name for req in requirements), # type: ignore + ) + + with indent_log(): + build_successes, build_failures = [], [] + for req in requirements: + cache_dir = _get_cache_dir(req, wheel_cache) + wheel_file = _build_one( + req, cache_dir, verify, build_options, global_options + ) + if wheel_file: + # Update the link for this. + req.link = Link(path_to_url(wheel_file)) + req.local_file_path = req.link.file_path + assert req.link.is_wheel + build_successes.append(req) + else: + build_failures.append(req) + + # notify success/failure + if build_successes: + logger.info( + 'Successfully built %s', + ' '.join([req.name for req in build_successes]), # type: ignore + ) + if build_failures: + logger.info( + 'Failed to build %s', + ' '.join([req.name for req in build_failures]), # type: ignore + ) + # Return a list of requirements that failed to build + return build_successes, build_failures diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_vendor/__init__.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_vendor/__init__.py new file mode 100644 index 0000000000..d4e20fedd9 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_vendor/__init__.py @@ -0,0 +1,123 @@ +""" +pip._vendor is for vendoring dependencies of pip to prevent needing pip to +depend on something external. + +Files inside of pip._vendor should be considered immutable and should only be +updated to versions from upstream. +""" +from __future__ import absolute_import + +import glob +import os.path +import sys + +# Downstream redistributors which have debundled our dependencies should also +# patch this value to be true. This will trigger the additional patching +# to cause things like "six" to be available as pip. +DEBUNDLED = True + +# By default, look in this directory for a bunch of .whl files which we will +# add to the beginning of sys.path before attempting to import anything. This +# is done to support downstream re-distributors like Debian and Fedora who +# wish to create their own Wheels for our dependencies to aid in debundling. +prefix = getattr(sys, "base_prefix", sys.prefix) +if prefix.startswith('/usr/lib/pypy'): + prefix = '/usr' +WHEEL_DIR = os.path.abspath(os.path.join(prefix, 'share', 'python-wheels')) + + +# Define a small helper function to alias our vendored modules to the real ones +# if the vendored ones do not exist. This idea of this was taken from +# https://github.com/kennethreitz/requests/pull/2567. +def vendored(modulename): + vendored_name = "{0}.{1}".format(__name__, modulename) + + try: + __import__(modulename, globals(), locals(), level=0) + except ImportError: + # We can just silently allow import failures to pass here. If we + # got to this point it means that ``import pip._vendor.whatever`` + # failed and so did ``import whatever``. Since we're importing this + # upfront in an attempt to alias imports, not erroring here will + # just mean we get a regular import error whenever pip *actually* + # tries to import one of these modules to use it, which actually + # gives us a better error message than we would have otherwise + # gotten. + pass + else: + sys.modules[vendored_name] = sys.modules[modulename] + base, head = vendored_name.rsplit(".", 1) + setattr(sys.modules[base], head, sys.modules[modulename]) + + +# If we're operating in a debundled setup, then we want to go ahead and trigger +# the aliasing of our vendored libraries as well as looking for wheels to add +# to our sys.path. This will cause all of this code to be a no-op typically +# however downstream redistributors can enable it in a consistent way across +# all platforms. +if DEBUNDLED: + # Actually look inside of WHEEL_DIR to find .whl files and add them to the + # front of our sys.path. + sys.path[:] = [fn for fn in glob.iglob(os.path.join(WHEEL_DIR, '*.whl')) + if not (os.path.basename(fn).startswith('wheel') or + os.path.basename(fn).startswith('pip'))] + sys.path + + # Actually alias all of our vendored dependencies. + vendored("appdirs") + vendored("cachecontrol") + vendored("certifi") + vendored("colorama") + vendored("contextlib2") + vendored("distlib") + vendored("distro") + vendored("html5lib") + vendored("six") + vendored("six.moves") + vendored("six.moves.urllib") + vendored("six.moves.urllib.parse") + vendored("packaging") + vendored("packaging.version") + vendored("packaging.specifiers") + vendored("pep517") + vendored("pkg_resources") + vendored("progress") + vendored("retrying") + vendored("requests") + vendored("requests.exceptions") + vendored("requests.packages") + vendored("requests.packages.urllib3") + vendored("requests.packages.urllib3._collections") + vendored("requests.packages.urllib3.connection") + vendored("requests.packages.urllib3.connectionpool") + vendored("requests.packages.urllib3.contrib") + vendored("requests.packages.urllib3.contrib.ntlmpool") + vendored("requests.packages.urllib3.contrib.pyopenssl") + vendored("requests.packages.urllib3.exceptions") + vendored("requests.packages.urllib3.fields") + vendored("requests.packages.urllib3.filepost") + vendored("requests.packages.urllib3.packages") + try: + vendored("requests.packages.urllib3.packages.ordered_dict") + vendored("requests.packages.urllib3.packages.six") + except ImportError: + # Debian already unbundles these from requests. + pass + vendored("requests.packages.urllib3.packages.ssl_match_hostname") + vendored("requests.packages.urllib3.packages.ssl_match_hostname." + "_implementation") + vendored("requests.packages.urllib3.poolmanager") + vendored("requests.packages.urllib3.request") + vendored("requests.packages.urllib3.response") + vendored("requests.packages.urllib3.util") + vendored("requests.packages.urllib3.util.connection") + vendored("requests.packages.urllib3.util.request") + vendored("requests.packages.urllib3.util.response") + vendored("requests.packages.urllib3.util.retry") + vendored("requests.packages.urllib3.util.ssl_") + vendored("requests.packages.urllib3.util.timeout") + vendored("requests.packages.urllib3.util.url") + vendored("resolvelib") + vendored("toml") + vendored("toml.encoder") + vendored("toml.decoder") + vendored("urllib3") diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_vendor/vendor.txt b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_vendor/vendor.txt new file mode 100644 index 0000000000..712fb77d46 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pip/_vendor/vendor.txt @@ -0,0 +1,24 @@ +appdirs==1.4.4 +CacheControl==0.12.6 +colorama==0.4.4 +contextlib2==0.6.0.post1 +distlib==0.3.1 +distro==1.5.0 +html5lib==1.1 +ipaddress==1.0.23 # Only needed on 2.6 and 2.7 +msgpack==1.0.0 +packaging==20.8 +pep517==0.9.1 +progress==1.5 +pyparsing==2.4.7 +requests==2.25.0 + certifi==2020.11.08 + chardet==3.0.4 + idna==2.10 + urllib3==1.26.2 +resolvelib==0.5.4 +retrying==1.3.3 +setuptools==44.0.0 +six==1.15.0 +toml==0.10.2 +webencodings==0.5.1 diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/AUTHORS.txt b/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/AUTHORS.txt new file mode 100644 index 0000000000..0360f988f2 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/AUTHORS.txt @@ -0,0 +1,590 @@ +@Switch01 +A_Rog +Aakanksha Agrawal +Abhinav Sagar +ABHYUDAY PRATAP SINGH +abs51295 +AceGentile +Adam Chainz +Adam Tse +Adam Wentz +admin +Adrien Morison +ahayrapetyan +Ahilya +AinsworthK +Akash Srivastava +Alan Yee +Albert Tugushev +Albert-Guan +albertg +Aleks Bunin +Alethea Flowers +Alex Gaynor +Alex Grönholm +Alex Loosley +Alex Morega +Alex Stachowiak +Alexander Shtyrov +Alexandre Conrad +Alexey Popravka +Alli +Ami Fischman +Ananya Maiti +Anatoly Techtonik +Anders Kaseorg +Andre Aguiar +Andreas Lutro +Andrei Geacar +Andrew Gaul +Andrey Bulgakov +Andrés Delfino +Andy Freeland +Andy Kluger +Ani Hayrapetyan +Aniruddha Basak +Anish Tambe +Anrs Hu +Anthony Sottile +Antoine Musso +Anton Ovchinnikov +Anton Patrushev +Antonio Alvarado Hernandez +Antony Lee +Antti Kaihola +Anubhav Patel +Anudit Nagar +Anuj Godase +AQNOUCH Mohammed +AraHaan +Arindam Choudhury +Armin Ronacher +Artem +Ashley Manton +Ashwin Ramaswami +atse +Atsushi Odagiri +Avinash Karhana +Avner Cohen +Baptiste Mispelon +Barney Gale +barneygale +Bartek Ogryczak +Bastian Venthur +Ben Darnell +Ben Hoyt +Ben Rosser +Bence Nagy +Benjamin Peterson +Benjamin VanEvery +Benoit Pierre +Berker Peksag +Bernard +Bernard Tyers +Bernardo B. Marques +Bernhard M. Wiedemann +Bertil Hatt +Bhavam Vidyarthi +Bogdan Opanchuk +BorisZZZ +Brad Erickson +Bradley Ayers +Brandon L. Reiss +Brandt Bucher +Brett Randall +Brian Cristante +Brian Rosner +BrownTruck +Bruno Oliveira +Bruno Renié +Bstrdsmkr +Buck Golemon +burrows +Bussonnier Matthias +c22 +Caleb Martinez +Calvin Smith +Carl Meyer +Carlos Liam +Carol Willing +Carter Thayer +Cass +Chandrasekhar Atina +Chih-Hsuan Yen +Chris Brinker +Chris Hunt +Chris Jerdonek +Chris McDonough +Chris Wolfe +Christian Clauss +Christian Heimes +Christian Oudard +Christoph Reiter +Christopher Hunt +Christopher Snyder +cjc7373 +Clark Boylan +Clay McClure +Cody +Cody Soyland +Colin Watson +Connor Osborn +Cooper Lees +Cooper Ry Lees +Cory Benfield +Cory Wright +Craig Kerstiens +Cristian Sorinel +Cristina +Cristina Muñoz +Curtis Doty +cytolentino +Damian Quiroga +Dan Black +Dan Savilonis +Dan Sully +daniel +Daniel Collins +Daniel Hahler +Daniel Holth +Daniel Jost +Daniel Katz +Daniel Shaulov +Daniele Esposti +Daniele Procida +Danny Hermes +Danny McClanahan +Dav Clark +Dave Abrahams +Dave Jones +David Aguilar +David Black +David Bordeynik +David Caro +David Evans +David Linke +David Poggi +David Pursehouse +David Tucker +David Wales +Davidovich +Deepak Sharma +derwolfe +Desetude +Devesh Kumar Singh +Diego Caraballo +DiegoCaraballo +Dmitry Gladkov +Domen Kožar +Donald Stufft +Dongweiming +Douglas Thor +DrFeathers +Dustin Ingram +Dwayne Bailey +Ed Morley +Eitan Adler +ekristina +elainechan +Eli Schwartz +Elisha Hollander +Ellen Marie Dash +Emil Burzo +Emil Styrke +Emmanuel Arias +Endoh Takanao +enoch +Erdinc Mutlu +Eric Gillingham +Eric Hanchrow +Eric Hopper +Erik M. Bray +Erik Rose +Ernest W Durbin III +Ernest W. Durbin III +Erwin Janssen +Eugene Vereshchagin +everdimension +Felix Yan +fiber-space +Filip Kokosiński +Filipe Laíns +Florian Briand +Florian Rathgeber +Francesco +Francesco Montesano +Frost Ming +Gabriel Curio +Gabriel de Perthuis +Garry Polley +gdanielson +Geoffrey Sneddon +George Song +Georgi Valkov +ghost +Giftlin Rajaiah +gizmoguy1 +gkdoc +Gopinath M +GOTO Hayato +gpiks +Greg Ward +Guilherme Espada +gutsytechster +Guy Rozendorn +gzpan123 +Hanjun Kim +Hari Charan +Harsh Vardhan +Herbert Pfennig +Hsiaoming Yang +Hugo +Hugo Lopes Tavares +Hugo van Kemenade +hugovk +Hynek Schlawack +Ian Bicking +Ian Cordasco +Ian Lee +Ian Stapleton Cordasco +Ian Wienand +Igor Kuzmitshov +Igor Sobreira +Ilan Schnell +Ilya Baryshev +INADA Naoki +Ionel Cristian Mărieș +Ionel Maries Cristian +Ivan Pozdeev +Jacob Kim +jakirkham +Jakub Stasiak +Jakub Vysoky +Jakub Wilk +James Cleveland +James Firth +James Polley +Jan Pokorný +Jannis Leidel +jarondl +Jason R. Coombs +Jay Graves +Jean-Christophe Fillion-Robin +Jeff Barber +Jeff Dairiki +Jelmer Vernooij +jenix21 +Jeremy Stanley +Jeremy Zafran +Jiashuo Li +Jim Garrison +Jivan Amara +John Paton +John T. Wodder II +John-Scott Atlakson +johnthagen +Jon Banafato +Jon Dufresne +Jon Parise +Jonas Nockert +Jonathan Herbert +Joost Molenaar +Jorge Niedbalski +Joseph Long +Josh Bronson +Josh Hansen +Josh Schneier +Juanjo Bazán +Julian Berman +Julian Gethmann +Julien Demoor +Jussi Kukkonen +jwg4 +Jyrki Pulliainen +Kai Chen +Kamal Bin Mustafa +kaustav haldar +keanemind +Keith Maxwell +Kelsey Hightower +Kenneth Belitzky +Kenneth Reitz +Kevin Burke +Kevin Carter +Kevin Frommelt +Kevin R Patterson +Kexuan Sun +Kit Randel +KOLANICH +kpinc +Krishna Oza +Kumar McMillan +Kyle Persohn +lakshmanaram +Laszlo Kiss-Kollar +Laurent Bristiel +Laurie O +Laurie Opperman +Leon Sasson +Lev Givon +Lincoln de Sousa +Lipis +Loren Carvalho +Lucas Cimon +Ludovic Gasc +Luke Macken +Luo Jiebin +luojiebin +luz.paz +László Kiss Kollár +Marc Abramowitz +Marc Tamlyn +Marcus Smith +Mariatta +Mark Kohler +Mark Williams +Markus Hametner +Masaki +Masklinn +Matej Stuchlik +Mathew Jennings +Mathieu Bridon +Matt Good +Matt Maker +Matt Robenolt +matthew +Matthew Einhorn +Matthew Gilliard +Matthew Iversen +Matthew Trumbell +Matthew Willson +Matthias Bussonnier +mattip +Maxim Kurnikov +Maxime Rouyrre +mayeut +mbaluna +mdebi +memoselyk +Michael +Michael Aquilina +Michael E. Karpeles +Michael Klich +Michael Williamson +michaelpacer +Mickaël Schoentgen +Miguel Araujo Perez +Mihir Singh +Mike +Mike Hendricks +Min RK +MinRK +Miro Hrončok +Monica Baluna +montefra +Monty Taylor +Nate Coraor +Nathaniel J. Smith +Nehal J Wani +Neil Botelho +Nguyễn Gia Phong +Nick Coghlan +Nick Stenning +Nick Timkovich +Nicolas Bock +Nicole Harris +Nikhil Benesch +Nikolay Korolev +Nitesh Sharma +Noah +Noah Gorny +Nowell Strite +NtaleGrey +nvdv +Ofekmeister +ofrinevo +Oliver Jeeves +Oliver Mannion +Oliver Tonnhofer +Olivier Girardot +Olivier Grisel +Ollie Rutherfurd +OMOTO Kenji +Omry Yadan +onlinejudge95 +Oren Held +Oscar Benjamin +Oz N Tiram +Pachwenko +Patrick Dubroy +Patrick Jenkins +Patrick Lawson +patricktokeeffe +Patrik Kopkan +Paul Kehrer +Paul Moore +Paul Nasrat +Paul Oswald +Paul van der Linden +Paulus Schoutsen +Pavithra Eswaramoorthy +Pawel Jasinski +Pekka Klärck +Peter Lisák +Peter Waller +petr-tik +Phaneendra Chiruvella +Phil Elson +Phil Freo +Phil Pennock +Phil Whelan +Philip Jägenstedt +Philip Molloy +Philippe Ombredanne +Pi Delport +Pierre-Yves Rofes +pip +Prabakaran Kumaresshan +Prabhjyotsing Surjit Singh Sodhi +Prabhu Marappan +Pradyun Gedam +Prashant Sharma +Pratik Mallya +Preet Thakkar +Preston Holmes +Przemek Wrzos +Pulkit Goyal +Qiangning Hong +Quentin Pradet +R. David Murray +Rafael Caricio +Ralf Schmitt +Razzi Abuissa +rdb +Reece Dunham +Remi Rampin +Rene Dudfield +Riccardo Magliocchetti +Richard Jones +Ricky Ng-Adam +RobberPhex +Robert Collins +Robert McGibbon +Robert T. McGibbon +robin elisha robinson +Roey Berman +Rohan Jain +Roman Bogorodskiy +Romuald Brunet +Ronny Pfannschmidt +Rory McCann +Ross Brattain +Roy Wellington Ⅳ +Ruairidh MacLeod +Ryan Wooden +ryneeverett +Sachi King +Salvatore Rinchiera +Savio Jomton +schlamar +Scott Kitterman +Sean +seanj +Sebastian Jordan +Sebastian Schaetz +Segev Finer +SeongSoo Cho +Sergey Vasilyev +Seth Woodworth +shireenrao +Shlomi Fish +Shovan Maity +Simeon Visser +Simon Cross +Simon Pichugin +sinoroc +sinscary +socketubs +Sorin Sbarnea +Srinivas Nyayapati +Stavros Korokithakis +Stefan Scherfke +Stefano Rivera +Stephan Erb +stepshal +Steve (Gadget) Barnes +Steve Barnes +Steve Dower +Steve Kowalik +Steven Myint +stonebig +Stéphane Bidoul +Stéphane Bidoul (ACSONE) +Stéphane Klein +Sumana Harihareswara +Surbhi Sharma +Sviatoslav Sydorenko +Swat009 +Takayuki SHIMIZUKAWA +tbeswick +Thijs Triemstra +Thomas Fenzl +Thomas Grainger +Thomas Guettler +Thomas Johansson +Thomas Kluyver +Thomas Smith +Tim D. Smith +Tim Gates +Tim Harder +Tim Heap +tim smith +tinruufu +Tom Forbes +Tom Freudenheim +Tom V +Tomas Hrnciar +Tomas Orsava +Tomer Chachamu +Tony Beswick +Tony Zhaocheng Tan +TonyBeswick +toonarmycaptain +Toshio Kuratomi +toxinu +Travis Swicegood +Tzu-ping Chung +Valentin Haenel +Victor Stinner +victorvpaulo +Vikram - Google +Viktor Szépe +Ville Skyttä +Vinay Sajip +Vincent Philippon +Vinicyus Macedo +Vipul Kumar +Vitaly Babiy +Vladimir Rutsky +W. Trevor King +Wil Tan +Wilfred Hughes +William ML Leslie +William T Olson +Wilson Mo +wim glenn +Wolfgang Maier +Xavier Fernandez +xoviat +xtreak +YAMAMOTO Takashi +Yen Chi Hsuan +Yeray Diaz Diaz +Yoval P +Yu Jian +Yuan Jing Vincent Yan +Zearin +Zhiping Deng +Zvezdan Petkovic +Łukasz Langa +Семён Марьясин diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/INSTALLER b/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/INSTALLER new file mode 100644 index 0000000000..a1b589e38a --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/LICENSE.txt b/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/LICENSE.txt new file mode 100644 index 0000000000..75eb0fd80b --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/LICENSE.txt @@ -0,0 +1,20 @@ +Copyright (c) 2008-2020 The pip developers (see AUTHORS.txt file) + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/METADATA b/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/METADATA new file mode 100644 index 0000000000..cf6c9302c5 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/METADATA @@ -0,0 +1,13 @@ +Metadata-Version: 2.1 +Name: pkg_resources +Version: 0.0.0 +Summary: UNKNOWN +Home-page: UNKNOWN +Author: UNKNOWN +Author-email: UNKNOWN +License: UNKNOWN +Platform: UNKNOWN + +UNKNOWN + + diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/RECORD b/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/RECORD new file mode 100644 index 0000000000..2cc7da9cdb --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/RECORD @@ -0,0 +1,39 @@ +pkg_resources-0.0.0.dist-info/AUTHORS.txt,sha256=ilkpJ4nuW3rRgU3fX4EufclaM4Y7RsZu5uOu0oizmNM,8036 +pkg_resources-0.0.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +pkg_resources-0.0.0.dist-info/LICENSE.txt,sha256=gdAS_gPyTUkBTvvgoNNlG9Mv1KFDTig6W1JdeMD2Efg,1090 +pkg_resources-0.0.0.dist-info/METADATA,sha256=V9_WPOtD1FnuKrTGv6Ique7kAOn2lasvT8W0_iMCCCk,177 +pkg_resources-0.0.0.dist-info/RECORD,, +pkg_resources-0.0.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pkg_resources-0.0.0.dist-info/WHEEL,sha256=kGT74LWyRUZrL4VgLh6_g12IeVl_9u9ZVhadrgXZUEY,110 +pkg_resources/__init__.py,sha256=0IssxXPnaDKpYZRra8Ime0JG4hwosQljItGD0bnIkGk,108349 +pkg_resources/__pycache__/__init__.cpython-39.pyc,, +pkg_resources/__pycache__/py31compat.cpython-39.pyc,, +pkg_resources/_vendor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pkg_resources/_vendor/__pycache__/__init__.cpython-39.pyc,, +pkg_resources/_vendor/__pycache__/appdirs.cpython-39.pyc,, +pkg_resources/_vendor/__pycache__/pyparsing.cpython-39.pyc,, +pkg_resources/_vendor/__pycache__/six.cpython-39.pyc,, +pkg_resources/_vendor/appdirs.py,sha256=MievUEuv3l_mQISH5SF0shDk_BNhHHzYiAPrT3ITN4I,24701 +pkg_resources/_vendor/packaging/__about__.py,sha256=zkcCPTN_6TcLW0Nrlg0176-R1QQ_WVPTm8sz1R4-HjM,720 +pkg_resources/_vendor/packaging/__init__.py,sha256=_vNac5TrzwsrzbOFIbF-5cHqc_Y2aPT2D7zrIR06BOo,513 +pkg_resources/_vendor/packaging/__pycache__/__about__.cpython-39.pyc,, +pkg_resources/_vendor/packaging/__pycache__/__init__.cpython-39.pyc,, +pkg_resources/_vendor/packaging/__pycache__/_compat.cpython-39.pyc,, +pkg_resources/_vendor/packaging/__pycache__/_structures.cpython-39.pyc,, +pkg_resources/_vendor/packaging/__pycache__/markers.cpython-39.pyc,, +pkg_resources/_vendor/packaging/__pycache__/requirements.cpython-39.pyc,, +pkg_resources/_vendor/packaging/__pycache__/specifiers.cpython-39.pyc,, +pkg_resources/_vendor/packaging/__pycache__/utils.cpython-39.pyc,, +pkg_resources/_vendor/packaging/__pycache__/version.cpython-39.pyc,, +pkg_resources/_vendor/packaging/_compat.py,sha256=Vi_A0rAQeHbU-a9X0tt1yQm9RqkgQbDSxzRw8WlU9kA,860 +pkg_resources/_vendor/packaging/_structures.py,sha256=RImECJ4c_wTlaTYYwZYLHEiebDMaAJmK1oPARhw1T5o,1416 +pkg_resources/_vendor/packaging/markers.py,sha256=uEcBBtGvzqltgnArqb9c4RrcInXezDLos14zbBHhWJo,8248 +pkg_resources/_vendor/packaging/requirements.py,sha256=SikL2UynbsT0qtY9ltqngndha_sfo0w6XGFhAhoSoaQ,4355 +pkg_resources/_vendor/packaging/specifiers.py,sha256=SAMRerzO3fK2IkFZCaZkuwZaL_EGqHNOz4pni4vhnN0,28025 +pkg_resources/_vendor/packaging/utils.py,sha256=3m6WvPm6NNxE8rkTGmn0r75B_GZSGg7ikafxHsBN1WA,421 +pkg_resources/_vendor/packaging/version.py,sha256=OwGnxYfr2ghNzYx59qWIBkrK3SnB6n-Zfd1XaLpnnM0,11556 +pkg_resources/_vendor/pyparsing.py,sha256=tmrp-lu-qO1i75ZzIN5A12nKRRD1Cm4Vpk-5LR9rims,232055 +pkg_resources/_vendor/six.py,sha256=A6hdJZVjI3t_geebZ9BzUvwRrIXo0lfwzQlM2LcKyas,30098 +pkg_resources/extern/__init__.py,sha256=cHiEfHuLmm6rs5Ve_ztBfMI7Lr31vss-D4wkqF5xzlI,2498 +pkg_resources/extern/__pycache__/__init__.cpython-39.pyc,, +pkg_resources/py31compat.py,sha256=-WQ0e4c3RG_acdhwC3gLiXhP_lg4G5q7XYkZkQg0gxU,558 diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/REQUESTED b/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/REQUESTED new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/WHEEL b/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/WHEEL new file mode 100644 index 0000000000..ef99c6cf32 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.34.2) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/__init__.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/__init__.py new file mode 100644 index 0000000000..2f5aa64a6e --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/__init__.py @@ -0,0 +1,3296 @@ +# coding: utf-8 +""" +Package resource API +-------------------- + +A resource is a logical file contained within a package, or a logical +subdirectory thereof. The package resource API expects resource names +to have their path parts separated with ``/``, *not* whatever the local +path separator is. Do not use os.path operations to manipulate resource +names being passed into the API. + +The package resource API is designed to work with normal filesystem packages, +.egg files, and unpacked .egg files. It can also work in a limited way with +.zip files and with custom PEP 302 loaders that support the ``get_data()`` +method. +""" + +from __future__ import absolute_import + +import sys +import os +import io +import time +import re +import types +import zipfile +import zipimport +import warnings +import stat +import functools +import pkgutil +import operator +import platform +import collections +import plistlib +import email.parser +import errno +import tempfile +import textwrap +import itertools +import inspect +import ntpath +import posixpath +from pkgutil import get_importer + +try: + import _imp +except ImportError: + # Python 3.2 compatibility + import imp as _imp + +try: + FileExistsError +except NameError: + FileExistsError = OSError + +from pkg_resources.extern import six +from pkg_resources.extern.six.moves import urllib, map, filter + +# capture these to bypass sandboxing +from os import utime +try: + from os import mkdir, rename, unlink + WRITE_SUPPORT = True +except ImportError: + # no write support, probably under GAE + WRITE_SUPPORT = False + +from os import open as os_open +from os.path import isdir, split + +try: + import importlib.machinery as importlib_machinery + # access attribute to force import under delayed import mechanisms. + importlib_machinery.__name__ +except ImportError: + importlib_machinery = None + +from . import py31compat +from pkg_resources.extern import appdirs +from pkg_resources.extern import packaging +__import__('pkg_resources.extern.packaging.version') +__import__('pkg_resources.extern.packaging.specifiers') +__import__('pkg_resources.extern.packaging.requirements') +__import__('pkg_resources.extern.packaging.markers') + + +__metaclass__ = type + + +if (3, 0) < sys.version_info < (3, 5): + raise RuntimeError("Python 3.5 or later is required") + +if six.PY2: + # Those builtin exceptions are only defined in Python 3 + PermissionError = None + NotADirectoryError = None + +# declare some globals that will be defined later to +# satisfy the linters. +require = None +working_set = None +add_activation_listener = None +resources_stream = None +cleanup_resources = None +resource_dir = None +resource_stream = None +set_extraction_path = None +resource_isdir = None +resource_string = None +iter_entry_points = None +resource_listdir = None +resource_filename = None +resource_exists = None +_distribution_finders = None +_namespace_handlers = None +_namespace_packages = None + + +class PEP440Warning(RuntimeWarning): + """ + Used when there is an issue with a version or specifier not complying with + PEP 440. + """ + + +def parse_version(v): + try: + return packaging.version.Version(v) + except packaging.version.InvalidVersion: + return packaging.version.LegacyVersion(v) + + +_state_vars = {} + + +def _declare_state(vartype, **kw): + globals().update(kw) + _state_vars.update(dict.fromkeys(kw, vartype)) + + +def __getstate__(): + state = {} + g = globals() + for k, v in _state_vars.items(): + state[k] = g['_sget_' + v](g[k]) + return state + + +def __setstate__(state): + g = globals() + for k, v in state.items(): + g['_sset_' + _state_vars[k]](k, g[k], v) + return state + + +def _sget_dict(val): + return val.copy() + + +def _sset_dict(key, ob, state): + ob.clear() + ob.update(state) + + +def _sget_object(val): + return val.__getstate__() + + +def _sset_object(key, ob, state): + ob.__setstate__(state) + + +_sget_none = _sset_none = lambda *args: None + + +def get_supported_platform(): + """Return this platform's maximum compatible version. + + distutils.util.get_platform() normally reports the minimum version + of Mac OS X that would be required to *use* extensions produced by + distutils. But what we want when checking compatibility is to know the + version of Mac OS X that we are *running*. To allow usage of packages that + explicitly require a newer version of Mac OS X, we must also know the + current version of the OS. + + If this condition occurs for any other platform with a version in its + platform strings, this function should be extended accordingly. + """ + plat = get_build_platform() + m = macosVersionString.match(plat) + if m is not None and sys.platform == "darwin": + try: + plat = 'macosx-%s-%s' % ('.'.join(_macosx_vers()[:2]), m.group(3)) + except ValueError: + # not Mac OS X + pass + return plat + + +__all__ = [ + # Basic resource access and distribution/entry point discovery + 'require', 'run_script', 'get_provider', 'get_distribution', + 'load_entry_point', 'get_entry_map', 'get_entry_info', + 'iter_entry_points', + 'resource_string', 'resource_stream', 'resource_filename', + 'resource_listdir', 'resource_exists', 'resource_isdir', + + # Environmental control + 'declare_namespace', 'working_set', 'add_activation_listener', + 'find_distributions', 'set_extraction_path', 'cleanup_resources', + 'get_default_cache', + + # Primary implementation classes + 'Environment', 'WorkingSet', 'ResourceManager', + 'Distribution', 'Requirement', 'EntryPoint', + + # Exceptions + 'ResolutionError', 'VersionConflict', 'DistributionNotFound', + 'UnknownExtra', 'ExtractionError', + + # Warnings + 'PEP440Warning', + + # Parsing functions and string utilities + 'parse_requirements', 'parse_version', 'safe_name', 'safe_version', + 'get_platform', 'compatible_platforms', 'yield_lines', 'split_sections', + 'safe_extra', 'to_filename', 'invalid_marker', 'evaluate_marker', + + # filesystem utilities + 'ensure_directory', 'normalize_path', + + # Distribution "precedence" constants + 'EGG_DIST', 'BINARY_DIST', 'SOURCE_DIST', 'CHECKOUT_DIST', 'DEVELOP_DIST', + + # "Provider" interfaces, implementations, and registration/lookup APIs + 'IMetadataProvider', 'IResourceProvider', 'FileMetadata', + 'PathMetadata', 'EggMetadata', 'EmptyProvider', 'empty_provider', + 'NullProvider', 'EggProvider', 'DefaultProvider', 'ZipProvider', + 'register_finder', 'register_namespace_handler', 'register_loader_type', + 'fixup_namespace_packages', 'get_importer', + + # Warnings + 'PkgResourcesDeprecationWarning', + + # Deprecated/backward compatibility only + 'run_main', 'AvailableDistributions', +] + + +class ResolutionError(Exception): + """Abstract base for dependency resolution errors""" + + def __repr__(self): + return self.__class__.__name__ + repr(self.args) + + +class VersionConflict(ResolutionError): + """ + An already-installed version conflicts with the requested version. + + Should be initialized with the installed Distribution and the requested + Requirement. + """ + + _template = "{self.dist} is installed but {self.req} is required" + + @property + def dist(self): + return self.args[0] + + @property + def req(self): + return self.args[1] + + def report(self): + return self._template.format(**locals()) + + def with_context(self, required_by): + """ + If required_by is non-empty, return a version of self that is a + ContextualVersionConflict. + """ + if not required_by: + return self + args = self.args + (required_by,) + return ContextualVersionConflict(*args) + + +class ContextualVersionConflict(VersionConflict): + """ + A VersionConflict that accepts a third parameter, the set of the + requirements that required the installed Distribution. + """ + + _template = VersionConflict._template + ' by {self.required_by}' + + @property + def required_by(self): + return self.args[2] + + +class DistributionNotFound(ResolutionError): + """A requested distribution was not found""" + + _template = ("The '{self.req}' distribution was not found " + "and is required by {self.requirers_str}") + + @property + def req(self): + return self.args[0] + + @property + def requirers(self): + return self.args[1] + + @property + def requirers_str(self): + if not self.requirers: + return 'the application' + return ', '.join(self.requirers) + + def report(self): + return self._template.format(**locals()) + + def __str__(self): + return self.report() + + +class UnknownExtra(ResolutionError): + """Distribution doesn't have an "extra feature" of the given name""" + + +_provider_factories = {} + +PY_MAJOR = '{}.{}'.format(*sys.version_info) +EGG_DIST = 3 +BINARY_DIST = 2 +SOURCE_DIST = 1 +CHECKOUT_DIST = 0 +DEVELOP_DIST = -1 + + +def register_loader_type(loader_type, provider_factory): + """Register `provider_factory` to make providers for `loader_type` + + `loader_type` is the type or class of a PEP 302 ``module.__loader__``, + and `provider_factory` is a function that, passed a *module* object, + returns an ``IResourceProvider`` for that module. + """ + _provider_factories[loader_type] = provider_factory + + +def get_provider(moduleOrReq): + """Return an IResourceProvider for the named module or requirement""" + if isinstance(moduleOrReq, Requirement): + return working_set.find(moduleOrReq) or require(str(moduleOrReq))[0] + try: + module = sys.modules[moduleOrReq] + except KeyError: + __import__(moduleOrReq) + module = sys.modules[moduleOrReq] + loader = getattr(module, '__loader__', None) + return _find_adapter(_provider_factories, loader)(module) + + +def _macosx_vers(_cache=[]): + if not _cache: + version = platform.mac_ver()[0] + # fallback for MacPorts + if version == '': + plist = '/System/Library/CoreServices/SystemVersion.plist' + if os.path.exists(plist): + if hasattr(plistlib, 'readPlist'): + plist_content = plistlib.readPlist(plist) + if 'ProductVersion' in plist_content: + version = plist_content['ProductVersion'] + + _cache.append(version.split('.')) + return _cache[0] + + +def _macosx_arch(machine): + return {'PowerPC': 'ppc', 'Power_Macintosh': 'ppc'}.get(machine, machine) + + +def get_build_platform(): + """Return this platform's string for platform-specific distributions + + XXX Currently this is the same as ``distutils.util.get_platform()``, but it + needs some hacks for Linux and Mac OS X. + """ + from sysconfig import get_platform + + plat = get_platform() + if sys.platform == "darwin" and not plat.startswith('macosx-'): + try: + version = _macosx_vers() + machine = os.uname()[4].replace(" ", "_") + return "macosx-%d.%d-%s" % ( + int(version[0]), int(version[1]), + _macosx_arch(machine), + ) + except ValueError: + # if someone is running a non-Mac darwin system, this will fall + # through to the default implementation + pass + return plat + + +macosVersionString = re.compile(r"macosx-(\d+)\.(\d+)-(.*)") +darwinVersionString = re.compile(r"darwin-(\d+)\.(\d+)\.(\d+)-(.*)") +# XXX backward compat +get_platform = get_build_platform + + +def compatible_platforms(provided, required): + """Can code for the `provided` platform run on the `required` platform? + + Returns true if either platform is ``None``, or the platforms are equal. + + XXX Needs compatibility checks for Linux and other unixy OSes. + """ + if provided is None or required is None or provided == required: + # easy case + return True + + # Mac OS X special cases + reqMac = macosVersionString.match(required) + if reqMac: + provMac = macosVersionString.match(provided) + + # is this a Mac package? + if not provMac: + # this is backwards compatibility for packages built before + # setuptools 0.6. All packages built after this point will + # use the new macosx designation. + provDarwin = darwinVersionString.match(provided) + if provDarwin: + dversion = int(provDarwin.group(1)) + macosversion = "%s.%s" % (reqMac.group(1), reqMac.group(2)) + if dversion == 7 and macosversion >= "10.3" or \ + dversion == 8 and macosversion >= "10.4": + return True + # egg isn't macosx or legacy darwin + return False + + # are they the same major version and machine type? + if provMac.group(1) != reqMac.group(1) or \ + provMac.group(3) != reqMac.group(3): + return False + + # is the required OS major update >= the provided one? + if int(provMac.group(2)) > int(reqMac.group(2)): + return False + + return True + + # XXX Linux and other platforms' special cases should go here + return False + + +def run_script(dist_spec, script_name): + """Locate distribution `dist_spec` and run its `script_name` script""" + ns = sys._getframe(1).f_globals + name = ns['__name__'] + ns.clear() + ns['__name__'] = name + require(dist_spec)[0].run_script(script_name, ns) + + +# backward compatibility +run_main = run_script + + +def get_distribution(dist): + """Return a current distribution object for a Requirement or string""" + if isinstance(dist, six.string_types): + dist = Requirement.parse(dist) + if isinstance(dist, Requirement): + dist = get_provider(dist) + if not isinstance(dist, Distribution): + raise TypeError("Expected string, Requirement, or Distribution", dist) + return dist + + +def load_entry_point(dist, group, name): + """Return `name` entry point of `group` for `dist` or raise ImportError""" + return get_distribution(dist).load_entry_point(group, name) + + +def get_entry_map(dist, group=None): + """Return the entry point map for `group`, or the full entry map""" + return get_distribution(dist).get_entry_map(group) + + +def get_entry_info(dist, group, name): + """Return the EntryPoint object for `group`+`name`, or ``None``""" + return get_distribution(dist).get_entry_info(group, name) + + +class IMetadataProvider: + def has_metadata(name): + """Does the package's distribution contain the named metadata?""" + + def get_metadata(name): + """The named metadata resource as a string""" + + def get_metadata_lines(name): + """Yield named metadata resource as list of non-blank non-comment lines + + Leading and trailing whitespace is stripped from each line, and lines + with ``#`` as the first non-blank character are omitted.""" + + def metadata_isdir(name): + """Is the named metadata a directory? (like ``os.path.isdir()``)""" + + def metadata_listdir(name): + """List of metadata names in the directory (like ``os.listdir()``)""" + + def run_script(script_name, namespace): + """Execute the named script in the supplied namespace dictionary""" + + +class IResourceProvider(IMetadataProvider): + """An object that provides access to package resources""" + + def get_resource_filename(manager, resource_name): + """Return a true filesystem path for `resource_name` + + `manager` must be an ``IResourceManager``""" + + def get_resource_stream(manager, resource_name): + """Return a readable file-like object for `resource_name` + + `manager` must be an ``IResourceManager``""" + + def get_resource_string(manager, resource_name): + """Return a string containing the contents of `resource_name` + + `manager` must be an ``IResourceManager``""" + + def has_resource(resource_name): + """Does the package contain the named resource?""" + + def resource_isdir(resource_name): + """Is the named resource a directory? (like ``os.path.isdir()``)""" + + def resource_listdir(resource_name): + """List of resource names in the directory (like ``os.listdir()``)""" + + +class WorkingSet: + """A collection of active distributions on sys.path (or a similar list)""" + + def __init__(self, entries=None): + """Create working set from list of path entries (default=sys.path)""" + self.entries = [] + self.entry_keys = {} + self.by_key = {} + self.callbacks = [] + + if entries is None: + entries = sys.path + + for entry in entries: + self.add_entry(entry) + + @classmethod + def _build_master(cls): + """ + Prepare the master working set. + """ + ws = cls() + try: + from __main__ import __requires__ + except ImportError: + # The main program does not list any requirements + return ws + + # ensure the requirements are met + try: + ws.require(__requires__) + except VersionConflict: + return cls._build_from_requirements(__requires__) + + return ws + + @classmethod + def _build_from_requirements(cls, req_spec): + """ + Build a working set from a requirement spec. Rewrites sys.path. + """ + # try it without defaults already on sys.path + # by starting with an empty path + ws = cls([]) + reqs = parse_requirements(req_spec) + dists = ws.resolve(reqs, Environment()) + for dist in dists: + ws.add(dist) + + # add any missing entries from sys.path + for entry in sys.path: + if entry not in ws.entries: + ws.add_entry(entry) + + # then copy back to sys.path + sys.path[:] = ws.entries + return ws + + def add_entry(self, entry): + """Add a path item to ``.entries``, finding any distributions on it + + ``find_distributions(entry, True)`` is used to find distributions + corresponding to the path entry, and they are added. `entry` is + always appended to ``.entries``, even if it is already present. + (This is because ``sys.path`` can contain the same value more than + once, and the ``.entries`` of the ``sys.path`` WorkingSet should always + equal ``sys.path``.) + """ + self.entry_keys.setdefault(entry, []) + self.entries.append(entry) + for dist in find_distributions(entry, True): + self.add(dist, entry, False) + + def __contains__(self, dist): + """True if `dist` is the active distribution for its project""" + return self.by_key.get(dist.key) == dist + + def find(self, req): + """Find a distribution matching requirement `req` + + If there is an active distribution for the requested project, this + returns it as long as it meets the version requirement specified by + `req`. But, if there is an active distribution for the project and it + does *not* meet the `req` requirement, ``VersionConflict`` is raised. + If there is no active distribution for the requested project, ``None`` + is returned. + """ + dist = self.by_key.get(req.key) + if dist is not None and dist not in req: + # XXX add more info + raise VersionConflict(dist, req) + return dist + + def iter_entry_points(self, group, name=None): + """Yield entry point objects from `group` matching `name` + + If `name` is None, yields all entry points in `group` from all + distributions in the working set, otherwise only ones matching + both `group` and `name` are yielded (in distribution order). + """ + return ( + entry + for dist in self + for entry in dist.get_entry_map(group).values() + if name is None or name == entry.name + ) + + def run_script(self, requires, script_name): + """Locate distribution for `requires` and run `script_name` script""" + ns = sys._getframe(1).f_globals + name = ns['__name__'] + ns.clear() + ns['__name__'] = name + self.require(requires)[0].run_script(script_name, ns) + + def __iter__(self): + """Yield distributions for non-duplicate projects in the working set + + The yield order is the order in which the items' path entries were + added to the working set. + """ + seen = {} + for item in self.entries: + if item not in self.entry_keys: + # workaround a cache issue + continue + + for key in self.entry_keys[item]: + if key not in seen: + seen[key] = 1 + yield self.by_key[key] + + def add(self, dist, entry=None, insert=True, replace=False): + """Add `dist` to working set, associated with `entry` + + If `entry` is unspecified, it defaults to the ``.location`` of `dist`. + On exit from this routine, `entry` is added to the end of the working + set's ``.entries`` (if it wasn't already present). + + `dist` is only added to the working set if it's for a project that + doesn't already have a distribution in the set, unless `replace=True`. + If it's added, any callbacks registered with the ``subscribe()`` method + will be called. + """ + if insert: + dist.insert_on(self.entries, entry, replace=replace) + + if entry is None: + entry = dist.location + keys = self.entry_keys.setdefault(entry, []) + keys2 = self.entry_keys.setdefault(dist.location, []) + if not replace and dist.key in self.by_key: + # ignore hidden distros + return + + self.by_key[dist.key] = dist + if dist.key not in keys: + keys.append(dist.key) + if dist.key not in keys2: + keys2.append(dist.key) + self._added_new(dist) + + def resolve(self, requirements, env=None, installer=None, + replace_conflicting=False, extras=None): + """List all distributions needed to (recursively) meet `requirements` + + `requirements` must be a sequence of ``Requirement`` objects. `env`, + if supplied, should be an ``Environment`` instance. If + not supplied, it defaults to all distributions available within any + entry or distribution in the working set. `installer`, if supplied, + will be invoked with each requirement that cannot be met by an + already-installed distribution; it should return a ``Distribution`` or + ``None``. + + Unless `replace_conflicting=True`, raises a VersionConflict exception + if + any requirements are found on the path that have the correct name but + the wrong version. Otherwise, if an `installer` is supplied it will be + invoked to obtain the correct version of the requirement and activate + it. + + `extras` is a list of the extras to be used with these requirements. + This is important because extra requirements may look like `my_req; + extra = "my_extra"`, which would otherwise be interpreted as a purely + optional requirement. Instead, we want to be able to assert that these + requirements are truly required. + """ + + # set up the stack + requirements = list(requirements)[::-1] + # set of processed requirements + processed = {} + # key -> dist + best = {} + to_activate = [] + + req_extras = _ReqExtras() + + # Mapping of requirement to set of distributions that required it; + # useful for reporting info about conflicts. + required_by = collections.defaultdict(set) + + while requirements: + # process dependencies breadth-first + req = requirements.pop(0) + if req in processed: + # Ignore cyclic or redundant dependencies + continue + + if not req_extras.markers_pass(req, extras): + continue + + dist = best.get(req.key) + if dist is None: + # Find the best distribution and add it to the map + dist = self.by_key.get(req.key) + if dist is None or (dist not in req and replace_conflicting): + ws = self + if env is None: + if dist is None: + env = Environment(self.entries) + else: + # Use an empty environment and workingset to avoid + # any further conflicts with the conflicting + # distribution + env = Environment([]) + ws = WorkingSet([]) + dist = best[req.key] = env.best_match( + req, ws, installer, + replace_conflicting=replace_conflicting + ) + if dist is None: + requirers = required_by.get(req, None) + raise DistributionNotFound(req, requirers) + to_activate.append(dist) + if dist not in req: + # Oops, the "best" so far conflicts with a dependency + dependent_req = required_by[req] + raise VersionConflict(dist, req).with_context(dependent_req) + + # push the new requirements onto the stack + new_requirements = dist.requires(req.extras)[::-1] + requirements.extend(new_requirements) + + # Register the new requirements needed by req + for new_requirement in new_requirements: + required_by[new_requirement].add(req.project_name) + req_extras[new_requirement] = req.extras + + processed[req] = True + + # return list of distros to activate + return to_activate + + def find_plugins( + self, plugin_env, full_env=None, installer=None, fallback=True): + """Find all activatable distributions in `plugin_env` + + Example usage:: + + distributions, errors = working_set.find_plugins( + Environment(plugin_dirlist) + ) + # add plugins+libs to sys.path + map(working_set.add, distributions) + # display errors + print('Could not load', errors) + + The `plugin_env` should be an ``Environment`` instance that contains + only distributions that are in the project's "plugin directory" or + directories. The `full_env`, if supplied, should be an ``Environment`` + contains all currently-available distributions. If `full_env` is not + supplied, one is created automatically from the ``WorkingSet`` this + method is called on, which will typically mean that every directory on + ``sys.path`` will be scanned for distributions. + + `installer` is a standard installer callback as used by the + ``resolve()`` method. The `fallback` flag indicates whether we should + attempt to resolve older versions of a plugin if the newest version + cannot be resolved. + + This method returns a 2-tuple: (`distributions`, `error_info`), where + `distributions` is a list of the distributions found in `plugin_env` + that were loadable, along with any other distributions that are needed + to resolve their dependencies. `error_info` is a dictionary mapping + unloadable plugin distributions to an exception instance describing the + error that occurred. Usually this will be a ``DistributionNotFound`` or + ``VersionConflict`` instance. + """ + + plugin_projects = list(plugin_env) + # scan project names in alphabetic order + plugin_projects.sort() + + error_info = {} + distributions = {} + + if full_env is None: + env = Environment(self.entries) + env += plugin_env + else: + env = full_env + plugin_env + + shadow_set = self.__class__([]) + # put all our entries in shadow_set + list(map(shadow_set.add, self)) + + for project_name in plugin_projects: + + for dist in plugin_env[project_name]: + + req = [dist.as_requirement()] + + try: + resolvees = shadow_set.resolve(req, env, installer) + + except ResolutionError as v: + # save error info + error_info[dist] = v + if fallback: + # try the next older version of project + continue + else: + # give up on this project, keep going + break + + else: + list(map(shadow_set.add, resolvees)) + distributions.update(dict.fromkeys(resolvees)) + + # success, no need to try any more versions of this project + break + + distributions = list(distributions) + distributions.sort() + + return distributions, error_info + + def require(self, *requirements): + """Ensure that distributions matching `requirements` are activated + + `requirements` must be a string or a (possibly-nested) sequence + thereof, specifying the distributions and versions required. The + return value is a sequence of the distributions that needed to be + activated to fulfill the requirements; all relevant distributions are + included, even if they were already activated in this working set. + """ + needed = self.resolve(parse_requirements(requirements)) + + for dist in needed: + self.add(dist) + + return needed + + def subscribe(self, callback, existing=True): + """Invoke `callback` for all distributions + + If `existing=True` (default), + call on all existing ones, as well. + """ + if callback in self.callbacks: + return + self.callbacks.append(callback) + if not existing: + return + for dist in self: + callback(dist) + + def _added_new(self, dist): + for callback in self.callbacks: + callback(dist) + + def __getstate__(self): + return ( + self.entries[:], self.entry_keys.copy(), self.by_key.copy(), + self.callbacks[:] + ) + + def __setstate__(self, e_k_b_c): + entries, keys, by_key, callbacks = e_k_b_c + self.entries = entries[:] + self.entry_keys = keys.copy() + self.by_key = by_key.copy() + self.callbacks = callbacks[:] + + +class _ReqExtras(dict): + """ + Map each requirement to the extras that demanded it. + """ + + def markers_pass(self, req, extras=None): + """ + Evaluate markers for req against each extra that + demanded it. + + Return False if the req has a marker and fails + evaluation. Otherwise, return True. + """ + extra_evals = ( + req.marker.evaluate({'extra': extra}) + for extra in self.get(req, ()) + (extras or (None,)) + ) + return not req.marker or any(extra_evals) + + +class Environment: + """Searchable snapshot of distributions on a search path""" + + def __init__( + self, search_path=None, platform=get_supported_platform(), + python=PY_MAJOR): + """Snapshot distributions available on a search path + + Any distributions found on `search_path` are added to the environment. + `search_path` should be a sequence of ``sys.path`` items. If not + supplied, ``sys.path`` is used. + + `platform` is an optional string specifying the name of the platform + that platform-specific distributions must be compatible with. If + unspecified, it defaults to the current platform. `python` is an + optional string naming the desired version of Python (e.g. ``'3.6'``); + it defaults to the current version. + + You may explicitly set `platform` (and/or `python`) to ``None`` if you + wish to map *all* distributions, not just those compatible with the + running platform or Python version. + """ + self._distmap = {} + self.platform = platform + self.python = python + self.scan(search_path) + + def can_add(self, dist): + """Is distribution `dist` acceptable for this environment? + + The distribution must match the platform and python version + requirements specified when this environment was created, or False + is returned. + """ + py_compat = ( + self.python is None + or dist.py_version is None + or dist.py_version == self.python + ) + return py_compat and compatible_platforms(dist.platform, self.platform) + + def remove(self, dist): + """Remove `dist` from the environment""" + self._distmap[dist.key].remove(dist) + + def scan(self, search_path=None): + """Scan `search_path` for distributions usable in this environment + + Any distributions found are added to the environment. + `search_path` should be a sequence of ``sys.path`` items. If not + supplied, ``sys.path`` is used. Only distributions conforming to + the platform/python version defined at initialization are added. + """ + if search_path is None: + search_path = sys.path + + for item in search_path: + for dist in find_distributions(item): + self.add(dist) + + def __getitem__(self, project_name): + """Return a newest-to-oldest list of distributions for `project_name` + + Uses case-insensitive `project_name` comparison, assuming all the + project's distributions use their project's name converted to all + lowercase as their key. + + """ + distribution_key = project_name.lower() + return self._distmap.get(distribution_key, []) + + def add(self, dist): + """Add `dist` if we ``can_add()`` it and it has not already been added + """ + if self.can_add(dist) and dist.has_version(): + dists = self._distmap.setdefault(dist.key, []) + if dist not in dists: + dists.append(dist) + dists.sort(key=operator.attrgetter('hashcmp'), reverse=True) + + def best_match( + self, req, working_set, installer=None, replace_conflicting=False): + """Find distribution best matching `req` and usable on `working_set` + + This calls the ``find(req)`` method of the `working_set` to see if a + suitable distribution is already active. (This may raise + ``VersionConflict`` if an unsuitable version of the project is already + active in the specified `working_set`.) If a suitable distribution + isn't active, this method returns the newest distribution in the + environment that meets the ``Requirement`` in `req`. If no suitable + distribution is found, and `installer` is supplied, then the result of + calling the environment's ``obtain(req, installer)`` method will be + returned. + """ + try: + dist = working_set.find(req) + except VersionConflict: + if not replace_conflicting: + raise + dist = None + if dist is not None: + return dist + for dist in self[req.key]: + if dist in req: + return dist + # try to download/install + return self.obtain(req, installer) + + def obtain(self, requirement, installer=None): + """Obtain a distribution matching `requirement` (e.g. via download) + + Obtain a distro that matches requirement (e.g. via download). In the + base ``Environment`` class, this routine just returns + ``installer(requirement)``, unless `installer` is None, in which case + None is returned instead. This method is a hook that allows subclasses + to attempt other ways of obtaining a distribution before falling back + to the `installer` argument.""" + if installer is not None: + return installer(requirement) + + def __iter__(self): + """Yield the unique project names of the available distributions""" + for key in self._distmap.keys(): + if self[key]: + yield key + + def __iadd__(self, other): + """In-place addition of a distribution or environment""" + if isinstance(other, Distribution): + self.add(other) + elif isinstance(other, Environment): + for project in other: + for dist in other[project]: + self.add(dist) + else: + raise TypeError("Can't add %r to environment" % (other,)) + return self + + def __add__(self, other): + """Add an environment or distribution to an environment""" + new = self.__class__([], platform=None, python=None) + for env in self, other: + new += env + return new + + +# XXX backward compatibility +AvailableDistributions = Environment + + +class ExtractionError(RuntimeError): + """An error occurred extracting a resource + + The following attributes are available from instances of this exception: + + manager + The resource manager that raised this exception + + cache_path + The base directory for resource extraction + + original_error + The exception instance that caused extraction to fail + """ + + +class ResourceManager: + """Manage resource extraction and packages""" + extraction_path = None + + def __init__(self): + self.cached_files = {} + + def resource_exists(self, package_or_requirement, resource_name): + """Does the named resource exist?""" + return get_provider(package_or_requirement).has_resource(resource_name) + + def resource_isdir(self, package_or_requirement, resource_name): + """Is the named resource an existing directory?""" + return get_provider(package_or_requirement).resource_isdir( + resource_name + ) + + def resource_filename(self, package_or_requirement, resource_name): + """Return a true filesystem path for specified resource""" + return get_provider(package_or_requirement).get_resource_filename( + self, resource_name + ) + + def resource_stream(self, package_or_requirement, resource_name): + """Return a readable file-like object for specified resource""" + return get_provider(package_or_requirement).get_resource_stream( + self, resource_name + ) + + def resource_string(self, package_or_requirement, resource_name): + """Return specified resource as a string""" + return get_provider(package_or_requirement).get_resource_string( + self, resource_name + ) + + def resource_listdir(self, package_or_requirement, resource_name): + """List the contents of the named resource directory""" + return get_provider(package_or_requirement).resource_listdir( + resource_name + ) + + def extraction_error(self): + """Give an error message for problems extracting file(s)""" + + old_exc = sys.exc_info()[1] + cache_path = self.extraction_path or get_default_cache() + + tmpl = textwrap.dedent(""" + Can't extract file(s) to egg cache + + The following error occurred while trying to extract file(s) + to the Python egg cache: + + {old_exc} + + The Python egg cache directory is currently set to: + + {cache_path} + + Perhaps your account does not have write access to this directory? + You can change the cache directory by setting the PYTHON_EGG_CACHE + environment variable to point to an accessible directory. + """).lstrip() + err = ExtractionError(tmpl.format(**locals())) + err.manager = self + err.cache_path = cache_path + err.original_error = old_exc + raise err + + def get_cache_path(self, archive_name, names=()): + """Return absolute location in cache for `archive_name` and `names` + + The parent directory of the resulting path will be created if it does + not already exist. `archive_name` should be the base filename of the + enclosing egg (which may not be the name of the enclosing zipfile!), + including its ".egg" extension. `names`, if provided, should be a + sequence of path name parts "under" the egg's extraction location. + + This method should only be called by resource providers that need to + obtain an extraction location, and only for names they intend to + extract, as it tracks the generated names for possible cleanup later. + """ + extract_path = self.extraction_path or get_default_cache() + target_path = os.path.join(extract_path, archive_name + '-tmp', *names) + try: + _bypass_ensure_directory(target_path) + except Exception: + self.extraction_error() + + self._warn_unsafe_extraction_path(extract_path) + + self.cached_files[target_path] = 1 + return target_path + + @staticmethod + def _warn_unsafe_extraction_path(path): + """ + If the default extraction path is overridden and set to an insecure + location, such as /tmp, it opens up an opportunity for an attacker to + replace an extracted file with an unauthorized payload. Warn the user + if a known insecure location is used. + + See Distribute #375 for more details. + """ + if os.name == 'nt' and not path.startswith(os.environ['windir']): + # On Windows, permissions are generally restrictive by default + # and temp directories are not writable by other users, so + # bypass the warning. + return + mode = os.stat(path).st_mode + if mode & stat.S_IWOTH or mode & stat.S_IWGRP: + msg = ( + "%s is writable by group/others and vulnerable to attack " + "when " + "used with get_resource_filename. Consider a more secure " + "location (set with .set_extraction_path or the " + "PYTHON_EGG_CACHE environment variable)." % path + ) + warnings.warn(msg, UserWarning) + + def postprocess(self, tempname, filename): + """Perform any platform-specific postprocessing of `tempname` + + This is where Mac header rewrites should be done; other platforms don't + have anything special they should do. + + Resource providers should call this method ONLY after successfully + extracting a compressed resource. They must NOT call it on resources + that are already in the filesystem. + + `tempname` is the current (temporary) name of the file, and `filename` + is the name it will be renamed to by the caller after this routine + returns. + """ + + if os.name == 'posix': + # Make the resource executable + mode = ((os.stat(tempname).st_mode) | 0o555) & 0o7777 + os.chmod(tempname, mode) + + def set_extraction_path(self, path): + """Set the base path where resources will be extracted to, if needed. + + If you do not call this routine before any extractions take place, the + path defaults to the return value of ``get_default_cache()``. (Which + is based on the ``PYTHON_EGG_CACHE`` environment variable, with various + platform-specific fallbacks. See that routine's documentation for more + details.) + + Resources are extracted to subdirectories of this path based upon + information given by the ``IResourceProvider``. You may set this to a + temporary directory, but then you must call ``cleanup_resources()`` to + delete the extracted files when done. There is no guarantee that + ``cleanup_resources()`` will be able to remove all extracted files. + + (Note: you may not change the extraction path for a given resource + manager once resources have been extracted, unless you first call + ``cleanup_resources()``.) + """ + if self.cached_files: + raise ValueError( + "Can't change extraction path, files already extracted" + ) + + self.extraction_path = path + + def cleanup_resources(self, force=False): + """ + Delete all extracted resource files and directories, returning a list + of the file and directory names that could not be successfully removed. + This function does not have any concurrency protection, so it should + generally only be called when the extraction path is a temporary + directory exclusive to a single process. This method is not + automatically called; you must call it explicitly or register it as an + ``atexit`` function if you wish to ensure cleanup of a temporary + directory used for extractions. + """ + # XXX + + +def get_default_cache(): + """ + Return the ``PYTHON_EGG_CACHE`` environment variable + or a platform-relevant user cache dir for an app + named "Python-Eggs". + """ + return ( + os.environ.get('PYTHON_EGG_CACHE') + or appdirs.user_cache_dir(appname='Python-Eggs') + ) + + +def safe_name(name): + """Convert an arbitrary string to a standard distribution name + + Any runs of non-alphanumeric/. characters are replaced with a single '-'. + """ + return re.sub('[^A-Za-z0-9.]+', '-', name) + + +def safe_version(version): + """ + Convert an arbitrary string to a standard version string + """ + try: + # normalize the version + return str(packaging.version.Version(version)) + except packaging.version.InvalidVersion: + version = version.replace(' ', '.') + return re.sub('[^A-Za-z0-9.]+', '-', version) + + +def safe_extra(extra): + """Convert an arbitrary string to a standard 'extra' name + + Any runs of non-alphanumeric characters are replaced with a single '_', + and the result is always lowercased. + """ + return re.sub('[^A-Za-z0-9.-]+', '_', extra).lower() + + +def to_filename(name): + """Convert a project or version name to its filename-escaped form + + Any '-' characters are currently replaced with '_'. + """ + return name.replace('-', '_') + + +def invalid_marker(text): + """ + Validate text as a PEP 508 environment marker; return an exception + if invalid or False otherwise. + """ + try: + evaluate_marker(text) + except SyntaxError as e: + e.filename = None + e.lineno = None + return e + return False + + +def evaluate_marker(text, extra=None): + """ + Evaluate a PEP 508 environment marker. + Return a boolean indicating the marker result in this environment. + Raise SyntaxError if marker is invalid. + + This implementation uses the 'pyparsing' module. + """ + try: + marker = packaging.markers.Marker(text) + return marker.evaluate() + except packaging.markers.InvalidMarker as e: + raise SyntaxError(e) + + +class NullProvider: + """Try to implement resources and metadata for arbitrary PEP 302 loaders""" + + egg_name = None + egg_info = None + loader = None + + def __init__(self, module): + self.loader = getattr(module, '__loader__', None) + self.module_path = os.path.dirname(getattr(module, '__file__', '')) + + def get_resource_filename(self, manager, resource_name): + return self._fn(self.module_path, resource_name) + + def get_resource_stream(self, manager, resource_name): + return io.BytesIO(self.get_resource_string(manager, resource_name)) + + def get_resource_string(self, manager, resource_name): + return self._get(self._fn(self.module_path, resource_name)) + + def has_resource(self, resource_name): + return self._has(self._fn(self.module_path, resource_name)) + + def _get_metadata_path(self, name): + return self._fn(self.egg_info, name) + + def has_metadata(self, name): + if not self.egg_info: + return self.egg_info + + path = self._get_metadata_path(name) + return self._has(path) + + def get_metadata(self, name): + if not self.egg_info: + return "" + path = self._get_metadata_path(name) + value = self._get(path) + if six.PY2: + return value + try: + return value.decode('utf-8') + except UnicodeDecodeError as exc: + # Include the path in the error message to simplify + # troubleshooting, and without changing the exception type. + exc.reason += ' in {} file at path: {}'.format(name, path) + raise + + def get_metadata_lines(self, name): + return yield_lines(self.get_metadata(name)) + + def resource_isdir(self, resource_name): + return self._isdir(self._fn(self.module_path, resource_name)) + + def metadata_isdir(self, name): + return self.egg_info and self._isdir(self._fn(self.egg_info, name)) + + def resource_listdir(self, resource_name): + return self._listdir(self._fn(self.module_path, resource_name)) + + def metadata_listdir(self, name): + if self.egg_info: + return self._listdir(self._fn(self.egg_info, name)) + return [] + + def run_script(self, script_name, namespace): + script = 'scripts/' + script_name + if not self.has_metadata(script): + raise ResolutionError( + "Script {script!r} not found in metadata at {self.egg_info!r}" + .format(**locals()), + ) + script_text = self.get_metadata(script).replace('\r\n', '\n') + script_text = script_text.replace('\r', '\n') + script_filename = self._fn(self.egg_info, script) + namespace['__file__'] = script_filename + if os.path.exists(script_filename): + source = open(script_filename).read() + code = compile(source, script_filename, 'exec') + exec(code, namespace, namespace) + else: + from linecache import cache + cache[script_filename] = ( + len(script_text), 0, script_text.split('\n'), script_filename + ) + script_code = compile(script_text, script_filename, 'exec') + exec(script_code, namespace, namespace) + + def _has(self, path): + raise NotImplementedError( + "Can't perform this operation for unregistered loader type" + ) + + def _isdir(self, path): + raise NotImplementedError( + "Can't perform this operation for unregistered loader type" + ) + + def _listdir(self, path): + raise NotImplementedError( + "Can't perform this operation for unregistered loader type" + ) + + def _fn(self, base, resource_name): + self._validate_resource_path(resource_name) + if resource_name: + return os.path.join(base, *resource_name.split('/')) + return base + + @staticmethod + def _validate_resource_path(path): + """ + Validate the resource paths according to the docs. + https://setuptools.readthedocs.io/en/latest/pkg_resources.html#basic-resource-access + + >>> warned = getfixture('recwarn') + >>> warnings.simplefilter('always') + >>> vrp = NullProvider._validate_resource_path + >>> vrp('foo/bar.txt') + >>> bool(warned) + False + >>> vrp('../foo/bar.txt') + >>> bool(warned) + True + >>> warned.clear() + >>> vrp('/foo/bar.txt') + >>> bool(warned) + True + >>> vrp('foo/../../bar.txt') + >>> bool(warned) + True + >>> warned.clear() + >>> vrp('foo/f../bar.txt') + >>> bool(warned) + False + + Windows path separators are straight-up disallowed. + >>> vrp(r'\\foo/bar.txt') + Traceback (most recent call last): + ... + ValueError: Use of .. or absolute path in a resource path \ +is not allowed. + + >>> vrp(r'C:\\foo/bar.txt') + Traceback (most recent call last): + ... + ValueError: Use of .. or absolute path in a resource path \ +is not allowed. + + Blank values are allowed + + >>> vrp('') + >>> bool(warned) + False + + Non-string values are not. + + >>> vrp(None) + Traceback (most recent call last): + ... + AttributeError: ... + """ + invalid = ( + os.path.pardir in path.split(posixpath.sep) or + posixpath.isabs(path) or + ntpath.isabs(path) + ) + if not invalid: + return + + msg = "Use of .. or absolute path in a resource path is not allowed." + + # Aggressively disallow Windows absolute paths + if ntpath.isabs(path) and not posixpath.isabs(path): + raise ValueError(msg) + + # for compatibility, warn; in future + # raise ValueError(msg) + warnings.warn( + msg[:-1] + " and will raise exceptions in a future release.", + DeprecationWarning, + stacklevel=4, + ) + + def _get(self, path): + if hasattr(self.loader, 'get_data'): + return self.loader.get_data(path) + raise NotImplementedError( + "Can't perform this operation for loaders without 'get_data()'" + ) + + +register_loader_type(object, NullProvider) + + +class EggProvider(NullProvider): + """Provider based on a virtual filesystem""" + + def __init__(self, module): + NullProvider.__init__(self, module) + self._setup_prefix() + + def _setup_prefix(self): + # we assume here that our metadata may be nested inside a "basket" + # of multiple eggs; that's why we use module_path instead of .archive + path = self.module_path + old = None + while path != old: + if _is_egg_path(path): + self.egg_name = os.path.basename(path) + self.egg_info = os.path.join(path, 'EGG-INFO') + self.egg_root = path + break + old = path + path, base = os.path.split(path) + + +class DefaultProvider(EggProvider): + """Provides access to package resources in the filesystem""" + + def _has(self, path): + return os.path.exists(path) + + def _isdir(self, path): + return os.path.isdir(path) + + def _listdir(self, path): + return os.listdir(path) + + def get_resource_stream(self, manager, resource_name): + return open(self._fn(self.module_path, resource_name), 'rb') + + def _get(self, path): + with open(path, 'rb') as stream: + return stream.read() + + @classmethod + def _register(cls): + loader_names = 'SourceFileLoader', 'SourcelessFileLoader', + for name in loader_names: + loader_cls = getattr(importlib_machinery, name, type(None)) + register_loader_type(loader_cls, cls) + + +DefaultProvider._register() + + +class EmptyProvider(NullProvider): + """Provider that returns nothing for all requests""" + + module_path = None + + _isdir = _has = lambda self, path: False + + def _get(self, path): + return '' + + def _listdir(self, path): + return [] + + def __init__(self): + pass + + +empty_provider = EmptyProvider() + + +class ZipManifests(dict): + """ + zip manifest builder + """ + + @classmethod + def build(cls, path): + """ + Build a dictionary similar to the zipimport directory + caches, except instead of tuples, store ZipInfo objects. + + Use a platform-specific path separator (os.sep) for the path keys + for compatibility with pypy on Windows. + """ + with zipfile.ZipFile(path) as zfile: + items = ( + ( + name.replace('/', os.sep), + zfile.getinfo(name), + ) + for name in zfile.namelist() + ) + return dict(items) + + load = build + + +class MemoizedZipManifests(ZipManifests): + """ + Memoized zipfile manifests. + """ + manifest_mod = collections.namedtuple('manifest_mod', 'manifest mtime') + + def load(self, path): + """ + Load a manifest at path or return a suitable manifest already loaded. + """ + path = os.path.normpath(path) + mtime = os.stat(path).st_mtime + + if path not in self or self[path].mtime != mtime: + manifest = self.build(path) + self[path] = self.manifest_mod(manifest, mtime) + + return self[path].manifest + + +class ZipProvider(EggProvider): + """Resource support for zips and eggs""" + + eagers = None + _zip_manifests = MemoizedZipManifests() + + def __init__(self, module): + EggProvider.__init__(self, module) + self.zip_pre = self.loader.archive + os.sep + + def _zipinfo_name(self, fspath): + # Convert a virtual filename (full path to file) into a zipfile subpath + # usable with the zipimport directory cache for our target archive + fspath = fspath.rstrip(os.sep) + if fspath == self.loader.archive: + return '' + if fspath.startswith(self.zip_pre): + return fspath[len(self.zip_pre):] + raise AssertionError( + "%s is not a subpath of %s" % (fspath, self.zip_pre) + ) + + def _parts(self, zip_path): + # Convert a zipfile subpath into an egg-relative path part list. + # pseudo-fs path + fspath = self.zip_pre + zip_path + if fspath.startswith(self.egg_root + os.sep): + return fspath[len(self.egg_root) + 1:].split(os.sep) + raise AssertionError( + "%s is not a subpath of %s" % (fspath, self.egg_root) + ) + + @property + def zipinfo(self): + return self._zip_manifests.load(self.loader.archive) + + def get_resource_filename(self, manager, resource_name): + if not self.egg_name: + raise NotImplementedError( + "resource_filename() only supported for .egg, not .zip" + ) + # no need to lock for extraction, since we use temp names + zip_path = self._resource_to_zip(resource_name) + eagers = self._get_eager_resources() + if '/'.join(self._parts(zip_path)) in eagers: + for name in eagers: + self._extract_resource(manager, self._eager_to_zip(name)) + return self._extract_resource(manager, zip_path) + + @staticmethod + def _get_date_and_size(zip_stat): + size = zip_stat.file_size + # ymdhms+wday, yday, dst + date_time = zip_stat.date_time + (0, 0, -1) + # 1980 offset already done + timestamp = time.mktime(date_time) + return timestamp, size + + def _extract_resource(self, manager, zip_path): + + if zip_path in self._index(): + for name in self._index()[zip_path]: + last = self._extract_resource( + manager, os.path.join(zip_path, name) + ) + # return the extracted directory name + return os.path.dirname(last) + + timestamp, size = self._get_date_and_size(self.zipinfo[zip_path]) + + if not WRITE_SUPPORT: + raise IOError('"os.rename" and "os.unlink" are not supported ' + 'on this platform') + try: + + real_path = manager.get_cache_path( + self.egg_name, self._parts(zip_path) + ) + + if self._is_current(real_path, zip_path): + return real_path + + outf, tmpnam = _mkstemp( + ".$extract", + dir=os.path.dirname(real_path), + ) + os.write(outf, self.loader.get_data(zip_path)) + os.close(outf) + utime(tmpnam, (timestamp, timestamp)) + manager.postprocess(tmpnam, real_path) + + try: + rename(tmpnam, real_path) + + except os.error: + if os.path.isfile(real_path): + if self._is_current(real_path, zip_path): + # the file became current since it was checked above, + # so proceed. + return real_path + # Windows, del old file and retry + elif os.name == 'nt': + unlink(real_path) + rename(tmpnam, real_path) + return real_path + raise + + except os.error: + # report a user-friendly error + manager.extraction_error() + + return real_path + + def _is_current(self, file_path, zip_path): + """ + Return True if the file_path is current for this zip_path + """ + timestamp, size = self._get_date_and_size(self.zipinfo[zip_path]) + if not os.path.isfile(file_path): + return False + stat = os.stat(file_path) + if stat.st_size != size or stat.st_mtime != timestamp: + return False + # check that the contents match + zip_contents = self.loader.get_data(zip_path) + with open(file_path, 'rb') as f: + file_contents = f.read() + return zip_contents == file_contents + + def _get_eager_resources(self): + if self.eagers is None: + eagers = [] + for name in ('native_libs.txt', 'eager_resources.txt'): + if self.has_metadata(name): + eagers.extend(self.get_metadata_lines(name)) + self.eagers = eagers + return self.eagers + + def _index(self): + try: + return self._dirindex + except AttributeError: + ind = {} + for path in self.zipinfo: + parts = path.split(os.sep) + while parts: + parent = os.sep.join(parts[:-1]) + if parent in ind: + ind[parent].append(parts[-1]) + break + else: + ind[parent] = [parts.pop()] + self._dirindex = ind + return ind + + def _has(self, fspath): + zip_path = self._zipinfo_name(fspath) + return zip_path in self.zipinfo or zip_path in self._index() + + def _isdir(self, fspath): + return self._zipinfo_name(fspath) in self._index() + + def _listdir(self, fspath): + return list(self._index().get(self._zipinfo_name(fspath), ())) + + def _eager_to_zip(self, resource_name): + return self._zipinfo_name(self._fn(self.egg_root, resource_name)) + + def _resource_to_zip(self, resource_name): + return self._zipinfo_name(self._fn(self.module_path, resource_name)) + + +register_loader_type(zipimport.zipimporter, ZipProvider) + + +class FileMetadata(EmptyProvider): + """Metadata handler for standalone PKG-INFO files + + Usage:: + + metadata = FileMetadata("/path/to/PKG-INFO") + + This provider rejects all data and metadata requests except for PKG-INFO, + which is treated as existing, and will be the contents of the file at + the provided location. + """ + + def __init__(self, path): + self.path = path + + def _get_metadata_path(self, name): + return self.path + + def has_metadata(self, name): + return name == 'PKG-INFO' and os.path.isfile(self.path) + + def get_metadata(self, name): + if name != 'PKG-INFO': + raise KeyError("No metadata except PKG-INFO is available") + + with io.open(self.path, encoding='utf-8', errors="replace") as f: + metadata = f.read() + self._warn_on_replacement(metadata) + return metadata + + def _warn_on_replacement(self, metadata): + # Python 2.7 compat for: replacement_char = '�' + replacement_char = b'\xef\xbf\xbd'.decode('utf-8') + if replacement_char in metadata: + tmpl = "{self.path} could not be properly decoded in UTF-8" + msg = tmpl.format(**locals()) + warnings.warn(msg) + + def get_metadata_lines(self, name): + return yield_lines(self.get_metadata(name)) + + +class PathMetadata(DefaultProvider): + """Metadata provider for egg directories + + Usage:: + + # Development eggs: + + egg_info = "/path/to/PackageName.egg-info" + base_dir = os.path.dirname(egg_info) + metadata = PathMetadata(base_dir, egg_info) + dist_name = os.path.splitext(os.path.basename(egg_info))[0] + dist = Distribution(basedir, project_name=dist_name, metadata=metadata) + + # Unpacked egg directories: + + egg_path = "/path/to/PackageName-ver-pyver-etc.egg" + metadata = PathMetadata(egg_path, os.path.join(egg_path,'EGG-INFO')) + dist = Distribution.from_filename(egg_path, metadata=metadata) + """ + + def __init__(self, path, egg_info): + self.module_path = path + self.egg_info = egg_info + + +class EggMetadata(ZipProvider): + """Metadata provider for .egg files""" + + def __init__(self, importer): + """Create a metadata provider from a zipimporter""" + + self.zip_pre = importer.archive + os.sep + self.loader = importer + if importer.prefix: + self.module_path = os.path.join(importer.archive, importer.prefix) + else: + self.module_path = importer.archive + self._setup_prefix() + + +_declare_state('dict', _distribution_finders={}) + + +def register_finder(importer_type, distribution_finder): + """Register `distribution_finder` to find distributions in sys.path items + + `importer_type` is the type or class of a PEP 302 "Importer" (sys.path item + handler), and `distribution_finder` is a callable that, passed a path + item and the importer instance, yields ``Distribution`` instances found on + that path item. See ``pkg_resources.find_on_path`` for an example.""" + _distribution_finders[importer_type] = distribution_finder + + +def find_distributions(path_item, only=False): + """Yield distributions accessible via `path_item`""" + importer = get_importer(path_item) + finder = _find_adapter(_distribution_finders, importer) + return finder(importer, path_item, only) + + +def find_eggs_in_zip(importer, path_item, only=False): + """ + Find eggs in zip files; possibly multiple nested eggs. + """ + if importer.archive.endswith('.whl'): + # wheels are not supported with this finder + # they don't have PKG-INFO metadata, and won't ever contain eggs + return + metadata = EggMetadata(importer) + if metadata.has_metadata('PKG-INFO'): + yield Distribution.from_filename(path_item, metadata=metadata) + if only: + # don't yield nested distros + return + for subitem in metadata.resource_listdir(''): + if _is_egg_path(subitem): + subpath = os.path.join(path_item, subitem) + dists = find_eggs_in_zip(zipimport.zipimporter(subpath), subpath) + for dist in dists: + yield dist + elif subitem.lower().endswith('.dist-info'): + subpath = os.path.join(path_item, subitem) + submeta = EggMetadata(zipimport.zipimporter(subpath)) + submeta.egg_info = subpath + yield Distribution.from_location(path_item, subitem, submeta) + + +register_finder(zipimport.zipimporter, find_eggs_in_zip) + + +def find_nothing(importer, path_item, only=False): + return () + + +register_finder(object, find_nothing) + + +def _by_version_descending(names): + """ + Given a list of filenames, return them in descending order + by version number. + + >>> names = 'bar', 'foo', 'Python-2.7.10.egg', 'Python-2.7.2.egg' + >>> _by_version_descending(names) + ['Python-2.7.10.egg', 'Python-2.7.2.egg', 'foo', 'bar'] + >>> names = 'Setuptools-1.2.3b1.egg', 'Setuptools-1.2.3.egg' + >>> _by_version_descending(names) + ['Setuptools-1.2.3.egg', 'Setuptools-1.2.3b1.egg'] + >>> names = 'Setuptools-1.2.3b1.egg', 'Setuptools-1.2.3.post1.egg' + >>> _by_version_descending(names) + ['Setuptools-1.2.3.post1.egg', 'Setuptools-1.2.3b1.egg'] + """ + def _by_version(name): + """ + Parse each component of the filename + """ + name, ext = os.path.splitext(name) + parts = itertools.chain(name.split('-'), [ext]) + return [packaging.version.parse(part) for part in parts] + + return sorted(names, key=_by_version, reverse=True) + + +def find_on_path(importer, path_item, only=False): + """Yield distributions accessible on a sys.path directory""" + path_item = _normalize_cached(path_item) + + if _is_unpacked_egg(path_item): + yield Distribution.from_filename( + path_item, metadata=PathMetadata( + path_item, os.path.join(path_item, 'EGG-INFO') + ) + ) + return + + entries = safe_listdir(path_item) + + # for performance, before sorting by version, + # screen entries for only those that will yield + # distributions + filtered = ( + entry + for entry in entries + if dist_factory(path_item, entry, only) + ) + + # scan for .egg and .egg-info in directory + path_item_entries = _by_version_descending(filtered) + for entry in path_item_entries: + fullpath = os.path.join(path_item, entry) + factory = dist_factory(path_item, entry, only) + for dist in factory(fullpath): + yield dist + + +def dist_factory(path_item, entry, only): + """ + Return a dist_factory for a path_item and entry + """ + lower = entry.lower() + is_meta = any(map(lower.endswith, ('.egg-info', '.dist-info'))) + return ( + distributions_from_metadata + if is_meta else + find_distributions + if not only and _is_egg_path(entry) else + resolve_egg_link + if not only and lower.endswith('.egg-link') else + NoDists() + ) + + +class NoDists: + """ + >>> bool(NoDists()) + False + + >>> list(NoDists()('anything')) + [] + """ + def __bool__(self): + return False + if six.PY2: + __nonzero__ = __bool__ + + def __call__(self, fullpath): + return iter(()) + + +def safe_listdir(path): + """ + Attempt to list contents of path, but suppress some exceptions. + """ + try: + return os.listdir(path) + except (PermissionError, NotADirectoryError): + pass + except OSError as e: + # Ignore the directory if does not exist, not a directory or + # permission denied + ignorable = ( + e.errno in (errno.ENOTDIR, errno.EACCES, errno.ENOENT) + # Python 2 on Windows needs to be handled this way :( + or getattr(e, "winerror", None) == 267 + ) + if not ignorable: + raise + return () + + +def distributions_from_metadata(path): + root = os.path.dirname(path) + if os.path.isdir(path): + if len(os.listdir(path)) == 0: + # empty metadata dir; skip + return + metadata = PathMetadata(root, path) + else: + metadata = FileMetadata(path) + entry = os.path.basename(path) + yield Distribution.from_location( + root, entry, metadata, precedence=DEVELOP_DIST, + ) + + +def non_empty_lines(path): + """ + Yield non-empty lines from file at path + """ + with open(path) as f: + for line in f: + line = line.strip() + if line: + yield line + + +def resolve_egg_link(path): + """ + Given a path to an .egg-link, resolve distributions + present in the referenced path. + """ + referenced_paths = non_empty_lines(path) + resolved_paths = ( + os.path.join(os.path.dirname(path), ref) + for ref in referenced_paths + ) + dist_groups = map(find_distributions, resolved_paths) + return next(dist_groups, ()) + + +register_finder(pkgutil.ImpImporter, find_on_path) + +if hasattr(importlib_machinery, 'FileFinder'): + register_finder(importlib_machinery.FileFinder, find_on_path) + +_declare_state('dict', _namespace_handlers={}) +_declare_state('dict', _namespace_packages={}) + + +def register_namespace_handler(importer_type, namespace_handler): + """Register `namespace_handler` to declare namespace packages + + `importer_type` is the type or class of a PEP 302 "Importer" (sys.path item + handler), and `namespace_handler` is a callable like this:: + + def namespace_handler(importer, path_entry, moduleName, module): + # return a path_entry to use for child packages + + Namespace handlers are only called if the importer object has already + agreed that it can handle the relevant path item, and they should only + return a subpath if the module __path__ does not already contain an + equivalent subpath. For an example namespace handler, see + ``pkg_resources.file_ns_handler``. + """ + _namespace_handlers[importer_type] = namespace_handler + + +def _handle_ns(packageName, path_item): + """Ensure that named package includes a subpath of path_item (if needed)""" + + importer = get_importer(path_item) + if importer is None: + return None + + # capture warnings due to #1111 + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + loader = importer.find_module(packageName) + + if loader is None: + return None + module = sys.modules.get(packageName) + if module is None: + module = sys.modules[packageName] = types.ModuleType(packageName) + module.__path__ = [] + _set_parent_ns(packageName) + elif not hasattr(module, '__path__'): + raise TypeError("Not a package:", packageName) + handler = _find_adapter(_namespace_handlers, importer) + subpath = handler(importer, path_item, packageName, module) + if subpath is not None: + path = module.__path__ + path.append(subpath) + loader.load_module(packageName) + _rebuild_mod_path(path, packageName, module) + return subpath + + +def _rebuild_mod_path(orig_path, package_name, module): + """ + Rebuild module.__path__ ensuring that all entries are ordered + corresponding to their sys.path order + """ + sys_path = [_normalize_cached(p) for p in sys.path] + + def safe_sys_path_index(entry): + """ + Workaround for #520 and #513. + """ + try: + return sys_path.index(entry) + except ValueError: + return float('inf') + + def position_in_sys_path(path): + """ + Return the ordinal of the path based on its position in sys.path + """ + path_parts = path.split(os.sep) + module_parts = package_name.count('.') + 1 + parts = path_parts[:-module_parts] + return safe_sys_path_index(_normalize_cached(os.sep.join(parts))) + + new_path = sorted(orig_path, key=position_in_sys_path) + new_path = [_normalize_cached(p) for p in new_path] + + if isinstance(module.__path__, list): + module.__path__[:] = new_path + else: + module.__path__ = new_path + + +def declare_namespace(packageName): + """Declare that package 'packageName' is a namespace package""" + + _imp.acquire_lock() + try: + if packageName in _namespace_packages: + return + + path = sys.path + parent, _, _ = packageName.rpartition('.') + + if parent: + declare_namespace(parent) + if parent not in _namespace_packages: + __import__(parent) + try: + path = sys.modules[parent].__path__ + except AttributeError: + raise TypeError("Not a package:", parent) + + # Track what packages are namespaces, so when new path items are added, + # they can be updated + _namespace_packages.setdefault(parent or None, []).append(packageName) + _namespace_packages.setdefault(packageName, []) + + for path_item in path: + # Ensure all the parent's path items are reflected in the child, + # if they apply + _handle_ns(packageName, path_item) + + finally: + _imp.release_lock() + + +def fixup_namespace_packages(path_item, parent=None): + """Ensure that previously-declared namespace packages include path_item""" + _imp.acquire_lock() + try: + for package in _namespace_packages.get(parent, ()): + subpath = _handle_ns(package, path_item) + if subpath: + fixup_namespace_packages(subpath, package) + finally: + _imp.release_lock() + + +def file_ns_handler(importer, path_item, packageName, module): + """Compute an ns-package subpath for a filesystem or zipfile importer""" + + subpath = os.path.join(path_item, packageName.split('.')[-1]) + normalized = _normalize_cached(subpath) + for item in module.__path__: + if _normalize_cached(item) == normalized: + break + else: + # Only return the path if it's not already there + return subpath + + +register_namespace_handler(pkgutil.ImpImporter, file_ns_handler) +register_namespace_handler(zipimport.zipimporter, file_ns_handler) + +if hasattr(importlib_machinery, 'FileFinder'): + register_namespace_handler(importlib_machinery.FileFinder, file_ns_handler) + + +def null_ns_handler(importer, path_item, packageName, module): + return None + + +register_namespace_handler(object, null_ns_handler) + + +def normalize_path(filename): + """Normalize a file/dir name for comparison purposes""" + return os.path.normcase(os.path.realpath(os.path.normpath(_cygwin_patch(filename)))) + + +def _cygwin_patch(filename): # pragma: nocover + """ + Contrary to POSIX 2008, on Cygwin, getcwd (3) contains + symlink components. Using + os.path.abspath() works around this limitation. A fix in os.getcwd() + would probably better, in Cygwin even more so, except + that this seems to be by design... + """ + return os.path.abspath(filename) if sys.platform == 'cygwin' else filename + + +def _normalize_cached(filename, _cache={}): + try: + return _cache[filename] + except KeyError: + _cache[filename] = result = normalize_path(filename) + return result + + +def _is_egg_path(path): + """ + Determine if given path appears to be an egg. + """ + return path.lower().endswith('.egg') + + +def _is_unpacked_egg(path): + """ + Determine if given path appears to be an unpacked egg. + """ + return ( + _is_egg_path(path) and + os.path.isfile(os.path.join(path, 'EGG-INFO', 'PKG-INFO')) + ) + + +def _set_parent_ns(packageName): + parts = packageName.split('.') + name = parts.pop() + if parts: + parent = '.'.join(parts) + setattr(sys.modules[parent], name, sys.modules[packageName]) + + +def yield_lines(strs): + """Yield non-empty/non-comment lines of a string or sequence""" + if isinstance(strs, six.string_types): + for s in strs.splitlines(): + s = s.strip() + # skip blank lines/comments + if s and not s.startswith('#'): + yield s + else: + for ss in strs: + for s in yield_lines(ss): + yield s + + +MODULE = re.compile(r"\w+(\.\w+)*$").match +EGG_NAME = re.compile( + r""" + (?P[^-]+) ( + -(?P[^-]+) ( + -py(?P[^-]+) ( + -(?P.+) + )? + )? + )? + """, + re.VERBOSE | re.IGNORECASE, +).match + + +class EntryPoint: + """Object representing an advertised importable object""" + + def __init__(self, name, module_name, attrs=(), extras=(), dist=None): + if not MODULE(module_name): + raise ValueError("Invalid module name", module_name) + self.name = name + self.module_name = module_name + self.attrs = tuple(attrs) + self.extras = tuple(extras) + self.dist = dist + + def __str__(self): + s = "%s = %s" % (self.name, self.module_name) + if self.attrs: + s += ':' + '.'.join(self.attrs) + if self.extras: + s += ' [%s]' % ','.join(self.extras) + return s + + def __repr__(self): + return "EntryPoint.parse(%r)" % str(self) + + def load(self, require=True, *args, **kwargs): + """ + Require packages for this EntryPoint, then resolve it. + """ + if not require or args or kwargs: + warnings.warn( + "Parameters to load are deprecated. Call .resolve and " + ".require separately.", + PkgResourcesDeprecationWarning, + stacklevel=2, + ) + if require: + self.require(*args, **kwargs) + return self.resolve() + + def resolve(self): + """ + Resolve the entry point from its module and attrs. + """ + module = __import__(self.module_name, fromlist=['__name__'], level=0) + try: + return functools.reduce(getattr, self.attrs, module) + except AttributeError as exc: + raise ImportError(str(exc)) + + def require(self, env=None, installer=None): + if self.extras and not self.dist: + raise UnknownExtra("Can't require() without a distribution", self) + + # Get the requirements for this entry point with all its extras and + # then resolve them. We have to pass `extras` along when resolving so + # that the working set knows what extras we want. Otherwise, for + # dist-info distributions, the working set will assume that the + # requirements for that extra are purely optional and skip over them. + reqs = self.dist.requires(self.extras) + items = working_set.resolve(reqs, env, installer, extras=self.extras) + list(map(working_set.add, items)) + + pattern = re.compile( + r'\s*' + r'(?P.+?)\s*' + r'=\s*' + r'(?P[\w.]+)\s*' + r'(:\s*(?P[\w.]+))?\s*' + r'(?P\[.*\])?\s*$' + ) + + @classmethod + def parse(cls, src, dist=None): + """Parse a single entry point from string `src` + + Entry point syntax follows the form:: + + name = some.module:some.attr [extra1, extra2] + + The entry name and module name are required, but the ``:attrs`` and + ``[extras]`` parts are optional + """ + m = cls.pattern.match(src) + if not m: + msg = "EntryPoint must be in 'name=module:attrs [extras]' format" + raise ValueError(msg, src) + res = m.groupdict() + extras = cls._parse_extras(res['extras']) + attrs = res['attr'].split('.') if res['attr'] else () + return cls(res['name'], res['module'], attrs, extras, dist) + + @classmethod + def _parse_extras(cls, extras_spec): + if not extras_spec: + return () + req = Requirement.parse('x' + extras_spec) + if req.specs: + raise ValueError() + return req.extras + + @classmethod + def parse_group(cls, group, lines, dist=None): + """Parse an entry point group""" + if not MODULE(group): + raise ValueError("Invalid group name", group) + this = {} + for line in yield_lines(lines): + ep = cls.parse(line, dist) + if ep.name in this: + raise ValueError("Duplicate entry point", group, ep.name) + this[ep.name] = ep + return this + + @classmethod + def parse_map(cls, data, dist=None): + """Parse a map of entry point groups""" + if isinstance(data, dict): + data = data.items() + else: + data = split_sections(data) + maps = {} + for group, lines in data: + if group is None: + if not lines: + continue + raise ValueError("Entry points must be listed in groups") + group = group.strip() + if group in maps: + raise ValueError("Duplicate group name", group) + maps[group] = cls.parse_group(group, lines, dist) + return maps + + +def _remove_md5_fragment(location): + if not location: + return '' + parsed = urllib.parse.urlparse(location) + if parsed[-1].startswith('md5='): + return urllib.parse.urlunparse(parsed[:-1] + ('',)) + return location + + +def _version_from_file(lines): + """ + Given an iterable of lines from a Metadata file, return + the value of the Version field, if present, or None otherwise. + """ + def is_version_line(line): + return line.lower().startswith('version:') + version_lines = filter(is_version_line, lines) + line = next(iter(version_lines), '') + _, _, value = line.partition(':') + return safe_version(value.strip()) or None + + +class Distribution: + """Wrap an actual or potential sys.path entry w/metadata""" + PKG_INFO = 'PKG-INFO' + + def __init__( + self, location=None, metadata=None, project_name=None, + version=None, py_version=PY_MAJOR, platform=None, + precedence=EGG_DIST): + self.project_name = safe_name(project_name or 'Unknown') + if version is not None: + self._version = safe_version(version) + self.py_version = py_version + self.platform = platform + self.location = location + self.precedence = precedence + self._provider = metadata or empty_provider + + @classmethod + def from_location(cls, location, basename, metadata=None, **kw): + project_name, version, py_version, platform = [None] * 4 + basename, ext = os.path.splitext(basename) + if ext.lower() in _distributionImpl: + cls = _distributionImpl[ext.lower()] + + match = EGG_NAME(basename) + if match: + project_name, version, py_version, platform = match.group( + 'name', 'ver', 'pyver', 'plat' + ) + return cls( + location, metadata, project_name=project_name, version=version, + py_version=py_version, platform=platform, **kw + )._reload_version() + + def _reload_version(self): + return self + + @property + def hashcmp(self): + return ( + self.parsed_version, + self.precedence, + self.key, + _remove_md5_fragment(self.location), + self.py_version or '', + self.platform or '', + ) + + def __hash__(self): + return hash(self.hashcmp) + + def __lt__(self, other): + return self.hashcmp < other.hashcmp + + def __le__(self, other): + return self.hashcmp <= other.hashcmp + + def __gt__(self, other): + return self.hashcmp > other.hashcmp + + def __ge__(self, other): + return self.hashcmp >= other.hashcmp + + def __eq__(self, other): + if not isinstance(other, self.__class__): + # It's not a Distribution, so they are not equal + return False + return self.hashcmp == other.hashcmp + + def __ne__(self, other): + return not self == other + + # These properties have to be lazy so that we don't have to load any + # metadata until/unless it's actually needed. (i.e., some distributions + # may not know their name or version without loading PKG-INFO) + + @property + def key(self): + try: + return self._key + except AttributeError: + self._key = key = self.project_name.lower() + return key + + @property + def parsed_version(self): + if not hasattr(self, "_parsed_version"): + self._parsed_version = parse_version(self.version) + + return self._parsed_version + + def _warn_legacy_version(self): + LV = packaging.version.LegacyVersion + is_legacy = isinstance(self._parsed_version, LV) + if not is_legacy: + return + + # While an empty version is technically a legacy version and + # is not a valid PEP 440 version, it's also unlikely to + # actually come from someone and instead it is more likely that + # it comes from setuptools attempting to parse a filename and + # including it in the list. So for that we'll gate this warning + # on if the version is anything at all or not. + if not self.version: + return + + tmpl = textwrap.dedent(""" + '{project_name} ({version})' is being parsed as a legacy, + non PEP 440, + version. You may find odd behavior and sort order. + In particular it will be sorted as less than 0.0. It + is recommended to migrate to PEP 440 compatible + versions. + """).strip().replace('\n', ' ') + + warnings.warn(tmpl.format(**vars(self)), PEP440Warning) + + @property + def version(self): + try: + return self._version + except AttributeError: + version = self._get_version() + if version is None: + path = self._get_metadata_path_for_display(self.PKG_INFO) + msg = ( + "Missing 'Version:' header and/or {} file at path: {}" + ).format(self.PKG_INFO, path) + raise ValueError(msg, self) + + return version + + @property + def _dep_map(self): + """ + A map of extra to its list of (direct) requirements + for this distribution, including the null extra. + """ + try: + return self.__dep_map + except AttributeError: + self.__dep_map = self._filter_extras(self._build_dep_map()) + return self.__dep_map + + @staticmethod + def _filter_extras(dm): + """ + Given a mapping of extras to dependencies, strip off + environment markers and filter out any dependencies + not matching the markers. + """ + for extra in list(filter(None, dm)): + new_extra = extra + reqs = dm.pop(extra) + new_extra, _, marker = extra.partition(':') + fails_marker = marker and ( + invalid_marker(marker) + or not evaluate_marker(marker) + ) + if fails_marker: + reqs = [] + new_extra = safe_extra(new_extra) or None + + dm.setdefault(new_extra, []).extend(reqs) + return dm + + def _build_dep_map(self): + dm = {} + for name in 'requires.txt', 'depends.txt': + for extra, reqs in split_sections(self._get_metadata(name)): + dm.setdefault(extra, []).extend(parse_requirements(reqs)) + return dm + + def requires(self, extras=()): + """List of Requirements needed for this distro if `extras` are used""" + dm = self._dep_map + deps = [] + deps.extend(dm.get(None, ())) + for ext in extras: + try: + deps.extend(dm[safe_extra(ext)]) + except KeyError: + raise UnknownExtra( + "%s has no such extra feature %r" % (self, ext) + ) + return deps + + def _get_metadata_path_for_display(self, name): + """ + Return the path to the given metadata file, if available. + """ + try: + # We need to access _get_metadata_path() on the provider object + # directly rather than through this class's __getattr__() + # since _get_metadata_path() is marked private. + path = self._provider._get_metadata_path(name) + + # Handle exceptions e.g. in case the distribution's metadata + # provider doesn't support _get_metadata_path(). + except Exception: + return '[could not detect]' + + return path + + def _get_metadata(self, name): + if self.has_metadata(name): + for line in self.get_metadata_lines(name): + yield line + + def _get_version(self): + lines = self._get_metadata(self.PKG_INFO) + version = _version_from_file(lines) + + return version + + def activate(self, path=None, replace=False): + """Ensure distribution is importable on `path` (default=sys.path)""" + if path is None: + path = sys.path + self.insert_on(path, replace=replace) + if path is sys.path: + fixup_namespace_packages(self.location) + for pkg in self._get_metadata('namespace_packages.txt'): + if pkg in sys.modules: + declare_namespace(pkg) + + def egg_name(self): + """Return what this distribution's standard .egg filename should be""" + filename = "%s-%s-py%s" % ( + to_filename(self.project_name), to_filename(self.version), + self.py_version or PY_MAJOR + ) + + if self.platform: + filename += '-' + self.platform + return filename + + def __repr__(self): + if self.location: + return "%s (%s)" % (self, self.location) + else: + return str(self) + + def __str__(self): + try: + version = getattr(self, 'version', None) + except ValueError: + version = None + version = version or "[unknown version]" + return "%s %s" % (self.project_name, version) + + def __getattr__(self, attr): + """Delegate all unrecognized public attributes to .metadata provider""" + if attr.startswith('_'): + raise AttributeError(attr) + return getattr(self._provider, attr) + + def __dir__(self): + return list( + set(super(Distribution, self).__dir__()) + | set( + attr for attr in self._provider.__dir__() + if not attr.startswith('_') + ) + ) + + if not hasattr(object, '__dir__'): + # python 2.7 not supported + del __dir__ + + @classmethod + def from_filename(cls, filename, metadata=None, **kw): + return cls.from_location( + _normalize_cached(filename), os.path.basename(filename), metadata, + **kw + ) + + def as_requirement(self): + """Return a ``Requirement`` that matches this distribution exactly""" + if isinstance(self.parsed_version, packaging.version.Version): + spec = "%s==%s" % (self.project_name, self.parsed_version) + else: + spec = "%s===%s" % (self.project_name, self.parsed_version) + + return Requirement.parse(spec) + + def load_entry_point(self, group, name): + """Return the `name` entry point of `group` or raise ImportError""" + ep = self.get_entry_info(group, name) + if ep is None: + raise ImportError("Entry point %r not found" % ((group, name),)) + return ep.load() + + def get_entry_map(self, group=None): + """Return the entry point map for `group`, or the full entry map""" + try: + ep_map = self._ep_map + except AttributeError: + ep_map = self._ep_map = EntryPoint.parse_map( + self._get_metadata('entry_points.txt'), self + ) + if group is not None: + return ep_map.get(group, {}) + return ep_map + + def get_entry_info(self, group, name): + """Return the EntryPoint object for `group`+`name`, or ``None``""" + return self.get_entry_map(group).get(name) + + def insert_on(self, path, loc=None, replace=False): + """Ensure self.location is on path + + If replace=False (default): + - If location is already in path anywhere, do nothing. + - Else: + - If it's an egg and its parent directory is on path, + insert just ahead of the parent. + - Else: add to the end of path. + If replace=True: + - If location is already on path anywhere (not eggs) + or higher priority than its parent (eggs) + do nothing. + - Else: + - If it's an egg and its parent directory is on path, + insert just ahead of the parent, + removing any lower-priority entries. + - Else: add it to the front of path. + """ + + loc = loc or self.location + if not loc: + return + + nloc = _normalize_cached(loc) + bdir = os.path.dirname(nloc) + npath = [(p and _normalize_cached(p) or p) for p in path] + + for p, item in enumerate(npath): + if item == nloc: + if replace: + break + else: + # don't modify path (even removing duplicates) if + # found and not replace + return + elif item == bdir and self.precedence == EGG_DIST: + # if it's an .egg, give it precedence over its directory + # UNLESS it's already been added to sys.path and replace=False + if (not replace) and nloc in npath[p:]: + return + if path is sys.path: + self.check_version_conflict() + path.insert(p, loc) + npath.insert(p, nloc) + break + else: + if path is sys.path: + self.check_version_conflict() + if replace: + path.insert(0, loc) + else: + path.append(loc) + return + + # p is the spot where we found or inserted loc; now remove duplicates + while True: + try: + np = npath.index(nloc, p + 1) + except ValueError: + break + else: + del npath[np], path[np] + # ha! + p = np + + return + + def check_version_conflict(self): + if self.key == 'setuptools': + # ignore the inevitable setuptools self-conflicts :( + return + + nsp = dict.fromkeys(self._get_metadata('namespace_packages.txt')) + loc = normalize_path(self.location) + for modname in self._get_metadata('top_level.txt'): + if (modname not in sys.modules or modname in nsp + or modname in _namespace_packages): + continue + if modname in ('pkg_resources', 'setuptools', 'site'): + continue + fn = getattr(sys.modules[modname], '__file__', None) + if fn and (normalize_path(fn).startswith(loc) or + fn.startswith(self.location)): + continue + issue_warning( + "Module %s was already imported from %s, but %s is being added" + " to sys.path" % (modname, fn, self.location), + ) + + def has_version(self): + try: + self.version + except ValueError: + issue_warning("Unbuilt egg for " + repr(self)) + return False + return True + + def clone(self, **kw): + """Copy this distribution, substituting in any changed keyword args""" + names = 'project_name version py_version platform location precedence' + for attr in names.split(): + kw.setdefault(attr, getattr(self, attr, None)) + kw.setdefault('metadata', self._provider) + return self.__class__(**kw) + + @property + def extras(self): + return [dep for dep in self._dep_map if dep] + + +class EggInfoDistribution(Distribution): + def _reload_version(self): + """ + Packages installed by distutils (e.g. numpy or scipy), + which uses an old safe_version, and so + their version numbers can get mangled when + converted to filenames (e.g., 1.11.0.dev0+2329eae to + 1.11.0.dev0_2329eae). These distributions will not be + parsed properly + downstream by Distribution and safe_version, so + take an extra step and try to get the version number from + the metadata file itself instead of the filename. + """ + md_version = self._get_version() + if md_version: + self._version = md_version + return self + + +class DistInfoDistribution(Distribution): + """ + Wrap an actual or potential sys.path entry + w/metadata, .dist-info style. + """ + PKG_INFO = 'METADATA' + EQEQ = re.compile(r"([\(,])\s*(\d.*?)\s*([,\)])") + + @property + def _parsed_pkg_info(self): + """Parse and cache metadata""" + try: + return self._pkg_info + except AttributeError: + metadata = self.get_metadata(self.PKG_INFO) + self._pkg_info = email.parser.Parser().parsestr(metadata) + return self._pkg_info + + @property + def _dep_map(self): + try: + return self.__dep_map + except AttributeError: + self.__dep_map = self._compute_dependencies() + return self.__dep_map + + def _compute_dependencies(self): + """Recompute this distribution's dependencies.""" + dm = self.__dep_map = {None: []} + + reqs = [] + # Including any condition expressions + for req in self._parsed_pkg_info.get_all('Requires-Dist') or []: + reqs.extend(parse_requirements(req)) + + def reqs_for_extra(extra): + for req in reqs: + if not req.marker or req.marker.evaluate({'extra': extra}): + yield req + + common = frozenset(reqs_for_extra(None)) + dm[None].extend(common) + + for extra in self._parsed_pkg_info.get_all('Provides-Extra') or []: + s_extra = safe_extra(extra.strip()) + dm[s_extra] = list(frozenset(reqs_for_extra(extra)) - common) + + return dm + + +_distributionImpl = { + '.egg': Distribution, + '.egg-info': EggInfoDistribution, + '.dist-info': DistInfoDistribution, +} + + +def issue_warning(*args, **kw): + level = 1 + g = globals() + try: + # find the first stack frame that is *not* code in + # the pkg_resources module, to use for the warning + while sys._getframe(level).f_globals is g: + level += 1 + except ValueError: + pass + warnings.warn(stacklevel=level + 1, *args, **kw) + + +class RequirementParseError(ValueError): + def __str__(self): + return ' '.join(self.args) + + +def parse_requirements(strs): + """Yield ``Requirement`` objects for each specification in `strs` + + `strs` must be a string, or a (possibly-nested) iterable thereof. + """ + # create a steppable iterator, so we can handle \-continuations + lines = iter(yield_lines(strs)) + + for line in lines: + # Drop comments -- a hash without a space may be in a URL. + if ' #' in line: + line = line[:line.find(' #')] + # If there is a line continuation, drop it, and append the next line. + if line.endswith('\\'): + line = line[:-2].strip() + try: + line += next(lines) + except StopIteration: + return + yield Requirement(line) + + +class Requirement(packaging.requirements.Requirement): + def __init__(self, requirement_string): + """DO NOT CALL THIS UNDOCUMENTED METHOD; use Requirement.parse()!""" + try: + super(Requirement, self).__init__(requirement_string) + except packaging.requirements.InvalidRequirement as e: + raise RequirementParseError(str(e)) + self.unsafe_name = self.name + project_name = safe_name(self.name) + self.project_name, self.key = project_name, project_name.lower() + self.specs = [ + (spec.operator, spec.version) for spec in self.specifier] + self.extras = tuple(map(safe_extra, self.extras)) + self.hashCmp = ( + self.key, + self.url, + self.specifier, + frozenset(self.extras), + str(self.marker) if self.marker else None, + ) + self.__hash = hash(self.hashCmp) + + def __eq__(self, other): + return ( + isinstance(other, Requirement) and + self.hashCmp == other.hashCmp + ) + + def __ne__(self, other): + return not self == other + + def __contains__(self, item): + if isinstance(item, Distribution): + if item.key != self.key: + return False + + item = item.version + + # Allow prereleases always in order to match the previous behavior of + # this method. In the future this should be smarter and follow PEP 440 + # more accurately. + return self.specifier.contains(item, prereleases=True) + + def __hash__(self): + return self.__hash + + def __repr__(self): + return "Requirement.parse(%r)" % str(self) + + @staticmethod + def parse(s): + req, = parse_requirements(s) + return req + + +def _always_object(classes): + """ + Ensure object appears in the mro even + for old-style classes. + """ + if object not in classes: + return classes + (object,) + return classes + + +def _find_adapter(registry, ob): + """Return an adapter factory for `ob` from `registry`""" + types = _always_object(inspect.getmro(getattr(ob, '__class__', type(ob)))) + for t in types: + if t in registry: + return registry[t] + + +def ensure_directory(path): + """Ensure that the parent directory of `path` exists""" + dirname = os.path.dirname(path) + py31compat.makedirs(dirname, exist_ok=True) + + +def _bypass_ensure_directory(path): + """Sandbox-bypassing version of ensure_directory()""" + if not WRITE_SUPPORT: + raise IOError('"os.mkdir" not supported on this platform.') + dirname, filename = split(path) + if dirname and filename and not isdir(dirname): + _bypass_ensure_directory(dirname) + try: + mkdir(dirname, 0o755) + except FileExistsError: + pass + + +def split_sections(s): + """Split a string or iterable thereof into (section, content) pairs + + Each ``section`` is a stripped version of the section header ("[section]") + and each ``content`` is a list of stripped lines excluding blank lines and + comment-only lines. If there are any such lines before the first section + header, they're returned in a first ``section`` of ``None``. + """ + section = None + content = [] + for line in yield_lines(s): + if line.startswith("["): + if line.endswith("]"): + if section or content: + yield section, content + section = line[1:-1].strip() + content = [] + else: + raise ValueError("Invalid section heading", line) + else: + content.append(line) + + # wrap up last segment + yield section, content + + +def _mkstemp(*args, **kw): + old_open = os.open + try: + # temporarily bypass sandboxing + os.open = os_open + return tempfile.mkstemp(*args, **kw) + finally: + # and then put it back + os.open = old_open + + +# Silence the PEP440Warning by default, so that end users don't get hit by it +# randomly just because they use pkg_resources. We want to append the rule +# because we want earlier uses of filterwarnings to take precedence over this +# one. +warnings.filterwarnings("ignore", category=PEP440Warning, append=True) + + +# from jaraco.functools 1.3 +def _call_aside(f, *args, **kwargs): + f(*args, **kwargs) + return f + + +@_call_aside +def _initialize(g=globals()): + "Set up global resource manager (deliberately not state-saved)" + manager = ResourceManager() + g['_manager'] = manager + g.update( + (name, getattr(manager, name)) + for name in dir(manager) + if not name.startswith('_') + ) + + +@_call_aside +def _initialize_master_working_set(): + """ + Prepare the master working set and make the ``require()`` + API available. + + This function has explicit effects on the global state + of pkg_resources. It is intended to be invoked once at + the initialization of this module. + + Invocation by other packages is unsupported and done + at their own risk. + """ + working_set = WorkingSet._build_master() + _declare_state('object', working_set=working_set) + + require = working_set.require + iter_entry_points = working_set.iter_entry_points + add_activation_listener = working_set.subscribe + run_script = working_set.run_script + # backward compatibility + run_main = run_script + # Activate all distributions already on sys.path with replace=False and + # ensure that all distributions added to the working set in the future + # (e.g. by calling ``require()``) will get activated as well, + # with higher priority (replace=True). + tuple( + dist.activate(replace=False) + for dist in working_set + ) + add_activation_listener( + lambda dist: dist.activate(replace=True), + existing=False, + ) + working_set.entries = [] + # match order + list(map(working_set.add_entry, sys.path)) + globals().update(locals()) + +class PkgResourcesDeprecationWarning(Warning): + """ + Base class for warning about deprecations in ``pkg_resources`` + + This class is not derived from ``DeprecationWarning``, and as such is + visible by default. + """ diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/__init__.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/appdirs.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/appdirs.py new file mode 100644 index 0000000000..ae67001af8 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/appdirs.py @@ -0,0 +1,608 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (c) 2005-2010 ActiveState Software Inc. +# Copyright (c) 2013 Eddy Petrișor + +"""Utilities for determining application-specific dirs. + +See for details and usage. +""" +# Dev Notes: +# - MSDN on where to store app data files: +# http://support.microsoft.com/default.aspx?scid=kb;en-us;310294#XSLTH3194121123120121120120 +# - Mac OS X: http://developer.apple.com/documentation/MacOSX/Conceptual/BPFileSystem/index.html +# - XDG spec for Un*x: http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html + +__version_info__ = (1, 4, 3) +__version__ = '.'.join(map(str, __version_info__)) + + +import sys +import os + +PY3 = sys.version_info[0] == 3 + +if PY3: + unicode = str + +if sys.platform.startswith('java'): + import platform + os_name = platform.java_ver()[3][0] + if os_name.startswith('Windows'): # "Windows XP", "Windows 7", etc. + system = 'win32' + elif os_name.startswith('Mac'): # "Mac OS X", etc. + system = 'darwin' + else: # "Linux", "SunOS", "FreeBSD", etc. + # Setting this to "linux2" is not ideal, but only Windows or Mac + # are actually checked for and the rest of the module expects + # *sys.platform* style strings. + system = 'linux2' +else: + system = sys.platform + + + +def user_data_dir(appname=None, appauthor=None, version=None, roaming=False): + r"""Return full path to the user-specific data dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be ".". + Only applied when appname is present. + "roaming" (boolean, default False) can be set True to use the Windows + roaming appdata directory. That means that for users on a Windows + network setup for roaming profiles, this user data will be + sync'd on login. See + + for a discussion of issues. + + Typical user data directories are: + Mac OS X: ~/Library/Application Support/ + Unix: ~/.local/share/ # or in $XDG_DATA_HOME, if defined + Win XP (not roaming): C:\Documents and Settings\\Application Data\\ + Win XP (roaming): C:\Documents and Settings\\Local Settings\Application Data\\ + Win 7 (not roaming): C:\Users\\AppData\Local\\ + Win 7 (roaming): C:\Users\\AppData\Roaming\\ + + For Unix, we follow the XDG spec and support $XDG_DATA_HOME. + That means, by default "~/.local/share/". + """ + if system == "win32": + if appauthor is None: + appauthor = appname + const = roaming and "CSIDL_APPDATA" or "CSIDL_LOCAL_APPDATA" + path = os.path.normpath(_get_win_folder(const)) + if appname: + if appauthor is not False: + path = os.path.join(path, appauthor, appname) + else: + path = os.path.join(path, appname) + elif system == 'darwin': + path = os.path.expanduser('~/Library/Application Support/') + if appname: + path = os.path.join(path, appname) + else: + path = os.getenv('XDG_DATA_HOME', os.path.expanduser("~/.local/share")) + if appname: + path = os.path.join(path, appname) + if appname and version: + path = os.path.join(path, version) + return path + + +def site_data_dir(appname=None, appauthor=None, version=None, multipath=False): + r"""Return full path to the user-shared data dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be ".". + Only applied when appname is present. + "multipath" is an optional parameter only applicable to *nix + which indicates that the entire list of data dirs should be + returned. By default, the first item from XDG_DATA_DIRS is + returned, or '/usr/local/share/', + if XDG_DATA_DIRS is not set + + Typical site data directories are: + Mac OS X: /Library/Application Support/ + Unix: /usr/local/share/ or /usr/share/ + Win XP: C:\Documents and Settings\All Users\Application Data\\ + Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.) + Win 7: C:\ProgramData\\ # Hidden, but writeable on Win 7. + + For Unix, this is using the $XDG_DATA_DIRS[0] default. + + WARNING: Do not use this on Windows. See the Vista-Fail note above for why. + """ + if system == "win32": + if appauthor is None: + appauthor = appname + path = os.path.normpath(_get_win_folder("CSIDL_COMMON_APPDATA")) + if appname: + if appauthor is not False: + path = os.path.join(path, appauthor, appname) + else: + path = os.path.join(path, appname) + elif system == 'darwin': + path = os.path.expanduser('/Library/Application Support') + if appname: + path = os.path.join(path, appname) + else: + # XDG default for $XDG_DATA_DIRS + # only first, if multipath is False + path = os.getenv('XDG_DATA_DIRS', + os.pathsep.join(['/usr/local/share', '/usr/share'])) + pathlist = [os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep)] + if appname: + if version: + appname = os.path.join(appname, version) + pathlist = [os.sep.join([x, appname]) for x in pathlist] + + if multipath: + path = os.pathsep.join(pathlist) + else: + path = pathlist[0] + return path + + if appname and version: + path = os.path.join(path, version) + return path + + +def user_config_dir(appname=None, appauthor=None, version=None, roaming=False): + r"""Return full path to the user-specific config dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be ".". + Only applied when appname is present. + "roaming" (boolean, default False) can be set True to use the Windows + roaming appdata directory. That means that for users on a Windows + network setup for roaming profiles, this user data will be + sync'd on login. See + + for a discussion of issues. + + Typical user config directories are: + Mac OS X: same as user_data_dir + Unix: ~/.config/ # or in $XDG_CONFIG_HOME, if defined + Win *: same as user_data_dir + + For Unix, we follow the XDG spec and support $XDG_CONFIG_HOME. + That means, by default "~/.config/". + """ + if system in ["win32", "darwin"]: + path = user_data_dir(appname, appauthor, None, roaming) + else: + path = os.getenv('XDG_CONFIG_HOME', os.path.expanduser("~/.config")) + if appname: + path = os.path.join(path, appname) + if appname and version: + path = os.path.join(path, version) + return path + + +def site_config_dir(appname=None, appauthor=None, version=None, multipath=False): + r"""Return full path to the user-shared data dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be ".". + Only applied when appname is present. + "multipath" is an optional parameter only applicable to *nix + which indicates that the entire list of config dirs should be + returned. By default, the first item from XDG_CONFIG_DIRS is + returned, or '/etc/xdg/', if XDG_CONFIG_DIRS is not set + + Typical site config directories are: + Mac OS X: same as site_data_dir + Unix: /etc/xdg/ or $XDG_CONFIG_DIRS[i]/ for each value in + $XDG_CONFIG_DIRS + Win *: same as site_data_dir + Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.) + + For Unix, this is using the $XDG_CONFIG_DIRS[0] default, if multipath=False + + WARNING: Do not use this on Windows. See the Vista-Fail note above for why. + """ + if system in ["win32", "darwin"]: + path = site_data_dir(appname, appauthor) + if appname and version: + path = os.path.join(path, version) + else: + # XDG default for $XDG_CONFIG_DIRS + # only first, if multipath is False + path = os.getenv('XDG_CONFIG_DIRS', '/etc/xdg') + pathlist = [os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep)] + if appname: + if version: + appname = os.path.join(appname, version) + pathlist = [os.sep.join([x, appname]) for x in pathlist] + + if multipath: + path = os.pathsep.join(pathlist) + else: + path = pathlist[0] + return path + + +def user_cache_dir(appname=None, appauthor=None, version=None, opinion=True): + r"""Return full path to the user-specific cache dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be ".". + Only applied when appname is present. + "opinion" (boolean) can be False to disable the appending of + "Cache" to the base app data dir for Windows. See + discussion below. + + Typical user cache directories are: + Mac OS X: ~/Library/Caches/ + Unix: ~/.cache/ (XDG default) + Win XP: C:\Documents and Settings\\Local Settings\Application Data\\\Cache + Vista: C:\Users\\AppData\Local\\\Cache + + On Windows the only suggestion in the MSDN docs is that local settings go in + the `CSIDL_LOCAL_APPDATA` directory. This is identical to the non-roaming + app data dir (the default returned by `user_data_dir` above). Apps typically + put cache data somewhere *under* the given dir here. Some examples: + ...\Mozilla\Firefox\Profiles\\Cache + ...\Acme\SuperApp\Cache\1.0 + OPINION: This function appends "Cache" to the `CSIDL_LOCAL_APPDATA` value. + This can be disabled with the `opinion=False` option. + """ + if system == "win32": + if appauthor is None: + appauthor = appname + path = os.path.normpath(_get_win_folder("CSIDL_LOCAL_APPDATA")) + if appname: + if appauthor is not False: + path = os.path.join(path, appauthor, appname) + else: + path = os.path.join(path, appname) + if opinion: + path = os.path.join(path, "Cache") + elif system == 'darwin': + path = os.path.expanduser('~/Library/Caches') + if appname: + path = os.path.join(path, appname) + else: + path = os.getenv('XDG_CACHE_HOME', os.path.expanduser('~/.cache')) + if appname: + path = os.path.join(path, appname) + if appname and version: + path = os.path.join(path, version) + return path + + +def user_state_dir(appname=None, appauthor=None, version=None, roaming=False): + r"""Return full path to the user-specific state dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be ".". + Only applied when appname is present. + "roaming" (boolean, default False) can be set True to use the Windows + roaming appdata directory. That means that for users on a Windows + network setup for roaming profiles, this user data will be + sync'd on login. See + + for a discussion of issues. + + Typical user state directories are: + Mac OS X: same as user_data_dir + Unix: ~/.local/state/ # or in $XDG_STATE_HOME, if defined + Win *: same as user_data_dir + + For Unix, we follow this Debian proposal + to extend the XDG spec and support $XDG_STATE_HOME. + + That means, by default "~/.local/state/". + """ + if system in ["win32", "darwin"]: + path = user_data_dir(appname, appauthor, None, roaming) + else: + path = os.getenv('XDG_STATE_HOME', os.path.expanduser("~/.local/state")) + if appname: + path = os.path.join(path, appname) + if appname and version: + path = os.path.join(path, version) + return path + + +def user_log_dir(appname=None, appauthor=None, version=None, opinion=True): + r"""Return full path to the user-specific log dir for this application. + + "appname" is the name of application. + If None, just the system directory is returned. + "appauthor" (only used on Windows) is the name of the + appauthor or distributing body for this application. Typically + it is the owning company name. This falls back to appname. You may + pass False to disable it. + "version" is an optional version path element to append to the + path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this + would typically be ".". + Only applied when appname is present. + "opinion" (boolean) can be False to disable the appending of + "Logs" to the base app data dir for Windows, and "log" to the + base cache dir for Unix. See discussion below. + + Typical user log directories are: + Mac OS X: ~/Library/Logs/ + Unix: ~/.cache//log # or under $XDG_CACHE_HOME if defined + Win XP: C:\Documents and Settings\\Local Settings\Application Data\\\Logs + Vista: C:\Users\\AppData\Local\\\Logs + + On Windows the only suggestion in the MSDN docs is that local settings + go in the `CSIDL_LOCAL_APPDATA` directory. (Note: I'm interested in + examples of what some windows apps use for a logs dir.) + + OPINION: This function appends "Logs" to the `CSIDL_LOCAL_APPDATA` + value for Windows and appends "log" to the user cache dir for Unix. + This can be disabled with the `opinion=False` option. + """ + if system == "darwin": + path = os.path.join( + os.path.expanduser('~/Library/Logs'), + appname) + elif system == "win32": + path = user_data_dir(appname, appauthor, version) + version = False + if opinion: + path = os.path.join(path, "Logs") + else: + path = user_cache_dir(appname, appauthor, version) + version = False + if opinion: + path = os.path.join(path, "log") + if appname and version: + path = os.path.join(path, version) + return path + + +class AppDirs(object): + """Convenience wrapper for getting application dirs.""" + def __init__(self, appname=None, appauthor=None, version=None, + roaming=False, multipath=False): + self.appname = appname + self.appauthor = appauthor + self.version = version + self.roaming = roaming + self.multipath = multipath + + @property + def user_data_dir(self): + return user_data_dir(self.appname, self.appauthor, + version=self.version, roaming=self.roaming) + + @property + def site_data_dir(self): + return site_data_dir(self.appname, self.appauthor, + version=self.version, multipath=self.multipath) + + @property + def user_config_dir(self): + return user_config_dir(self.appname, self.appauthor, + version=self.version, roaming=self.roaming) + + @property + def site_config_dir(self): + return site_config_dir(self.appname, self.appauthor, + version=self.version, multipath=self.multipath) + + @property + def user_cache_dir(self): + return user_cache_dir(self.appname, self.appauthor, + version=self.version) + + @property + def user_state_dir(self): + return user_state_dir(self.appname, self.appauthor, + version=self.version) + + @property + def user_log_dir(self): + return user_log_dir(self.appname, self.appauthor, + version=self.version) + + +#---- internal support stuff + +def _get_win_folder_from_registry(csidl_name): + """This is a fallback technique at best. I'm not sure if using the + registry for this guarantees us the correct answer for all CSIDL_* + names. + """ + if PY3: + import winreg as _winreg + else: + import _winreg + + shell_folder_name = { + "CSIDL_APPDATA": "AppData", + "CSIDL_COMMON_APPDATA": "Common AppData", + "CSIDL_LOCAL_APPDATA": "Local AppData", + }[csidl_name] + + key = _winreg.OpenKey( + _winreg.HKEY_CURRENT_USER, + r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders" + ) + dir, type = _winreg.QueryValueEx(key, shell_folder_name) + return dir + + +def _get_win_folder_with_pywin32(csidl_name): + from win32com.shell import shellcon, shell + dir = shell.SHGetFolderPath(0, getattr(shellcon, csidl_name), 0, 0) + # Try to make this a unicode path because SHGetFolderPath does + # not return unicode strings when there is unicode data in the + # path. + try: + dir = unicode(dir) + + # Downgrade to short path name if have highbit chars. See + # . + has_high_char = False + for c in dir: + if ord(c) > 255: + has_high_char = True + break + if has_high_char: + try: + import win32api + dir = win32api.GetShortPathName(dir) + except ImportError: + pass + except UnicodeError: + pass + return dir + + +def _get_win_folder_with_ctypes(csidl_name): + import ctypes + + csidl_const = { + "CSIDL_APPDATA": 26, + "CSIDL_COMMON_APPDATA": 35, + "CSIDL_LOCAL_APPDATA": 28, + }[csidl_name] + + buf = ctypes.create_unicode_buffer(1024) + ctypes.windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf) + + # Downgrade to short path name if have highbit chars. See + # . + has_high_char = False + for c in buf: + if ord(c) > 255: + has_high_char = True + break + if has_high_char: + buf2 = ctypes.create_unicode_buffer(1024) + if ctypes.windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024): + buf = buf2 + + return buf.value + +def _get_win_folder_with_jna(csidl_name): + import array + from com.sun import jna + from com.sun.jna.platform import win32 + + buf_size = win32.WinDef.MAX_PATH * 2 + buf = array.zeros('c', buf_size) + shell = win32.Shell32.INSTANCE + shell.SHGetFolderPath(None, getattr(win32.ShlObj, csidl_name), None, win32.ShlObj.SHGFP_TYPE_CURRENT, buf) + dir = jna.Native.toString(buf.tostring()).rstrip("\0") + + # Downgrade to short path name if have highbit chars. See + # . + has_high_char = False + for c in dir: + if ord(c) > 255: + has_high_char = True + break + if has_high_char: + buf = array.zeros('c', buf_size) + kernel = win32.Kernel32.INSTANCE + if kernel.GetShortPathName(dir, buf, buf_size): + dir = jna.Native.toString(buf.tostring()).rstrip("\0") + + return dir + +if system == "win32": + try: + import win32com.shell + _get_win_folder = _get_win_folder_with_pywin32 + except ImportError: + try: + from ctypes import windll + _get_win_folder = _get_win_folder_with_ctypes + except ImportError: + try: + import com.sun.jna + _get_win_folder = _get_win_folder_with_jna + except ImportError: + _get_win_folder = _get_win_folder_from_registry + + +#---- self test code + +if __name__ == "__main__": + appname = "MyApp" + appauthor = "MyCompany" + + props = ("user_data_dir", + "user_config_dir", + "user_cache_dir", + "user_state_dir", + "user_log_dir", + "site_data_dir", + "site_config_dir") + + print("-- app dirs %s --" % __version__) + + print("-- app dirs (with optional 'version')") + dirs = AppDirs(appname, appauthor, version="1.0") + for prop in props: + print("%s: %s" % (prop, getattr(dirs, prop))) + + print("\n-- app dirs (without optional 'version')") + dirs = AppDirs(appname, appauthor) + for prop in props: + print("%s: %s" % (prop, getattr(dirs, prop))) + + print("\n-- app dirs (without optional 'appauthor')") + dirs = AppDirs(appname) + for prop in props: + print("%s: %s" % (prop, getattr(dirs, prop))) + + print("\n-- app dirs (with disabled 'appauthor')") + dirs = AppDirs(appname, appauthor=False) + for prop in props: + print("%s: %s" % (prop, getattr(dirs, prop))) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__about__.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__about__.py new file mode 100644 index 0000000000..95d330ef82 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__about__.py @@ -0,0 +1,21 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +__all__ = [ + "__title__", "__summary__", "__uri__", "__version__", "__author__", + "__email__", "__license__", "__copyright__", +] + +__title__ = "packaging" +__summary__ = "Core utilities for Python packages" +__uri__ = "https://github.com/pypa/packaging" + +__version__ = "16.8" + +__author__ = "Donald Stufft and individual contributors" +__email__ = "donald@stufft.io" + +__license__ = "BSD or Apache License, Version 2.0" +__copyright__ = "Copyright 2014-2016 %s" % __author__ diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__init__.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__init__.py new file mode 100644 index 0000000000..5ee6220203 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__init__.py @@ -0,0 +1,14 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +from .__about__ import ( + __author__, __copyright__, __email__, __license__, __summary__, __title__, + __uri__, __version__ +) + +__all__ = [ + "__title__", "__summary__", "__uri__", "__version__", "__author__", + "__email__", "__license__", "__copyright__", +] diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/_compat.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/_compat.py new file mode 100644 index 0000000000..210bb80b7e --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/_compat.py @@ -0,0 +1,30 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +import sys + + +PY2 = sys.version_info[0] == 2 +PY3 = sys.version_info[0] == 3 + +# flake8: noqa + +if PY3: + string_types = str, +else: + string_types = basestring, + + +def with_metaclass(meta, *bases): + """ + Create a base class with a metaclass. + """ + # This requires a bit of explanation: the basic idea is to make a dummy + # metaclass for one level of class instantiation that replaces itself with + # the actual metaclass. + class metaclass(meta): + def __new__(cls, name, this_bases, d): + return meta(name, bases, d) + return type.__new__(metaclass, 'temporary_class', (), {}) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/_structures.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/_structures.py new file mode 100644 index 0000000000..ccc27861c3 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/_structures.py @@ -0,0 +1,68 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + + +class Infinity(object): + + def __repr__(self): + return "Infinity" + + def __hash__(self): + return hash(repr(self)) + + def __lt__(self, other): + return False + + def __le__(self, other): + return False + + def __eq__(self, other): + return isinstance(other, self.__class__) + + def __ne__(self, other): + return not isinstance(other, self.__class__) + + def __gt__(self, other): + return True + + def __ge__(self, other): + return True + + def __neg__(self): + return NegativeInfinity + +Infinity = Infinity() + + +class NegativeInfinity(object): + + def __repr__(self): + return "-Infinity" + + def __hash__(self): + return hash(repr(self)) + + def __lt__(self, other): + return True + + def __le__(self, other): + return True + + def __eq__(self, other): + return isinstance(other, self.__class__) + + def __ne__(self, other): + return not isinstance(other, self.__class__) + + def __gt__(self, other): + return False + + def __ge__(self, other): + return False + + def __neg__(self): + return Infinity + +NegativeInfinity = NegativeInfinity() diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/markers.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/markers.py new file mode 100644 index 0000000000..892e578edd --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/markers.py @@ -0,0 +1,301 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +import operator +import os +import platform +import sys + +from pkg_resources.extern.pyparsing import ParseException, ParseResults, stringStart, stringEnd +from pkg_resources.extern.pyparsing import ZeroOrMore, Group, Forward, QuotedString +from pkg_resources.extern.pyparsing import Literal as L # noqa + +from ._compat import string_types +from .specifiers import Specifier, InvalidSpecifier + + +__all__ = [ + "InvalidMarker", "UndefinedComparison", "UndefinedEnvironmentName", + "Marker", "default_environment", +] + + +class InvalidMarker(ValueError): + """ + An invalid marker was found, users should refer to PEP 508. + """ + + +class UndefinedComparison(ValueError): + """ + An invalid operation was attempted on a value that doesn't support it. + """ + + +class UndefinedEnvironmentName(ValueError): + """ + A name was attempted to be used that does not exist inside of the + environment. + """ + + +class Node(object): + + def __init__(self, value): + self.value = value + + def __str__(self): + return str(self.value) + + def __repr__(self): + return "<{0}({1!r})>".format(self.__class__.__name__, str(self)) + + def serialize(self): + raise NotImplementedError + + +class Variable(Node): + + def serialize(self): + return str(self) + + +class Value(Node): + + def serialize(self): + return '"{0}"'.format(self) + + +class Op(Node): + + def serialize(self): + return str(self) + + +VARIABLE = ( + L("implementation_version") | + L("platform_python_implementation") | + L("implementation_name") | + L("python_full_version") | + L("platform_release") | + L("platform_version") | + L("platform_machine") | + L("platform_system") | + L("python_version") | + L("sys_platform") | + L("os_name") | + L("os.name") | # PEP-345 + L("sys.platform") | # PEP-345 + L("platform.version") | # PEP-345 + L("platform.machine") | # PEP-345 + L("platform.python_implementation") | # PEP-345 + L("python_implementation") | # undocumented setuptools legacy + L("extra") +) +ALIASES = { + 'os.name': 'os_name', + 'sys.platform': 'sys_platform', + 'platform.version': 'platform_version', + 'platform.machine': 'platform_machine', + 'platform.python_implementation': 'platform_python_implementation', + 'python_implementation': 'platform_python_implementation' +} +VARIABLE.setParseAction(lambda s, l, t: Variable(ALIASES.get(t[0], t[0]))) + +VERSION_CMP = ( + L("===") | + L("==") | + L(">=") | + L("<=") | + L("!=") | + L("~=") | + L(">") | + L("<") +) + +MARKER_OP = VERSION_CMP | L("not in") | L("in") +MARKER_OP.setParseAction(lambda s, l, t: Op(t[0])) + +MARKER_VALUE = QuotedString("'") | QuotedString('"') +MARKER_VALUE.setParseAction(lambda s, l, t: Value(t[0])) + +BOOLOP = L("and") | L("or") + +MARKER_VAR = VARIABLE | MARKER_VALUE + +MARKER_ITEM = Group(MARKER_VAR + MARKER_OP + MARKER_VAR) +MARKER_ITEM.setParseAction(lambda s, l, t: tuple(t[0])) + +LPAREN = L("(").suppress() +RPAREN = L(")").suppress() + +MARKER_EXPR = Forward() +MARKER_ATOM = MARKER_ITEM | Group(LPAREN + MARKER_EXPR + RPAREN) +MARKER_EXPR << MARKER_ATOM + ZeroOrMore(BOOLOP + MARKER_EXPR) + +MARKER = stringStart + MARKER_EXPR + stringEnd + + +def _coerce_parse_result(results): + if isinstance(results, ParseResults): + return [_coerce_parse_result(i) for i in results] + else: + return results + + +def _format_marker(marker, first=True): + assert isinstance(marker, (list, tuple, string_types)) + + # Sometimes we have a structure like [[...]] which is a single item list + # where the single item is itself it's own list. In that case we want skip + # the rest of this function so that we don't get extraneous () on the + # outside. + if (isinstance(marker, list) and len(marker) == 1 and + isinstance(marker[0], (list, tuple))): + return _format_marker(marker[0]) + + if isinstance(marker, list): + inner = (_format_marker(m, first=False) for m in marker) + if first: + return " ".join(inner) + else: + return "(" + " ".join(inner) + ")" + elif isinstance(marker, tuple): + return " ".join([m.serialize() for m in marker]) + else: + return marker + + +_operators = { + "in": lambda lhs, rhs: lhs in rhs, + "not in": lambda lhs, rhs: lhs not in rhs, + "<": operator.lt, + "<=": operator.le, + "==": operator.eq, + "!=": operator.ne, + ">=": operator.ge, + ">": operator.gt, +} + + +def _eval_op(lhs, op, rhs): + try: + spec = Specifier("".join([op.serialize(), rhs])) + except InvalidSpecifier: + pass + else: + return spec.contains(lhs) + + oper = _operators.get(op.serialize()) + if oper is None: + raise UndefinedComparison( + "Undefined {0!r} on {1!r} and {2!r}.".format(op, lhs, rhs) + ) + + return oper(lhs, rhs) + + +_undefined = object() + + +def _get_env(environment, name): + value = environment.get(name, _undefined) + + if value is _undefined: + raise UndefinedEnvironmentName( + "{0!r} does not exist in evaluation environment.".format(name) + ) + + return value + + +def _evaluate_markers(markers, environment): + groups = [[]] + + for marker in markers: + assert isinstance(marker, (list, tuple, string_types)) + + if isinstance(marker, list): + groups[-1].append(_evaluate_markers(marker, environment)) + elif isinstance(marker, tuple): + lhs, op, rhs = marker + + if isinstance(lhs, Variable): + lhs_value = _get_env(environment, lhs.value) + rhs_value = rhs.value + else: + lhs_value = lhs.value + rhs_value = _get_env(environment, rhs.value) + + groups[-1].append(_eval_op(lhs_value, op, rhs_value)) + else: + assert marker in ["and", "or"] + if marker == "or": + groups.append([]) + + return any(all(item) for item in groups) + + +def format_full_version(info): + version = '{0.major}.{0.minor}.{0.micro}'.format(info) + kind = info.releaselevel + if kind != 'final': + version += kind[0] + str(info.serial) + return version + + +def default_environment(): + if hasattr(sys, 'implementation'): + iver = format_full_version(sys.implementation.version) + implementation_name = sys.implementation.name + else: + iver = '0' + implementation_name = '' + + return { + "implementation_name": implementation_name, + "implementation_version": iver, + "os_name": os.name, + "platform_machine": platform.machine(), + "platform_release": platform.release(), + "platform_system": platform.system(), + "platform_version": platform.version(), + "python_full_version": platform.python_version(), + "platform_python_implementation": platform.python_implementation(), + "python_version": platform.python_version()[:3], + "sys_platform": sys.platform, + } + + +class Marker(object): + + def __init__(self, marker): + try: + self._markers = _coerce_parse_result(MARKER.parseString(marker)) + except ParseException as e: + err_str = "Invalid marker: {0!r}, parse error at {1!r}".format( + marker, marker[e.loc:e.loc + 8]) + raise InvalidMarker(err_str) + + def __str__(self): + return _format_marker(self._markers) + + def __repr__(self): + return "".format(str(self)) + + def evaluate(self, environment=None): + """Evaluate a marker. + + Return the boolean from evaluating the given marker against the + environment. environment is an optional argument to override all or + part of the determined environment. + + The environment is determined from the current Python process. + """ + current_environment = default_environment() + if environment is not None: + current_environment.update(environment) + + return _evaluate_markers(self._markers, current_environment) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/requirements.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/requirements.py new file mode 100644 index 0000000000..0c8c4a3852 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/requirements.py @@ -0,0 +1,127 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +import string +import re + +from pkg_resources.extern.pyparsing import stringStart, stringEnd, originalTextFor, ParseException +from pkg_resources.extern.pyparsing import ZeroOrMore, Word, Optional, Regex, Combine +from pkg_resources.extern.pyparsing import Literal as L # noqa +from pkg_resources.extern.six.moves.urllib import parse as urlparse + +from .markers import MARKER_EXPR, Marker +from .specifiers import LegacySpecifier, Specifier, SpecifierSet + + +class InvalidRequirement(ValueError): + """ + An invalid requirement was found, users should refer to PEP 508. + """ + + +ALPHANUM = Word(string.ascii_letters + string.digits) + +LBRACKET = L("[").suppress() +RBRACKET = L("]").suppress() +LPAREN = L("(").suppress() +RPAREN = L(")").suppress() +COMMA = L(",").suppress() +SEMICOLON = L(";").suppress() +AT = L("@").suppress() + +PUNCTUATION = Word("-_.") +IDENTIFIER_END = ALPHANUM | (ZeroOrMore(PUNCTUATION) + ALPHANUM) +IDENTIFIER = Combine(ALPHANUM + ZeroOrMore(IDENTIFIER_END)) + +NAME = IDENTIFIER("name") +EXTRA = IDENTIFIER + +URI = Regex(r'[^ ]+')("url") +URL = (AT + URI) + +EXTRAS_LIST = EXTRA + ZeroOrMore(COMMA + EXTRA) +EXTRAS = (LBRACKET + Optional(EXTRAS_LIST) + RBRACKET)("extras") + +VERSION_PEP440 = Regex(Specifier._regex_str, re.VERBOSE | re.IGNORECASE) +VERSION_LEGACY = Regex(LegacySpecifier._regex_str, re.VERBOSE | re.IGNORECASE) + +VERSION_ONE = VERSION_PEP440 ^ VERSION_LEGACY +VERSION_MANY = Combine(VERSION_ONE + ZeroOrMore(COMMA + VERSION_ONE), + joinString=",", adjacent=False)("_raw_spec") +_VERSION_SPEC = Optional(((LPAREN + VERSION_MANY + RPAREN) | VERSION_MANY)) +_VERSION_SPEC.setParseAction(lambda s, l, t: t._raw_spec or '') + +VERSION_SPEC = originalTextFor(_VERSION_SPEC)("specifier") +VERSION_SPEC.setParseAction(lambda s, l, t: t[1]) + +MARKER_EXPR = originalTextFor(MARKER_EXPR())("marker") +MARKER_EXPR.setParseAction( + lambda s, l, t: Marker(s[t._original_start:t._original_end]) +) +MARKER_SEPERATOR = SEMICOLON +MARKER = MARKER_SEPERATOR + MARKER_EXPR + +VERSION_AND_MARKER = VERSION_SPEC + Optional(MARKER) +URL_AND_MARKER = URL + Optional(MARKER) + +NAMED_REQUIREMENT = \ + NAME + Optional(EXTRAS) + (URL_AND_MARKER | VERSION_AND_MARKER) + +REQUIREMENT = stringStart + NAMED_REQUIREMENT + stringEnd + + +class Requirement(object): + """Parse a requirement. + + Parse a given requirement string into its parts, such as name, specifier, + URL, and extras. Raises InvalidRequirement on a badly-formed requirement + string. + """ + + # TODO: Can we test whether something is contained within a requirement? + # If so how do we do that? Do we need to test against the _name_ of + # the thing as well as the version? What about the markers? + # TODO: Can we normalize the name and extra name? + + def __init__(self, requirement_string): + try: + req = REQUIREMENT.parseString(requirement_string) + except ParseException as e: + raise InvalidRequirement( + "Invalid requirement, parse error at \"{0!r}\"".format( + requirement_string[e.loc:e.loc + 8])) + + self.name = req.name + if req.url: + parsed_url = urlparse.urlparse(req.url) + if not (parsed_url.scheme and parsed_url.netloc) or ( + not parsed_url.scheme and not parsed_url.netloc): + raise InvalidRequirement("Invalid URL given") + self.url = req.url + else: + self.url = None + self.extras = set(req.extras.asList() if req.extras else []) + self.specifier = SpecifierSet(req.specifier) + self.marker = req.marker if req.marker else None + + def __str__(self): + parts = [self.name] + + if self.extras: + parts.append("[{0}]".format(",".join(sorted(self.extras)))) + + if self.specifier: + parts.append(str(self.specifier)) + + if self.url: + parts.append("@ {0}".format(self.url)) + + if self.marker: + parts.append("; {0}".format(self.marker)) + + return "".join(parts) + + def __repr__(self): + return "".format(str(self)) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/specifiers.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/specifiers.py new file mode 100644 index 0000000000..7f5a76cfd6 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/specifiers.py @@ -0,0 +1,774 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +import abc +import functools +import itertools +import re + +from ._compat import string_types, with_metaclass +from .version import Version, LegacyVersion, parse + + +class InvalidSpecifier(ValueError): + """ + An invalid specifier was found, users should refer to PEP 440. + """ + + +class BaseSpecifier(with_metaclass(abc.ABCMeta, object)): + + @abc.abstractmethod + def __str__(self): + """ + Returns the str representation of this Specifier like object. This + should be representative of the Specifier itself. + """ + + @abc.abstractmethod + def __hash__(self): + """ + Returns a hash value for this Specifier like object. + """ + + @abc.abstractmethod + def __eq__(self, other): + """ + Returns a boolean representing whether or not the two Specifier like + objects are equal. + """ + + @abc.abstractmethod + def __ne__(self, other): + """ + Returns a boolean representing whether or not the two Specifier like + objects are not equal. + """ + + @abc.abstractproperty + def prereleases(self): + """ + Returns whether or not pre-releases as a whole are allowed by this + specifier. + """ + + @prereleases.setter + def prereleases(self, value): + """ + Sets whether or not pre-releases as a whole are allowed by this + specifier. + """ + + @abc.abstractmethod + def contains(self, item, prereleases=None): + """ + Determines if the given item is contained within this specifier. + """ + + @abc.abstractmethod + def filter(self, iterable, prereleases=None): + """ + Takes an iterable of items and filters them so that only items which + are contained within this specifier are allowed in it. + """ + + +class _IndividualSpecifier(BaseSpecifier): + + _operators = {} + + def __init__(self, spec="", prereleases=None): + match = self._regex.search(spec) + if not match: + raise InvalidSpecifier("Invalid specifier: '{0}'".format(spec)) + + self._spec = ( + match.group("operator").strip(), + match.group("version").strip(), + ) + + # Store whether or not this Specifier should accept prereleases + self._prereleases = prereleases + + def __repr__(self): + pre = ( + ", prereleases={0!r}".format(self.prereleases) + if self._prereleases is not None + else "" + ) + + return "<{0}({1!r}{2})>".format( + self.__class__.__name__, + str(self), + pre, + ) + + def __str__(self): + return "{0}{1}".format(*self._spec) + + def __hash__(self): + return hash(self._spec) + + def __eq__(self, other): + if isinstance(other, string_types): + try: + other = self.__class__(other) + except InvalidSpecifier: + return NotImplemented + elif not isinstance(other, self.__class__): + return NotImplemented + + return self._spec == other._spec + + def __ne__(self, other): + if isinstance(other, string_types): + try: + other = self.__class__(other) + except InvalidSpecifier: + return NotImplemented + elif not isinstance(other, self.__class__): + return NotImplemented + + return self._spec != other._spec + + def _get_operator(self, op): + return getattr(self, "_compare_{0}".format(self._operators[op])) + + def _coerce_version(self, version): + if not isinstance(version, (LegacyVersion, Version)): + version = parse(version) + return version + + @property + def operator(self): + return self._spec[0] + + @property + def version(self): + return self._spec[1] + + @property + def prereleases(self): + return self._prereleases + + @prereleases.setter + def prereleases(self, value): + self._prereleases = value + + def __contains__(self, item): + return self.contains(item) + + def contains(self, item, prereleases=None): + # Determine if prereleases are to be allowed or not. + if prereleases is None: + prereleases = self.prereleases + + # Normalize item to a Version or LegacyVersion, this allows us to have + # a shortcut for ``"2.0" in Specifier(">=2") + item = self._coerce_version(item) + + # Determine if we should be supporting prereleases in this specifier + # or not, if we do not support prereleases than we can short circuit + # logic if this version is a prereleases. + if item.is_prerelease and not prereleases: + return False + + # Actually do the comparison to determine if this item is contained + # within this Specifier or not. + return self._get_operator(self.operator)(item, self.version) + + def filter(self, iterable, prereleases=None): + yielded = False + found_prereleases = [] + + kw = {"prereleases": prereleases if prereleases is not None else True} + + # Attempt to iterate over all the values in the iterable and if any of + # them match, yield them. + for version in iterable: + parsed_version = self._coerce_version(version) + + if self.contains(parsed_version, **kw): + # If our version is a prerelease, and we were not set to allow + # prereleases, then we'll store it for later incase nothing + # else matches this specifier. + if (parsed_version.is_prerelease and not + (prereleases or self.prereleases)): + found_prereleases.append(version) + # Either this is not a prerelease, or we should have been + # accepting prereleases from the begining. + else: + yielded = True + yield version + + # Now that we've iterated over everything, determine if we've yielded + # any values, and if we have not and we have any prereleases stored up + # then we will go ahead and yield the prereleases. + if not yielded and found_prereleases: + for version in found_prereleases: + yield version + + +class LegacySpecifier(_IndividualSpecifier): + + _regex_str = ( + r""" + (?P(==|!=|<=|>=|<|>)) + \s* + (?P + [^,;\s)]* # Since this is a "legacy" specifier, and the version + # string can be just about anything, we match everything + # except for whitespace, a semi-colon for marker support, + # a closing paren since versions can be enclosed in + # them, and a comma since it's a version separator. + ) + """ + ) + + _regex = re.compile( + r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE) + + _operators = { + "==": "equal", + "!=": "not_equal", + "<=": "less_than_equal", + ">=": "greater_than_equal", + "<": "less_than", + ">": "greater_than", + } + + def _coerce_version(self, version): + if not isinstance(version, LegacyVersion): + version = LegacyVersion(str(version)) + return version + + def _compare_equal(self, prospective, spec): + return prospective == self._coerce_version(spec) + + def _compare_not_equal(self, prospective, spec): + return prospective != self._coerce_version(spec) + + def _compare_less_than_equal(self, prospective, spec): + return prospective <= self._coerce_version(spec) + + def _compare_greater_than_equal(self, prospective, spec): + return prospective >= self._coerce_version(spec) + + def _compare_less_than(self, prospective, spec): + return prospective < self._coerce_version(spec) + + def _compare_greater_than(self, prospective, spec): + return prospective > self._coerce_version(spec) + + +def _require_version_compare(fn): + @functools.wraps(fn) + def wrapped(self, prospective, spec): + if not isinstance(prospective, Version): + return False + return fn(self, prospective, spec) + return wrapped + + +class Specifier(_IndividualSpecifier): + + _regex_str = ( + r""" + (?P(~=|==|!=|<=|>=|<|>|===)) + (?P + (?: + # The identity operators allow for an escape hatch that will + # do an exact string match of the version you wish to install. + # This will not be parsed by PEP 440 and we cannot determine + # any semantic meaning from it. This operator is discouraged + # but included entirely as an escape hatch. + (?<====) # Only match for the identity operator + \s* + [^\s]* # We just match everything, except for whitespace + # since we are only testing for strict identity. + ) + | + (?: + # The (non)equality operators allow for wild card and local + # versions to be specified so we have to define these two + # operators separately to enable that. + (?<===|!=) # Only match for equals and not equals + + \s* + v? + (?:[0-9]+!)? # epoch + [0-9]+(?:\.[0-9]+)* # release + (?: # pre release + [-_\.]? + (a|b|c|rc|alpha|beta|pre|preview) + [-_\.]? + [0-9]* + )? + (?: # post release + (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) + )? + + # You cannot use a wild card and a dev or local version + # together so group them with a | and make them optional. + (?: + (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release + (?:\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*)? # local + | + \.\* # Wild card syntax of .* + )? + ) + | + (?: + # The compatible operator requires at least two digits in the + # release segment. + (?<=~=) # Only match for the compatible operator + + \s* + v? + (?:[0-9]+!)? # epoch + [0-9]+(?:\.[0-9]+)+ # release (We have a + instead of a *) + (?: # pre release + [-_\.]? + (a|b|c|rc|alpha|beta|pre|preview) + [-_\.]? + [0-9]* + )? + (?: # post release + (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) + )? + (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release + ) + | + (?: + # All other operators only allow a sub set of what the + # (non)equality operators do. Specifically they do not allow + # local versions to be specified nor do they allow the prefix + # matching wild cards. + (?=": "greater_than_equal", + "<": "less_than", + ">": "greater_than", + "===": "arbitrary", + } + + @_require_version_compare + def _compare_compatible(self, prospective, spec): + # Compatible releases have an equivalent combination of >= and ==. That + # is that ~=2.2 is equivalent to >=2.2,==2.*. This allows us to + # implement this in terms of the other specifiers instead of + # implementing it ourselves. The only thing we need to do is construct + # the other specifiers. + + # We want everything but the last item in the version, but we want to + # ignore post and dev releases and we want to treat the pre-release as + # it's own separate segment. + prefix = ".".join( + list( + itertools.takewhile( + lambda x: (not x.startswith("post") and not + x.startswith("dev")), + _version_split(spec), + ) + )[:-1] + ) + + # Add the prefix notation to the end of our string + prefix += ".*" + + return (self._get_operator(">=")(prospective, spec) and + self._get_operator("==")(prospective, prefix)) + + @_require_version_compare + def _compare_equal(self, prospective, spec): + # We need special logic to handle prefix matching + if spec.endswith(".*"): + # In the case of prefix matching we want to ignore local segment. + prospective = Version(prospective.public) + # Split the spec out by dots, and pretend that there is an implicit + # dot in between a release segment and a pre-release segment. + spec = _version_split(spec[:-2]) # Remove the trailing .* + + # Split the prospective version out by dots, and pretend that there + # is an implicit dot in between a release segment and a pre-release + # segment. + prospective = _version_split(str(prospective)) + + # Shorten the prospective version to be the same length as the spec + # so that we can determine if the specifier is a prefix of the + # prospective version or not. + prospective = prospective[:len(spec)] + + # Pad out our two sides with zeros so that they both equal the same + # length. + spec, prospective = _pad_version(spec, prospective) + else: + # Convert our spec string into a Version + spec = Version(spec) + + # If the specifier does not have a local segment, then we want to + # act as if the prospective version also does not have a local + # segment. + if not spec.local: + prospective = Version(prospective.public) + + return prospective == spec + + @_require_version_compare + def _compare_not_equal(self, prospective, spec): + return not self._compare_equal(prospective, spec) + + @_require_version_compare + def _compare_less_than_equal(self, prospective, spec): + return prospective <= Version(spec) + + @_require_version_compare + def _compare_greater_than_equal(self, prospective, spec): + return prospective >= Version(spec) + + @_require_version_compare + def _compare_less_than(self, prospective, spec): + # Convert our spec to a Version instance, since we'll want to work with + # it as a version. + spec = Version(spec) + + # Check to see if the prospective version is less than the spec + # version. If it's not we can short circuit and just return False now + # instead of doing extra unneeded work. + if not prospective < spec: + return False + + # This special case is here so that, unless the specifier itself + # includes is a pre-release version, that we do not accept pre-release + # versions for the version mentioned in the specifier (e.g. <3.1 should + # not match 3.1.dev0, but should match 3.0.dev0). + if not spec.is_prerelease and prospective.is_prerelease: + if Version(prospective.base_version) == Version(spec.base_version): + return False + + # If we've gotten to here, it means that prospective version is both + # less than the spec version *and* it's not a pre-release of the same + # version in the spec. + return True + + @_require_version_compare + def _compare_greater_than(self, prospective, spec): + # Convert our spec to a Version instance, since we'll want to work with + # it as a version. + spec = Version(spec) + + # Check to see if the prospective version is greater than the spec + # version. If it's not we can short circuit and just return False now + # instead of doing extra unneeded work. + if not prospective > spec: + return False + + # This special case is here so that, unless the specifier itself + # includes is a post-release version, that we do not accept + # post-release versions for the version mentioned in the specifier + # (e.g. >3.1 should not match 3.0.post0, but should match 3.2.post0). + if not spec.is_postrelease and prospective.is_postrelease: + if Version(prospective.base_version) == Version(spec.base_version): + return False + + # Ensure that we do not allow a local version of the version mentioned + # in the specifier, which is techincally greater than, to match. + if prospective.local is not None: + if Version(prospective.base_version) == Version(spec.base_version): + return False + + # If we've gotten to here, it means that prospective version is both + # greater than the spec version *and* it's not a pre-release of the + # same version in the spec. + return True + + def _compare_arbitrary(self, prospective, spec): + return str(prospective).lower() == str(spec).lower() + + @property + def prereleases(self): + # If there is an explicit prereleases set for this, then we'll just + # blindly use that. + if self._prereleases is not None: + return self._prereleases + + # Look at all of our specifiers and determine if they are inclusive + # operators, and if they are if they are including an explicit + # prerelease. + operator, version = self._spec + if operator in ["==", ">=", "<=", "~=", "==="]: + # The == specifier can include a trailing .*, if it does we + # want to remove before parsing. + if operator == "==" and version.endswith(".*"): + version = version[:-2] + + # Parse the version, and if it is a pre-release than this + # specifier allows pre-releases. + if parse(version).is_prerelease: + return True + + return False + + @prereleases.setter + def prereleases(self, value): + self._prereleases = value + + +_prefix_regex = re.compile(r"^([0-9]+)((?:a|b|c|rc)[0-9]+)$") + + +def _version_split(version): + result = [] + for item in version.split("."): + match = _prefix_regex.search(item) + if match: + result.extend(match.groups()) + else: + result.append(item) + return result + + +def _pad_version(left, right): + left_split, right_split = [], [] + + # Get the release segment of our versions + left_split.append(list(itertools.takewhile(lambda x: x.isdigit(), left))) + right_split.append(list(itertools.takewhile(lambda x: x.isdigit(), right))) + + # Get the rest of our versions + left_split.append(left[len(left_split[0]):]) + right_split.append(right[len(right_split[0]):]) + + # Insert our padding + left_split.insert( + 1, + ["0"] * max(0, len(right_split[0]) - len(left_split[0])), + ) + right_split.insert( + 1, + ["0"] * max(0, len(left_split[0]) - len(right_split[0])), + ) + + return ( + list(itertools.chain(*left_split)), + list(itertools.chain(*right_split)), + ) + + +class SpecifierSet(BaseSpecifier): + + def __init__(self, specifiers="", prereleases=None): + # Split on , to break each indidivual specifier into it's own item, and + # strip each item to remove leading/trailing whitespace. + specifiers = [s.strip() for s in specifiers.split(",") if s.strip()] + + # Parsed each individual specifier, attempting first to make it a + # Specifier and falling back to a LegacySpecifier. + parsed = set() + for specifier in specifiers: + try: + parsed.add(Specifier(specifier)) + except InvalidSpecifier: + parsed.add(LegacySpecifier(specifier)) + + # Turn our parsed specifiers into a frozen set and save them for later. + self._specs = frozenset(parsed) + + # Store our prereleases value so we can use it later to determine if + # we accept prereleases or not. + self._prereleases = prereleases + + def __repr__(self): + pre = ( + ", prereleases={0!r}".format(self.prereleases) + if self._prereleases is not None + else "" + ) + + return "".format(str(self), pre) + + def __str__(self): + return ",".join(sorted(str(s) for s in self._specs)) + + def __hash__(self): + return hash(self._specs) + + def __and__(self, other): + if isinstance(other, string_types): + other = SpecifierSet(other) + elif not isinstance(other, SpecifierSet): + return NotImplemented + + specifier = SpecifierSet() + specifier._specs = frozenset(self._specs | other._specs) + + if self._prereleases is None and other._prereleases is not None: + specifier._prereleases = other._prereleases + elif self._prereleases is not None and other._prereleases is None: + specifier._prereleases = self._prereleases + elif self._prereleases == other._prereleases: + specifier._prereleases = self._prereleases + else: + raise ValueError( + "Cannot combine SpecifierSets with True and False prerelease " + "overrides." + ) + + return specifier + + def __eq__(self, other): + if isinstance(other, string_types): + other = SpecifierSet(other) + elif isinstance(other, _IndividualSpecifier): + other = SpecifierSet(str(other)) + elif not isinstance(other, SpecifierSet): + return NotImplemented + + return self._specs == other._specs + + def __ne__(self, other): + if isinstance(other, string_types): + other = SpecifierSet(other) + elif isinstance(other, _IndividualSpecifier): + other = SpecifierSet(str(other)) + elif not isinstance(other, SpecifierSet): + return NotImplemented + + return self._specs != other._specs + + def __len__(self): + return len(self._specs) + + def __iter__(self): + return iter(self._specs) + + @property + def prereleases(self): + # If we have been given an explicit prerelease modifier, then we'll + # pass that through here. + if self._prereleases is not None: + return self._prereleases + + # If we don't have any specifiers, and we don't have a forced value, + # then we'll just return None since we don't know if this should have + # pre-releases or not. + if not self._specs: + return None + + # Otherwise we'll see if any of the given specifiers accept + # prereleases, if any of them do we'll return True, otherwise False. + return any(s.prereleases for s in self._specs) + + @prereleases.setter + def prereleases(self, value): + self._prereleases = value + + def __contains__(self, item): + return self.contains(item) + + def contains(self, item, prereleases=None): + # Ensure that our item is a Version or LegacyVersion instance. + if not isinstance(item, (LegacyVersion, Version)): + item = parse(item) + + # Determine if we're forcing a prerelease or not, if we're not forcing + # one for this particular filter call, then we'll use whatever the + # SpecifierSet thinks for whether or not we should support prereleases. + if prereleases is None: + prereleases = self.prereleases + + # We can determine if we're going to allow pre-releases by looking to + # see if any of the underlying items supports them. If none of them do + # and this item is a pre-release then we do not allow it and we can + # short circuit that here. + # Note: This means that 1.0.dev1 would not be contained in something + # like >=1.0.devabc however it would be in >=1.0.debabc,>0.0.dev0 + if not prereleases and item.is_prerelease: + return False + + # We simply dispatch to the underlying specs here to make sure that the + # given version is contained within all of them. + # Note: This use of all() here means that an empty set of specifiers + # will always return True, this is an explicit design decision. + return all( + s.contains(item, prereleases=prereleases) + for s in self._specs + ) + + def filter(self, iterable, prereleases=None): + # Determine if we're forcing a prerelease or not, if we're not forcing + # one for this particular filter call, then we'll use whatever the + # SpecifierSet thinks for whether or not we should support prereleases. + if prereleases is None: + prereleases = self.prereleases + + # If we have any specifiers, then we want to wrap our iterable in the + # filter method for each one, this will act as a logical AND amongst + # each specifier. + if self._specs: + for spec in self._specs: + iterable = spec.filter(iterable, prereleases=bool(prereleases)) + return iterable + # If we do not have any specifiers, then we need to have a rough filter + # which will filter out any pre-releases, unless there are no final + # releases, and which will filter out LegacyVersion in general. + else: + filtered = [] + found_prereleases = [] + + for item in iterable: + # Ensure that we some kind of Version class for this item. + if not isinstance(item, (LegacyVersion, Version)): + parsed_version = parse(item) + else: + parsed_version = item + + # Filter out any item which is parsed as a LegacyVersion + if isinstance(parsed_version, LegacyVersion): + continue + + # Store any item which is a pre-release for later unless we've + # already found a final version or we are accepting prereleases + if parsed_version.is_prerelease and not prereleases: + if not filtered: + found_prereleases.append(item) + else: + filtered.append(item) + + # If we've found no items except for pre-releases, then we'll go + # ahead and use the pre-releases + if not filtered and found_prereleases and prereleases is None: + return found_prereleases + + return filtered diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/utils.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/utils.py new file mode 100644 index 0000000000..942387cef5 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/utils.py @@ -0,0 +1,14 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +import re + + +_canonicalize_regex = re.compile(r"[-_.]+") + + +def canonicalize_name(name): + # This is taken from PEP 503. + return _canonicalize_regex.sub("-", name).lower() diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/version.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/version.py new file mode 100644 index 0000000000..83b5ee8c5e --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/version.py @@ -0,0 +1,393 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +import collections +import itertools +import re + +from ._structures import Infinity + + +__all__ = [ + "parse", "Version", "LegacyVersion", "InvalidVersion", "VERSION_PATTERN" +] + + +_Version = collections.namedtuple( + "_Version", + ["epoch", "release", "dev", "pre", "post", "local"], +) + + +def parse(version): + """ + Parse the given version string and return either a :class:`Version` object + or a :class:`LegacyVersion` object depending on if the given version is + a valid PEP 440 version or a legacy version. + """ + try: + return Version(version) + except InvalidVersion: + return LegacyVersion(version) + + +class InvalidVersion(ValueError): + """ + An invalid version was found, users should refer to PEP 440. + """ + + +class _BaseVersion(object): + + def __hash__(self): + return hash(self._key) + + def __lt__(self, other): + return self._compare(other, lambda s, o: s < o) + + def __le__(self, other): + return self._compare(other, lambda s, o: s <= o) + + def __eq__(self, other): + return self._compare(other, lambda s, o: s == o) + + def __ge__(self, other): + return self._compare(other, lambda s, o: s >= o) + + def __gt__(self, other): + return self._compare(other, lambda s, o: s > o) + + def __ne__(self, other): + return self._compare(other, lambda s, o: s != o) + + def _compare(self, other, method): + if not isinstance(other, _BaseVersion): + return NotImplemented + + return method(self._key, other._key) + + +class LegacyVersion(_BaseVersion): + + def __init__(self, version): + self._version = str(version) + self._key = _legacy_cmpkey(self._version) + + def __str__(self): + return self._version + + def __repr__(self): + return "".format(repr(str(self))) + + @property + def public(self): + return self._version + + @property + def base_version(self): + return self._version + + @property + def local(self): + return None + + @property + def is_prerelease(self): + return False + + @property + def is_postrelease(self): + return False + + +_legacy_version_component_re = re.compile( + r"(\d+ | [a-z]+ | \.| -)", re.VERBOSE, +) + +_legacy_version_replacement_map = { + "pre": "c", "preview": "c", "-": "final-", "rc": "c", "dev": "@", +} + + +def _parse_version_parts(s): + for part in _legacy_version_component_re.split(s): + part = _legacy_version_replacement_map.get(part, part) + + if not part or part == ".": + continue + + if part[:1] in "0123456789": + # pad for numeric comparison + yield part.zfill(8) + else: + yield "*" + part + + # ensure that alpha/beta/candidate are before final + yield "*final" + + +def _legacy_cmpkey(version): + # We hardcode an epoch of -1 here. A PEP 440 version can only have a epoch + # greater than or equal to 0. This will effectively put the LegacyVersion, + # which uses the defacto standard originally implemented by setuptools, + # as before all PEP 440 versions. + epoch = -1 + + # This scheme is taken from pkg_resources.parse_version setuptools prior to + # it's adoption of the packaging library. + parts = [] + for part in _parse_version_parts(version.lower()): + if part.startswith("*"): + # remove "-" before a prerelease tag + if part < "*final": + while parts and parts[-1] == "*final-": + parts.pop() + + # remove trailing zeros from each series of numeric parts + while parts and parts[-1] == "00000000": + parts.pop() + + parts.append(part) + parts = tuple(parts) + + return epoch, parts + +# Deliberately not anchored to the start and end of the string, to make it +# easier for 3rd party code to reuse +VERSION_PATTERN = r""" + v? + (?: + (?:(?P[0-9]+)!)? # epoch + (?P[0-9]+(?:\.[0-9]+)*) # release segment + (?P
                                          # pre-release
+            [-_\.]?
+            (?P(a|b|c|rc|alpha|beta|pre|preview))
+            [-_\.]?
+            (?P[0-9]+)?
+        )?
+        (?P                                         # post release
+            (?:-(?P[0-9]+))
+            |
+            (?:
+                [-_\.]?
+                (?Ppost|rev|r)
+                [-_\.]?
+                (?P[0-9]+)?
+            )
+        )?
+        (?P                                          # dev release
+            [-_\.]?
+            (?Pdev)
+            [-_\.]?
+            (?P[0-9]+)?
+        )?
+    )
+    (?:\+(?P[a-z0-9]+(?:[-_\.][a-z0-9]+)*))?       # local version
+"""
+
+
+class Version(_BaseVersion):
+
+    _regex = re.compile(
+        r"^\s*" + VERSION_PATTERN + r"\s*$",
+        re.VERBOSE | re.IGNORECASE,
+    )
+
+    def __init__(self, version):
+        # Validate the version and parse it into pieces
+        match = self._regex.search(version)
+        if not match:
+            raise InvalidVersion("Invalid version: '{0}'".format(version))
+
+        # Store the parsed out pieces of the version
+        self._version = _Version(
+            epoch=int(match.group("epoch")) if match.group("epoch") else 0,
+            release=tuple(int(i) for i in match.group("release").split(".")),
+            pre=_parse_letter_version(
+                match.group("pre_l"),
+                match.group("pre_n"),
+            ),
+            post=_parse_letter_version(
+                match.group("post_l"),
+                match.group("post_n1") or match.group("post_n2"),
+            ),
+            dev=_parse_letter_version(
+                match.group("dev_l"),
+                match.group("dev_n"),
+            ),
+            local=_parse_local_version(match.group("local")),
+        )
+
+        # Generate a key which will be used for sorting
+        self._key = _cmpkey(
+            self._version.epoch,
+            self._version.release,
+            self._version.pre,
+            self._version.post,
+            self._version.dev,
+            self._version.local,
+        )
+
+    def __repr__(self):
+        return "".format(repr(str(self)))
+
+    def __str__(self):
+        parts = []
+
+        # Epoch
+        if self._version.epoch != 0:
+            parts.append("{0}!".format(self._version.epoch))
+
+        # Release segment
+        parts.append(".".join(str(x) for x in self._version.release))
+
+        # Pre-release
+        if self._version.pre is not None:
+            parts.append("".join(str(x) for x in self._version.pre))
+
+        # Post-release
+        if self._version.post is not None:
+            parts.append(".post{0}".format(self._version.post[1]))
+
+        # Development release
+        if self._version.dev is not None:
+            parts.append(".dev{0}".format(self._version.dev[1]))
+
+        # Local version segment
+        if self._version.local is not None:
+            parts.append(
+                "+{0}".format(".".join(str(x) for x in self._version.local))
+            )
+
+        return "".join(parts)
+
+    @property
+    def public(self):
+        return str(self).split("+", 1)[0]
+
+    @property
+    def base_version(self):
+        parts = []
+
+        # Epoch
+        if self._version.epoch != 0:
+            parts.append("{0}!".format(self._version.epoch))
+
+        # Release segment
+        parts.append(".".join(str(x) for x in self._version.release))
+
+        return "".join(parts)
+
+    @property
+    def local(self):
+        version_string = str(self)
+        if "+" in version_string:
+            return version_string.split("+", 1)[1]
+
+    @property
+    def is_prerelease(self):
+        return bool(self._version.dev or self._version.pre)
+
+    @property
+    def is_postrelease(self):
+        return bool(self._version.post)
+
+
+def _parse_letter_version(letter, number):
+    if letter:
+        # We consider there to be an implicit 0 in a pre-release if there is
+        # not a numeral associated with it.
+        if number is None:
+            number = 0
+
+        # We normalize any letters to their lower case form
+        letter = letter.lower()
+
+        # We consider some words to be alternate spellings of other words and
+        # in those cases we want to normalize the spellings to our preferred
+        # spelling.
+        if letter == "alpha":
+            letter = "a"
+        elif letter == "beta":
+            letter = "b"
+        elif letter in ["c", "pre", "preview"]:
+            letter = "rc"
+        elif letter in ["rev", "r"]:
+            letter = "post"
+
+        return letter, int(number)
+    if not letter and number:
+        # We assume if we are given a number, but we are not given a letter
+        # then this is using the implicit post release syntax (e.g. 1.0-1)
+        letter = "post"
+
+        return letter, int(number)
+
+
+_local_version_seperators = re.compile(r"[\._-]")
+
+
+def _parse_local_version(local):
+    """
+    Takes a string like abc.1.twelve and turns it into ("abc", 1, "twelve").
+    """
+    if local is not None:
+        return tuple(
+            part.lower() if not part.isdigit() else int(part)
+            for part in _local_version_seperators.split(local)
+        )
+
+
+def _cmpkey(epoch, release, pre, post, dev, local):
+    # When we compare a release version, we want to compare it with all of the
+    # trailing zeros removed. So we'll use a reverse the list, drop all the now
+    # leading zeros until we come to something non zero, then take the rest
+    # re-reverse it back into the correct order and make it a tuple and use
+    # that for our sorting key.
+    release = tuple(
+        reversed(list(
+            itertools.dropwhile(
+                lambda x: x == 0,
+                reversed(release),
+            )
+        ))
+    )
+
+    # We need to "trick" the sorting algorithm to put 1.0.dev0 before 1.0a0.
+    # We'll do this by abusing the pre segment, but we _only_ want to do this
+    # if there is not a pre or a post segment. If we have one of those then
+    # the normal sorting rules will handle this case correctly.
+    if pre is None and post is None and dev is not None:
+        pre = -Infinity
+    # Versions without a pre-release (except as noted above) should sort after
+    # those with one.
+    elif pre is None:
+        pre = Infinity
+
+    # Versions without a post segment should sort before those with one.
+    if post is None:
+        post = -Infinity
+
+    # Versions without a development segment should sort after those with one.
+    if dev is None:
+        dev = Infinity
+
+    if local is None:
+        # Versions without a local segment should sort before those with one.
+        local = -Infinity
+    else:
+        # Versions with a local segment need that segment parsed to implement
+        # the sorting rules in PEP440.
+        # - Alpha numeric segments sort before numeric segments
+        # - Alpha numeric segments sort lexicographically
+        # - Numeric segments sort numerically
+        # - Shorter versions sort before longer versions when the prefixes
+        #   match exactly
+        local = tuple(
+            (i, "") if isinstance(i, int) else (-Infinity, i)
+            for i in local
+        )
+
+    return epoch, release, pre, post, dev, local
diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/pyparsing.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/pyparsing.py
new file mode 100644
index 0000000000..cf75e1e5fc
--- /dev/null
+++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/pyparsing.py
@@ -0,0 +1,5742 @@
+# module pyparsing.py
+#
+# Copyright (c) 2003-2018  Paul T. McGuire
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__doc__ = \
+"""
+pyparsing module - Classes and methods to define and execute parsing grammars
+=============================================================================
+
+The pyparsing module is an alternative approach to creating and executing simple grammars,
+vs. the traditional lex/yacc approach, or the use of regular expressions.  With pyparsing, you
+don't need to learn a new syntax for defining grammars or matching expressions - the parsing module
+provides a library of classes that you use to construct the grammar directly in Python.
+
+Here is a program to parse "Hello, World!" (or any greeting of the form 
+C{", !"}), built up using L{Word}, L{Literal}, and L{And} elements 
+(L{'+'} operator gives L{And} expressions, strings are auto-converted to
+L{Literal} expressions)::
+
+    from pyparsing import Word, alphas
+
+    # define grammar of a greeting
+    greet = Word(alphas) + "," + Word(alphas) + "!"
+
+    hello = "Hello, World!"
+    print (hello, "->", greet.parseString(hello))
+
+The program outputs the following::
+
+    Hello, World! -> ['Hello', ',', 'World', '!']
+
+The Python representation of the grammar is quite readable, owing to the self-explanatory
+class names, and the use of '+', '|' and '^' operators.
+
+The L{ParseResults} object returned from L{ParserElement.parseString} can be accessed as a nested list, a dictionary, or an
+object with named attributes.
+
+The pyparsing module handles some of the problems that are typically vexing when writing text parsers:
+ - extra or missing whitespace (the above program will also handle "Hello,World!", "Hello  ,  World  !", etc.)
+ - quoted strings
+ - embedded comments
+
+
+Getting Started -
+-----------------
+Visit the classes L{ParserElement} and L{ParseResults} to see the base classes that most other pyparsing
+classes inherit from. Use the docstrings for examples of how to:
+ - construct literal match expressions from L{Literal} and L{CaselessLiteral} classes
+ - construct character word-group expressions using the L{Word} class
+ - see how to create repetitive expressions using L{ZeroOrMore} and L{OneOrMore} classes
+ - use L{'+'}, L{'|'}, L{'^'}, and L{'&'} operators to combine simple expressions into more complex ones
+ - associate names with your parsed results using L{ParserElement.setResultsName}
+ - find some helpful expression short-cuts like L{delimitedList} and L{oneOf}
+ - find more useful common expressions in the L{pyparsing_common} namespace class
+"""
+
+__version__ = "2.2.1"
+__versionTime__ = "18 Sep 2018 00:49 UTC"
+__author__ = "Paul McGuire "
+
+import string
+from weakref import ref as wkref
+import copy
+import sys
+import warnings
+import re
+import sre_constants
+import collections
+import pprint
+import traceback
+import types
+from datetime import datetime
+
+try:
+    from _thread import RLock
+except ImportError:
+    from threading import RLock
+
+try:
+    # Python 3
+    from collections.abc import Iterable
+    from collections.abc import MutableMapping
+except ImportError:
+    # Python 2.7
+    from collections import Iterable
+    from collections import MutableMapping
+
+try:
+    from collections import OrderedDict as _OrderedDict
+except ImportError:
+    try:
+        from ordereddict import OrderedDict as _OrderedDict
+    except ImportError:
+        _OrderedDict = None
+
+#~ sys.stderr.write( "testing pyparsing module, version %s, %s\n" % (__version__,__versionTime__ ) )
+
+__all__ = [
+'And', 'CaselessKeyword', 'CaselessLiteral', 'CharsNotIn', 'Combine', 'Dict', 'Each', 'Empty',
+'FollowedBy', 'Forward', 'GoToColumn', 'Group', 'Keyword', 'LineEnd', 'LineStart', 'Literal',
+'MatchFirst', 'NoMatch', 'NotAny', 'OneOrMore', 'OnlyOnce', 'Optional', 'Or',
+'ParseBaseException', 'ParseElementEnhance', 'ParseException', 'ParseExpression', 'ParseFatalException',
+'ParseResults', 'ParseSyntaxException', 'ParserElement', 'QuotedString', 'RecursiveGrammarException',
+'Regex', 'SkipTo', 'StringEnd', 'StringStart', 'Suppress', 'Token', 'TokenConverter', 
+'White', 'Word', 'WordEnd', 'WordStart', 'ZeroOrMore',
+'alphanums', 'alphas', 'alphas8bit', 'anyCloseTag', 'anyOpenTag', 'cStyleComment', 'col',
+'commaSeparatedList', 'commonHTMLEntity', 'countedArray', 'cppStyleComment', 'dblQuotedString',
+'dblSlashComment', 'delimitedList', 'dictOf', 'downcaseTokens', 'empty', 'hexnums',
+'htmlComment', 'javaStyleComment', 'line', 'lineEnd', 'lineStart', 'lineno',
+'makeHTMLTags', 'makeXMLTags', 'matchOnlyAtCol', 'matchPreviousExpr', 'matchPreviousLiteral',
+'nestedExpr', 'nullDebugAction', 'nums', 'oneOf', 'opAssoc', 'operatorPrecedence', 'printables',
+'punc8bit', 'pythonStyleComment', 'quotedString', 'removeQuotes', 'replaceHTMLEntity', 
+'replaceWith', 'restOfLine', 'sglQuotedString', 'srange', 'stringEnd',
+'stringStart', 'traceParseAction', 'unicodeString', 'upcaseTokens', 'withAttribute',
+'indentedBlock', 'originalTextFor', 'ungroup', 'infixNotation','locatedExpr', 'withClass',
+'CloseMatch', 'tokenMap', 'pyparsing_common',
+]
+
+system_version = tuple(sys.version_info)[:3]
+PY_3 = system_version[0] == 3
+if PY_3:
+    _MAX_INT = sys.maxsize
+    basestring = str
+    unichr = chr
+    _ustr = str
+
+    # build list of single arg builtins, that can be used as parse actions
+    singleArgBuiltins = [sum, len, sorted, reversed, list, tuple, set, any, all, min, max]
+
+else:
+    _MAX_INT = sys.maxint
+    range = xrange
+
+    def _ustr(obj):
+        """Drop-in replacement for str(obj) that tries to be Unicode friendly. It first tries
+           str(obj). If that fails with a UnicodeEncodeError, then it tries unicode(obj). It
+           then < returns the unicode object | encodes it with the default encoding | ... >.
+        """
+        if isinstance(obj,unicode):
+            return obj
+
+        try:
+            # If this works, then _ustr(obj) has the same behaviour as str(obj), so
+            # it won't break any existing code.
+            return str(obj)
+
+        except UnicodeEncodeError:
+            # Else encode it
+            ret = unicode(obj).encode(sys.getdefaultencoding(), 'xmlcharrefreplace')
+            xmlcharref = Regex(r'&#\d+;')
+            xmlcharref.setParseAction(lambda t: '\\u' + hex(int(t[0][2:-1]))[2:])
+            return xmlcharref.transformString(ret)
+
+    # build list of single arg builtins, tolerant of Python version, that can be used as parse actions
+    singleArgBuiltins = []
+    import __builtin__
+    for fname in "sum len sorted reversed list tuple set any all min max".split():
+        try:
+            singleArgBuiltins.append(getattr(__builtin__,fname))
+        except AttributeError:
+            continue
+            
+_generatorType = type((y for y in range(1)))
+ 
+def _xml_escape(data):
+    """Escape &, <, >, ", ', etc. in a string of data."""
+
+    # ampersand must be replaced first
+    from_symbols = '&><"\''
+    to_symbols = ('&'+s+';' for s in "amp gt lt quot apos".split())
+    for from_,to_ in zip(from_symbols, to_symbols):
+        data = data.replace(from_, to_)
+    return data
+
+class _Constants(object):
+    pass
+
+alphas     = string.ascii_uppercase + string.ascii_lowercase
+nums       = "0123456789"
+hexnums    = nums + "ABCDEFabcdef"
+alphanums  = alphas + nums
+_bslash    = chr(92)
+printables = "".join(c for c in string.printable if c not in string.whitespace)
+
+class ParseBaseException(Exception):
+    """base exception class for all parsing runtime exceptions"""
+    # Performance tuning: we construct a *lot* of these, so keep this
+    # constructor as small and fast as possible
+    def __init__( self, pstr, loc=0, msg=None, elem=None ):
+        self.loc = loc
+        if msg is None:
+            self.msg = pstr
+            self.pstr = ""
+        else:
+            self.msg = msg
+            self.pstr = pstr
+        self.parserElement = elem
+        self.args = (pstr, loc, msg)
+
+    @classmethod
+    def _from_exception(cls, pe):
+        """
+        internal factory method to simplify creating one type of ParseException 
+        from another - avoids having __init__ signature conflicts among subclasses
+        """
+        return cls(pe.pstr, pe.loc, pe.msg, pe.parserElement)
+
+    def __getattr__( self, aname ):
+        """supported attributes by name are:
+            - lineno - returns the line number of the exception text
+            - col - returns the column number of the exception text
+            - line - returns the line containing the exception text
+        """
+        if( aname == "lineno" ):
+            return lineno( self.loc, self.pstr )
+        elif( aname in ("col", "column") ):
+            return col( self.loc, self.pstr )
+        elif( aname == "line" ):
+            return line( self.loc, self.pstr )
+        else:
+            raise AttributeError(aname)
+
+    def __str__( self ):
+        return "%s (at char %d), (line:%d, col:%d)" % \
+                ( self.msg, self.loc, self.lineno, self.column )
+    def __repr__( self ):
+        return _ustr(self)
+    def markInputline( self, markerString = ">!<" ):
+        """Extracts the exception line from the input string, and marks
+           the location of the exception with a special symbol.
+        """
+        line_str = self.line
+        line_column = self.column - 1
+        if markerString:
+            line_str = "".join((line_str[:line_column],
+                                markerString, line_str[line_column:]))
+        return line_str.strip()
+    def __dir__(self):
+        return "lineno col line".split() + dir(type(self))
+
+class ParseException(ParseBaseException):
+    """
+    Exception thrown when parse expressions don't match class;
+    supported attributes by name are:
+     - lineno - returns the line number of the exception text
+     - col - returns the column number of the exception text
+     - line - returns the line containing the exception text
+        
+    Example::
+        try:
+            Word(nums).setName("integer").parseString("ABC")
+        except ParseException as pe:
+            print(pe)
+            print("column: {}".format(pe.col))
+            
+    prints::
+       Expected integer (at char 0), (line:1, col:1)
+        column: 1
+    """
+    pass
+
+class ParseFatalException(ParseBaseException):
+    """user-throwable exception thrown when inconsistent parse content
+       is found; stops all parsing immediately"""
+    pass
+
+class ParseSyntaxException(ParseFatalException):
+    """just like L{ParseFatalException}, but thrown internally when an
+       L{ErrorStop} ('-' operator) indicates that parsing is to stop 
+       immediately because an unbacktrackable syntax error has been found"""
+    pass
+
+#~ class ReparseException(ParseBaseException):
+    #~ """Experimental class - parse actions can raise this exception to cause
+       #~ pyparsing to reparse the input string:
+        #~ - with a modified input string, and/or
+        #~ - with a modified start location
+       #~ Set the values of the ReparseException in the constructor, and raise the
+       #~ exception in a parse action to cause pyparsing to use the new string/location.
+       #~ Setting the values as None causes no change to be made.
+       #~ """
+    #~ def __init_( self, newstring, restartLoc ):
+        #~ self.newParseText = newstring
+        #~ self.reparseLoc = restartLoc
+
+class RecursiveGrammarException(Exception):
+    """exception thrown by L{ParserElement.validate} if the grammar could be improperly recursive"""
+    def __init__( self, parseElementList ):
+        self.parseElementTrace = parseElementList
+
+    def __str__( self ):
+        return "RecursiveGrammarException: %s" % self.parseElementTrace
+
+class _ParseResultsWithOffset(object):
+    def __init__(self,p1,p2):
+        self.tup = (p1,p2)
+    def __getitem__(self,i):
+        return self.tup[i]
+    def __repr__(self):
+        return repr(self.tup[0])
+    def setOffset(self,i):
+        self.tup = (self.tup[0],i)
+
+class ParseResults(object):
+    """
+    Structured parse results, to provide multiple means of access to the parsed data:
+       - as a list (C{len(results)})
+       - by list index (C{results[0], results[1]}, etc.)
+       - by attribute (C{results.} - see L{ParserElement.setResultsName})
+
+    Example::
+        integer = Word(nums)
+        date_str = (integer.setResultsName("year") + '/' 
+                        + integer.setResultsName("month") + '/' 
+                        + integer.setResultsName("day"))
+        # equivalent form:
+        # date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
+
+        # parseString returns a ParseResults object
+        result = date_str.parseString("1999/12/31")
+
+        def test(s, fn=repr):
+            print("%s -> %s" % (s, fn(eval(s))))
+        test("list(result)")
+        test("result[0]")
+        test("result['month']")
+        test("result.day")
+        test("'month' in result")
+        test("'minutes' in result")
+        test("result.dump()", str)
+    prints::
+        list(result) -> ['1999', '/', '12', '/', '31']
+        result[0] -> '1999'
+        result['month'] -> '12'
+        result.day -> '31'
+        'month' in result -> True
+        'minutes' in result -> False
+        result.dump() -> ['1999', '/', '12', '/', '31']
+        - day: 31
+        - month: 12
+        - year: 1999
+    """
+    def __new__(cls, toklist=None, name=None, asList=True, modal=True ):
+        if isinstance(toklist, cls):
+            return toklist
+        retobj = object.__new__(cls)
+        retobj.__doinit = True
+        return retobj
+
+    # Performance tuning: we construct a *lot* of these, so keep this
+    # constructor as small and fast as possible
+    def __init__( self, toklist=None, name=None, asList=True, modal=True, isinstance=isinstance ):
+        if self.__doinit:
+            self.__doinit = False
+            self.__name = None
+            self.__parent = None
+            self.__accumNames = {}
+            self.__asList = asList
+            self.__modal = modal
+            if toklist is None:
+                toklist = []
+            if isinstance(toklist, list):
+                self.__toklist = toklist[:]
+            elif isinstance(toklist, _generatorType):
+                self.__toklist = list(toklist)
+            else:
+                self.__toklist = [toklist]
+            self.__tokdict = dict()
+
+        if name is not None and name:
+            if not modal:
+                self.__accumNames[name] = 0
+            if isinstance(name,int):
+                name = _ustr(name) # will always return a str, but use _ustr for consistency
+            self.__name = name
+            if not (isinstance(toklist, (type(None), basestring, list)) and toklist in (None,'',[])):
+                if isinstance(toklist,basestring):
+                    toklist = [ toklist ]
+                if asList:
+                    if isinstance(toklist,ParseResults):
+                        self[name] = _ParseResultsWithOffset(toklist.copy(),0)
+                    else:
+                        self[name] = _ParseResultsWithOffset(ParseResults(toklist[0]),0)
+                    self[name].__name = name
+                else:
+                    try:
+                        self[name] = toklist[0]
+                    except (KeyError,TypeError,IndexError):
+                        self[name] = toklist
+
+    def __getitem__( self, i ):
+        if isinstance( i, (int,slice) ):
+            return self.__toklist[i]
+        else:
+            if i not in self.__accumNames:
+                return self.__tokdict[i][-1][0]
+            else:
+                return ParseResults([ v[0] for v in self.__tokdict[i] ])
+
+    def __setitem__( self, k, v, isinstance=isinstance ):
+        if isinstance(v,_ParseResultsWithOffset):
+            self.__tokdict[k] = self.__tokdict.get(k,list()) + [v]
+            sub = v[0]
+        elif isinstance(k,(int,slice)):
+            self.__toklist[k] = v
+            sub = v
+        else:
+            self.__tokdict[k] = self.__tokdict.get(k,list()) + [_ParseResultsWithOffset(v,0)]
+            sub = v
+        if isinstance(sub,ParseResults):
+            sub.__parent = wkref(self)
+
+    def __delitem__( self, i ):
+        if isinstance(i,(int,slice)):
+            mylen = len( self.__toklist )
+            del self.__toklist[i]
+
+            # convert int to slice
+            if isinstance(i, int):
+                if i < 0:
+                    i += mylen
+                i = slice(i, i+1)
+            # get removed indices
+            removed = list(range(*i.indices(mylen)))
+            removed.reverse()
+            # fixup indices in token dictionary
+            for name,occurrences in self.__tokdict.items():
+                for j in removed:
+                    for k, (value, position) in enumerate(occurrences):
+                        occurrences[k] = _ParseResultsWithOffset(value, position - (position > j))
+        else:
+            del self.__tokdict[i]
+
+    def __contains__( self, k ):
+        return k in self.__tokdict
+
+    def __len__( self ): return len( self.__toklist )
+    def __bool__(self): return ( not not self.__toklist )
+    __nonzero__ = __bool__
+    def __iter__( self ): return iter( self.__toklist )
+    def __reversed__( self ): return iter( self.__toklist[::-1] )
+    def _iterkeys( self ):
+        if hasattr(self.__tokdict, "iterkeys"):
+            return self.__tokdict.iterkeys()
+        else:
+            return iter(self.__tokdict)
+
+    def _itervalues( self ):
+        return (self[k] for k in self._iterkeys())
+            
+    def _iteritems( self ):
+        return ((k, self[k]) for k in self._iterkeys())
+
+    if PY_3:
+        keys = _iterkeys       
+        """Returns an iterator of all named result keys (Python 3.x only)."""
+
+        values = _itervalues
+        """Returns an iterator of all named result values (Python 3.x only)."""
+
+        items = _iteritems
+        """Returns an iterator of all named result key-value tuples (Python 3.x only)."""
+
+    else:
+        iterkeys = _iterkeys
+        """Returns an iterator of all named result keys (Python 2.x only)."""
+
+        itervalues = _itervalues
+        """Returns an iterator of all named result values (Python 2.x only)."""
+
+        iteritems = _iteritems
+        """Returns an iterator of all named result key-value tuples (Python 2.x only)."""
+
+        def keys( self ):
+            """Returns all named result keys (as a list in Python 2.x, as an iterator in Python 3.x)."""
+            return list(self.iterkeys())
+
+        def values( self ):
+            """Returns all named result values (as a list in Python 2.x, as an iterator in Python 3.x)."""
+            return list(self.itervalues())
+                
+        def items( self ):
+            """Returns all named result key-values (as a list of tuples in Python 2.x, as an iterator in Python 3.x)."""
+            return list(self.iteritems())
+
+    def haskeys( self ):
+        """Since keys() returns an iterator, this method is helpful in bypassing
+           code that looks for the existence of any defined results names."""
+        return bool(self.__tokdict)
+        
+    def pop( self, *args, **kwargs):
+        """
+        Removes and returns item at specified index (default=C{last}).
+        Supports both C{list} and C{dict} semantics for C{pop()}. If passed no
+        argument or an integer argument, it will use C{list} semantics
+        and pop tokens from the list of parsed tokens. If passed a 
+        non-integer argument (most likely a string), it will use C{dict}
+        semantics and pop the corresponding value from any defined 
+        results names. A second default return value argument is 
+        supported, just as in C{dict.pop()}.
+
+        Example::
+            def remove_first(tokens):
+                tokens.pop(0)
+            print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321']
+            print(OneOrMore(Word(nums)).addParseAction(remove_first).parseString("0 123 321")) # -> ['123', '321']
+
+            label = Word(alphas)
+            patt = label("LABEL") + OneOrMore(Word(nums))
+            print(patt.parseString("AAB 123 321").dump())
+
+            # Use pop() in a parse action to remove named result (note that corresponding value is not
+            # removed from list form of results)
+            def remove_LABEL(tokens):
+                tokens.pop("LABEL")
+                return tokens
+            patt.addParseAction(remove_LABEL)
+            print(patt.parseString("AAB 123 321").dump())
+        prints::
+            ['AAB', '123', '321']
+            - LABEL: AAB
+
+            ['AAB', '123', '321']
+        """
+        if not args:
+            args = [-1]
+        for k,v in kwargs.items():
+            if k == 'default':
+                args = (args[0], v)
+            else:
+                raise TypeError("pop() got an unexpected keyword argument '%s'" % k)
+        if (isinstance(args[0], int) or 
+                        len(args) == 1 or 
+                        args[0] in self):
+            index = args[0]
+            ret = self[index]
+            del self[index]
+            return ret
+        else:
+            defaultvalue = args[1]
+            return defaultvalue
+
+    def get(self, key, defaultValue=None):
+        """
+        Returns named result matching the given key, or if there is no
+        such name, then returns the given C{defaultValue} or C{None} if no
+        C{defaultValue} is specified.
+
+        Similar to C{dict.get()}.
+        
+        Example::
+            integer = Word(nums)
+            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")           
+
+            result = date_str.parseString("1999/12/31")
+            print(result.get("year")) # -> '1999'
+            print(result.get("hour", "not specified")) # -> 'not specified'
+            print(result.get("hour")) # -> None
+        """
+        if key in self:
+            return self[key]
+        else:
+            return defaultValue
+
+    def insert( self, index, insStr ):
+        """
+        Inserts new element at location index in the list of parsed tokens.
+        
+        Similar to C{list.insert()}.
+
+        Example::
+            print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321']
+
+            # use a parse action to insert the parse location in the front of the parsed results
+            def insert_locn(locn, tokens):
+                tokens.insert(0, locn)
+            print(OneOrMore(Word(nums)).addParseAction(insert_locn).parseString("0 123 321")) # -> [0, '0', '123', '321']
+        """
+        self.__toklist.insert(index, insStr)
+        # fixup indices in token dictionary
+        for name,occurrences in self.__tokdict.items():
+            for k, (value, position) in enumerate(occurrences):
+                occurrences[k] = _ParseResultsWithOffset(value, position + (position > index))
+
+    def append( self, item ):
+        """
+        Add single element to end of ParseResults list of elements.
+
+        Example::
+            print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321']
+            
+            # use a parse action to compute the sum of the parsed integers, and add it to the end
+            def append_sum(tokens):
+                tokens.append(sum(map(int, tokens)))
+            print(OneOrMore(Word(nums)).addParseAction(append_sum).parseString("0 123 321")) # -> ['0', '123', '321', 444]
+        """
+        self.__toklist.append(item)
+
+    def extend( self, itemseq ):
+        """
+        Add sequence of elements to end of ParseResults list of elements.
+
+        Example::
+            patt = OneOrMore(Word(alphas))
+            
+            # use a parse action to append the reverse of the matched strings, to make a palindrome
+            def make_palindrome(tokens):
+                tokens.extend(reversed([t[::-1] for t in tokens]))
+                return ''.join(tokens)
+            print(patt.addParseAction(make_palindrome).parseString("lskdj sdlkjf lksd")) # -> 'lskdjsdlkjflksddsklfjkldsjdksl'
+        """
+        if isinstance(itemseq, ParseResults):
+            self += itemseq
+        else:
+            self.__toklist.extend(itemseq)
+
+    def clear( self ):
+        """
+        Clear all elements and results names.
+        """
+        del self.__toklist[:]
+        self.__tokdict.clear()
+
+    def __getattr__( self, name ):
+        try:
+            return self[name]
+        except KeyError:
+            return ""
+            
+        if name in self.__tokdict:
+            if name not in self.__accumNames:
+                return self.__tokdict[name][-1][0]
+            else:
+                return ParseResults([ v[0] for v in self.__tokdict[name] ])
+        else:
+            return ""
+
+    def __add__( self, other ):
+        ret = self.copy()
+        ret += other
+        return ret
+
+    def __iadd__( self, other ):
+        if other.__tokdict:
+            offset = len(self.__toklist)
+            addoffset = lambda a: offset if a<0 else a+offset
+            otheritems = other.__tokdict.items()
+            otherdictitems = [(k, _ParseResultsWithOffset(v[0],addoffset(v[1])) )
+                                for (k,vlist) in otheritems for v in vlist]
+            for k,v in otherdictitems:
+                self[k] = v
+                if isinstance(v[0],ParseResults):
+                    v[0].__parent = wkref(self)
+            
+        self.__toklist += other.__toklist
+        self.__accumNames.update( other.__accumNames )
+        return self
+
+    def __radd__(self, other):
+        if isinstance(other,int) and other == 0:
+            # useful for merging many ParseResults using sum() builtin
+            return self.copy()
+        else:
+            # this may raise a TypeError - so be it
+            return other + self
+        
+    def __repr__( self ):
+        return "(%s, %s)" % ( repr( self.__toklist ), repr( self.__tokdict ) )
+
+    def __str__( self ):
+        return '[' + ', '.join(_ustr(i) if isinstance(i, ParseResults) else repr(i) for i in self.__toklist) + ']'
+
+    def _asStringList( self, sep='' ):
+        out = []
+        for item in self.__toklist:
+            if out and sep:
+                out.append(sep)
+            if isinstance( item, ParseResults ):
+                out += item._asStringList()
+            else:
+                out.append( _ustr(item) )
+        return out
+
+    def asList( self ):
+        """
+        Returns the parse results as a nested list of matching tokens, all converted to strings.
+
+        Example::
+            patt = OneOrMore(Word(alphas))
+            result = patt.parseString("sldkj lsdkj sldkj")
+            # even though the result prints in string-like form, it is actually a pyparsing ParseResults
+            print(type(result), result) # ->  ['sldkj', 'lsdkj', 'sldkj']
+            
+            # Use asList() to create an actual list
+            result_list = result.asList()
+            print(type(result_list), result_list) # ->  ['sldkj', 'lsdkj', 'sldkj']
+        """
+        return [res.asList() if isinstance(res,ParseResults) else res for res in self.__toklist]
+
+    def asDict( self ):
+        """
+        Returns the named parse results as a nested dictionary.
+
+        Example::
+            integer = Word(nums)
+            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
+            
+            result = date_str.parseString('12/31/1999')
+            print(type(result), repr(result)) # ->  (['12', '/', '31', '/', '1999'], {'day': [('1999', 4)], 'year': [('12', 0)], 'month': [('31', 2)]})
+            
+            result_dict = result.asDict()
+            print(type(result_dict), repr(result_dict)) # ->  {'day': '1999', 'year': '12', 'month': '31'}
+
+            # even though a ParseResults supports dict-like access, sometime you just need to have a dict
+            import json
+            print(json.dumps(result)) # -> Exception: TypeError: ... is not JSON serializable
+            print(json.dumps(result.asDict())) # -> {"month": "31", "day": "1999", "year": "12"}
+        """
+        if PY_3:
+            item_fn = self.items
+        else:
+            item_fn = self.iteritems
+            
+        def toItem(obj):
+            if isinstance(obj, ParseResults):
+                if obj.haskeys():
+                    return obj.asDict()
+                else:
+                    return [toItem(v) for v in obj]
+            else:
+                return obj
+                
+        return dict((k,toItem(v)) for k,v in item_fn())
+
+    def copy( self ):
+        """
+        Returns a new copy of a C{ParseResults} object.
+        """
+        ret = ParseResults( self.__toklist )
+        ret.__tokdict = self.__tokdict.copy()
+        ret.__parent = self.__parent
+        ret.__accumNames.update( self.__accumNames )
+        ret.__name = self.__name
+        return ret
+
+    def asXML( self, doctag=None, namedItemsOnly=False, indent="", formatted=True ):
+        """
+        (Deprecated) Returns the parse results as XML. Tags are created for tokens and lists that have defined results names.
+        """
+        nl = "\n"
+        out = []
+        namedItems = dict((v[1],k) for (k,vlist) in self.__tokdict.items()
+                                                            for v in vlist)
+        nextLevelIndent = indent + "  "
+
+        # collapse out indents if formatting is not desired
+        if not formatted:
+            indent = ""
+            nextLevelIndent = ""
+            nl = ""
+
+        selfTag = None
+        if doctag is not None:
+            selfTag = doctag
+        else:
+            if self.__name:
+                selfTag = self.__name
+
+        if not selfTag:
+            if namedItemsOnly:
+                return ""
+            else:
+                selfTag = "ITEM"
+
+        out += [ nl, indent, "<", selfTag, ">" ]
+
+        for i,res in enumerate(self.__toklist):
+            if isinstance(res,ParseResults):
+                if i in namedItems:
+                    out += [ res.asXML(namedItems[i],
+                                        namedItemsOnly and doctag is None,
+                                        nextLevelIndent,
+                                        formatted)]
+                else:
+                    out += [ res.asXML(None,
+                                        namedItemsOnly and doctag is None,
+                                        nextLevelIndent,
+                                        formatted)]
+            else:
+                # individual token, see if there is a name for it
+                resTag = None
+                if i in namedItems:
+                    resTag = namedItems[i]
+                if not resTag:
+                    if namedItemsOnly:
+                        continue
+                    else:
+                        resTag = "ITEM"
+                xmlBodyText = _xml_escape(_ustr(res))
+                out += [ nl, nextLevelIndent, "<", resTag, ">",
+                                                xmlBodyText,
+                                                "" ]
+
+        out += [ nl, indent, "" ]
+        return "".join(out)
+
+    def __lookup(self,sub):
+        for k,vlist in self.__tokdict.items():
+            for v,loc in vlist:
+                if sub is v:
+                    return k
+        return None
+
+    def getName(self):
+        r"""
+        Returns the results name for this token expression. Useful when several 
+        different expressions might match at a particular location.
+
+        Example::
+            integer = Word(nums)
+            ssn_expr = Regex(r"\d\d\d-\d\d-\d\d\d\d")
+            house_number_expr = Suppress('#') + Word(nums, alphanums)
+            user_data = (Group(house_number_expr)("house_number") 
+                        | Group(ssn_expr)("ssn")
+                        | Group(integer)("age"))
+            user_info = OneOrMore(user_data)
+            
+            result = user_info.parseString("22 111-22-3333 #221B")
+            for item in result:
+                print(item.getName(), ':', item[0])
+        prints::
+            age : 22
+            ssn : 111-22-3333
+            house_number : 221B
+        """
+        if self.__name:
+            return self.__name
+        elif self.__parent:
+            par = self.__parent()
+            if par:
+                return par.__lookup(self)
+            else:
+                return None
+        elif (len(self) == 1 and
+               len(self.__tokdict) == 1 and
+               next(iter(self.__tokdict.values()))[0][1] in (0,-1)):
+            return next(iter(self.__tokdict.keys()))
+        else:
+            return None
+
+    def dump(self, indent='', depth=0, full=True):
+        """
+        Diagnostic method for listing out the contents of a C{ParseResults}.
+        Accepts an optional C{indent} argument so that this string can be embedded
+        in a nested display of other data.
+
+        Example::
+            integer = Word(nums)
+            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
+            
+            result = date_str.parseString('12/31/1999')
+            print(result.dump())
+        prints::
+            ['12', '/', '31', '/', '1999']
+            - day: 1999
+            - month: 31
+            - year: 12
+        """
+        out = []
+        NL = '\n'
+        out.append( indent+_ustr(self.asList()) )
+        if full:
+            if self.haskeys():
+                items = sorted((str(k), v) for k,v in self.items())
+                for k,v in items:
+                    if out:
+                        out.append(NL)
+                    out.append( "%s%s- %s: " % (indent,('  '*depth), k) )
+                    if isinstance(v,ParseResults):
+                        if v:
+                            out.append( v.dump(indent,depth+1) )
+                        else:
+                            out.append(_ustr(v))
+                    else:
+                        out.append(repr(v))
+            elif any(isinstance(vv,ParseResults) for vv in self):
+                v = self
+                for i,vv in enumerate(v):
+                    if isinstance(vv,ParseResults):
+                        out.append("\n%s%s[%d]:\n%s%s%s" % (indent,('  '*(depth)),i,indent,('  '*(depth+1)),vv.dump(indent,depth+1) ))
+                    else:
+                        out.append("\n%s%s[%d]:\n%s%s%s" % (indent,('  '*(depth)),i,indent,('  '*(depth+1)),_ustr(vv)))
+            
+        return "".join(out)
+
+    def pprint(self, *args, **kwargs):
+        """
+        Pretty-printer for parsed results as a list, using the C{pprint} module.
+        Accepts additional positional or keyword args as defined for the 
+        C{pprint.pprint} method. (U{http://docs.python.org/3/library/pprint.html#pprint.pprint})
+
+        Example::
+            ident = Word(alphas, alphanums)
+            num = Word(nums)
+            func = Forward()
+            term = ident | num | Group('(' + func + ')')
+            func <<= ident + Group(Optional(delimitedList(term)))
+            result = func.parseString("fna a,b,(fnb c,d,200),100")
+            result.pprint(width=40)
+        prints::
+            ['fna',
+             ['a',
+              'b',
+              ['(', 'fnb', ['c', 'd', '200'], ')'],
+              '100']]
+        """
+        pprint.pprint(self.asList(), *args, **kwargs)
+
+    # add support for pickle protocol
+    def __getstate__(self):
+        return ( self.__toklist,
+                 ( self.__tokdict.copy(),
+                   self.__parent is not None and self.__parent() or None,
+                   self.__accumNames,
+                   self.__name ) )
+
+    def __setstate__(self,state):
+        self.__toklist = state[0]
+        (self.__tokdict,
+         par,
+         inAccumNames,
+         self.__name) = state[1]
+        self.__accumNames = {}
+        self.__accumNames.update(inAccumNames)
+        if par is not None:
+            self.__parent = wkref(par)
+        else:
+            self.__parent = None
+
+    def __getnewargs__(self):
+        return self.__toklist, self.__name, self.__asList, self.__modal
+
+    def __dir__(self):
+        return (dir(type(self)) + list(self.keys()))
+
+MutableMapping.register(ParseResults)
+
+def col (loc,strg):
+    """Returns current column within a string, counting newlines as line separators.
+   The first column is number 1.
+
+   Note: the default parsing behavior is to expand tabs in the input string
+   before starting the parsing process.  See L{I{ParserElement.parseString}} for more information
+   on parsing strings containing C{}s, and suggested methods to maintain a
+   consistent view of the parsed string, the parse location, and line and column
+   positions within the parsed string.
+   """
+    s = strg
+    return 1 if 0} for more information
+   on parsing strings containing C{}s, and suggested methods to maintain a
+   consistent view of the parsed string, the parse location, and line and column
+   positions within the parsed string.
+   """
+    return strg.count("\n",0,loc) + 1
+
+def line( loc, strg ):
+    """Returns the line of text containing loc within a string, counting newlines as line separators.
+       """
+    lastCR = strg.rfind("\n", 0, loc)
+    nextCR = strg.find("\n", loc)
+    if nextCR >= 0:
+        return strg[lastCR+1:nextCR]
+    else:
+        return strg[lastCR+1:]
+
+def _defaultStartDebugAction( instring, loc, expr ):
+    print (("Match " + _ustr(expr) + " at loc " + _ustr(loc) + "(%d,%d)" % ( lineno(loc,instring), col(loc,instring) )))
+
+def _defaultSuccessDebugAction( instring, startloc, endloc, expr, toks ):
+    print ("Matched " + _ustr(expr) + " -> " + str(toks.asList()))
+
+def _defaultExceptionDebugAction( instring, loc, expr, exc ):
+    print ("Exception raised:" + _ustr(exc))
+
+def nullDebugAction(*args):
+    """'Do-nothing' debug action, to suppress debugging output during parsing."""
+    pass
+
+# Only works on Python 3.x - nonlocal is toxic to Python 2 installs
+#~ 'decorator to trim function calls to match the arity of the target'
+#~ def _trim_arity(func, maxargs=3):
+    #~ if func in singleArgBuiltins:
+        #~ return lambda s,l,t: func(t)
+    #~ limit = 0
+    #~ foundArity = False
+    #~ def wrapper(*args):
+        #~ nonlocal limit,foundArity
+        #~ while 1:
+            #~ try:
+                #~ ret = func(*args[limit:])
+                #~ foundArity = True
+                #~ return ret
+            #~ except TypeError:
+                #~ if limit == maxargs or foundArity:
+                    #~ raise
+                #~ limit += 1
+                #~ continue
+    #~ return wrapper
+
+# this version is Python 2.x-3.x cross-compatible
+'decorator to trim function calls to match the arity of the target'
+def _trim_arity(func, maxargs=2):
+    if func in singleArgBuiltins:
+        return lambda s,l,t: func(t)
+    limit = [0]
+    foundArity = [False]
+    
+    # traceback return data structure changed in Py3.5 - normalize back to plain tuples
+    if system_version[:2] >= (3,5):
+        def extract_stack(limit=0):
+            # special handling for Python 3.5.0 - extra deep call stack by 1
+            offset = -3 if system_version == (3,5,0) else -2
+            frame_summary = traceback.extract_stack(limit=-offset+limit-1)[offset]
+            return [frame_summary[:2]]
+        def extract_tb(tb, limit=0):
+            frames = traceback.extract_tb(tb, limit=limit)
+            frame_summary = frames[-1]
+            return [frame_summary[:2]]
+    else:
+        extract_stack = traceback.extract_stack
+        extract_tb = traceback.extract_tb
+    
+    # synthesize what would be returned by traceback.extract_stack at the call to 
+    # user's parse action 'func', so that we don't incur call penalty at parse time
+    
+    LINE_DIFF = 6
+    # IF ANY CODE CHANGES, EVEN JUST COMMENTS OR BLANK LINES, BETWEEN THE NEXT LINE AND 
+    # THE CALL TO FUNC INSIDE WRAPPER, LINE_DIFF MUST BE MODIFIED!!!!
+    this_line = extract_stack(limit=2)[-1]
+    pa_call_line_synth = (this_line[0], this_line[1]+LINE_DIFF)
+
+    def wrapper(*args):
+        while 1:
+            try:
+                ret = func(*args[limit[0]:])
+                foundArity[0] = True
+                return ret
+            except TypeError:
+                # re-raise TypeErrors if they did not come from our arity testing
+                if foundArity[0]:
+                    raise
+                else:
+                    try:
+                        tb = sys.exc_info()[-1]
+                        if not extract_tb(tb, limit=2)[-1][:2] == pa_call_line_synth:
+                            raise
+                    finally:
+                        del tb
+
+                if limit[0] <= maxargs:
+                    limit[0] += 1
+                    continue
+                raise
+
+    # copy func name to wrapper for sensible debug output
+    func_name = ""
+    try:
+        func_name = getattr(func, '__name__', 
+                            getattr(func, '__class__').__name__)
+    except Exception:
+        func_name = str(func)
+    wrapper.__name__ = func_name
+
+    return wrapper
+
+class ParserElement(object):
+    """Abstract base level parser element class."""
+    DEFAULT_WHITE_CHARS = " \n\t\r"
+    verbose_stacktrace = False
+
+    @staticmethod
+    def setDefaultWhitespaceChars( chars ):
+        r"""
+        Overrides the default whitespace chars
+
+        Example::
+            # default whitespace chars are space,  and newline
+            OneOrMore(Word(alphas)).parseString("abc def\nghi jkl")  # -> ['abc', 'def', 'ghi', 'jkl']
+            
+            # change to just treat newline as significant
+            ParserElement.setDefaultWhitespaceChars(" \t")
+            OneOrMore(Word(alphas)).parseString("abc def\nghi jkl")  # -> ['abc', 'def']
+        """
+        ParserElement.DEFAULT_WHITE_CHARS = chars
+
+    @staticmethod
+    def inlineLiteralsUsing(cls):
+        """
+        Set class to be used for inclusion of string literals into a parser.
+        
+        Example::
+            # default literal class used is Literal
+            integer = Word(nums)
+            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")           
+
+            date_str.parseString("1999/12/31")  # -> ['1999', '/', '12', '/', '31']
+
+
+            # change to Suppress
+            ParserElement.inlineLiteralsUsing(Suppress)
+            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")           
+
+            date_str.parseString("1999/12/31")  # -> ['1999', '12', '31']
+        """
+        ParserElement._literalStringClass = cls
+
+    def __init__( self, savelist=False ):
+        self.parseAction = list()
+        self.failAction = None
+        #~ self.name = ""  # don't define self.name, let subclasses try/except upcall
+        self.strRepr = None
+        self.resultsName = None
+        self.saveAsList = savelist
+        self.skipWhitespace = True
+        self.whiteChars = ParserElement.DEFAULT_WHITE_CHARS
+        self.copyDefaultWhiteChars = True
+        self.mayReturnEmpty = False # used when checking for left-recursion
+        self.keepTabs = False
+        self.ignoreExprs = list()
+        self.debug = False
+        self.streamlined = False
+        self.mayIndexError = True # used to optimize exception handling for subclasses that don't advance parse index
+        self.errmsg = ""
+        self.modalResults = True # used to mark results names as modal (report only last) or cumulative (list all)
+        self.debugActions = ( None, None, None ) #custom debug actions
+        self.re = None
+        self.callPreparse = True # used to avoid redundant calls to preParse
+        self.callDuringTry = False
+
+    def copy( self ):
+        """
+        Make a copy of this C{ParserElement}.  Useful for defining different parse actions
+        for the same parsing pattern, using copies of the original parse element.
+        
+        Example::
+            integer = Word(nums).setParseAction(lambda toks: int(toks[0]))
+            integerK = integer.copy().addParseAction(lambda toks: toks[0]*1024) + Suppress("K")
+            integerM = integer.copy().addParseAction(lambda toks: toks[0]*1024*1024) + Suppress("M")
+            
+            print(OneOrMore(integerK | integerM | integer).parseString("5K 100 640K 256M"))
+        prints::
+            [5120, 100, 655360, 268435456]
+        Equivalent form of C{expr.copy()} is just C{expr()}::
+            integerM = integer().addParseAction(lambda toks: toks[0]*1024*1024) + Suppress("M")
+        """
+        cpy = copy.copy( self )
+        cpy.parseAction = self.parseAction[:]
+        cpy.ignoreExprs = self.ignoreExprs[:]
+        if self.copyDefaultWhiteChars:
+            cpy.whiteChars = ParserElement.DEFAULT_WHITE_CHARS
+        return cpy
+
+    def setName( self, name ):
+        """
+        Define name for this expression, makes debugging and exception messages clearer.
+        
+        Example::
+            Word(nums).parseString("ABC")  # -> Exception: Expected W:(0123...) (at char 0), (line:1, col:1)
+            Word(nums).setName("integer").parseString("ABC")  # -> Exception: Expected integer (at char 0), (line:1, col:1)
+        """
+        self.name = name
+        self.errmsg = "Expected " + self.name
+        if hasattr(self,"exception"):
+            self.exception.msg = self.errmsg
+        return self
+
+    def setResultsName( self, name, listAllMatches=False ):
+        """
+        Define name for referencing matching tokens as a nested attribute
+        of the returned parse results.
+        NOTE: this returns a *copy* of the original C{ParserElement} object;
+        this is so that the client can define a basic element, such as an
+        integer, and reference it in multiple places with different names.
+
+        You can also set results names using the abbreviated syntax,
+        C{expr("name")} in place of C{expr.setResultsName("name")} - 
+        see L{I{__call__}<__call__>}.
+
+        Example::
+            date_str = (integer.setResultsName("year") + '/' 
+                        + integer.setResultsName("month") + '/' 
+                        + integer.setResultsName("day"))
+
+            # equivalent form:
+            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
+        """
+        newself = self.copy()
+        if name.endswith("*"):
+            name = name[:-1]
+            listAllMatches=True
+        newself.resultsName = name
+        newself.modalResults = not listAllMatches
+        return newself
+
+    def setBreak(self,breakFlag = True):
+        """Method to invoke the Python pdb debugger when this element is
+           about to be parsed. Set C{breakFlag} to True to enable, False to
+           disable.
+        """
+        if breakFlag:
+            _parseMethod = self._parse
+            def breaker(instring, loc, doActions=True, callPreParse=True):
+                import pdb
+                pdb.set_trace()
+                return _parseMethod( instring, loc, doActions, callPreParse )
+            breaker._originalParseMethod = _parseMethod
+            self._parse = breaker
+        else:
+            if hasattr(self._parse,"_originalParseMethod"):
+                self._parse = self._parse._originalParseMethod
+        return self
+
+    def setParseAction( self, *fns, **kwargs ):
+        """
+        Define one or more actions to perform when successfully matching parse element definition.
+        Parse action fn is a callable method with 0-3 arguments, called as C{fn(s,loc,toks)},
+        C{fn(loc,toks)}, C{fn(toks)}, or just C{fn()}, where:
+         - s   = the original string being parsed (see note below)
+         - loc = the location of the matching substring
+         - toks = a list of the matched tokens, packaged as a C{L{ParseResults}} object
+        If the functions in fns modify the tokens, they can return them as the return
+        value from fn, and the modified list of tokens will replace the original.
+        Otherwise, fn does not need to return any value.
+
+        Optional keyword arguments:
+         - callDuringTry = (default=C{False}) indicate if parse action should be run during lookaheads and alternate testing
+
+        Note: the default parsing behavior is to expand tabs in the input string
+        before starting the parsing process.  See L{I{parseString}} for more information
+        on parsing strings containing C{}s, and suggested methods to maintain a
+        consistent view of the parsed string, the parse location, and line and column
+        positions within the parsed string.
+        
+        Example::
+            integer = Word(nums)
+            date_str = integer + '/' + integer + '/' + integer
+
+            date_str.parseString("1999/12/31")  # -> ['1999', '/', '12', '/', '31']
+
+            # use parse action to convert to ints at parse time
+            integer = Word(nums).setParseAction(lambda toks: int(toks[0]))
+            date_str = integer + '/' + integer + '/' + integer
+
+            # note that integer fields are now ints, not strings
+            date_str.parseString("1999/12/31")  # -> [1999, '/', 12, '/', 31]
+        """
+        self.parseAction = list(map(_trim_arity, list(fns)))
+        self.callDuringTry = kwargs.get("callDuringTry", False)
+        return self
+
+    def addParseAction( self, *fns, **kwargs ):
+        """
+        Add one or more parse actions to expression's list of parse actions. See L{I{setParseAction}}.
+        
+        See examples in L{I{copy}}.
+        """
+        self.parseAction += list(map(_trim_arity, list(fns)))
+        self.callDuringTry = self.callDuringTry or kwargs.get("callDuringTry", False)
+        return self
+
+    def addCondition(self, *fns, **kwargs):
+        """Add a boolean predicate function to expression's list of parse actions. See 
+        L{I{setParseAction}} for function call signatures. Unlike C{setParseAction}, 
+        functions passed to C{addCondition} need to return boolean success/fail of the condition.
+
+        Optional keyword arguments:
+         - message = define a custom message to be used in the raised exception
+         - fatal   = if True, will raise ParseFatalException to stop parsing immediately; otherwise will raise ParseException
+         
+        Example::
+            integer = Word(nums).setParseAction(lambda toks: int(toks[0]))
+            year_int = integer.copy()
+            year_int.addCondition(lambda toks: toks[0] >= 2000, message="Only support years 2000 and later")
+            date_str = year_int + '/' + integer + '/' + integer
+
+            result = date_str.parseString("1999/12/31")  # -> Exception: Only support years 2000 and later (at char 0), (line:1, col:1)
+        """
+        msg = kwargs.get("message", "failed user-defined condition")
+        exc_type = ParseFatalException if kwargs.get("fatal", False) else ParseException
+        for fn in fns:
+            def pa(s,l,t):
+                if not bool(_trim_arity(fn)(s,l,t)):
+                    raise exc_type(s,l,msg)
+            self.parseAction.append(pa)
+        self.callDuringTry = self.callDuringTry or kwargs.get("callDuringTry", False)
+        return self
+
+    def setFailAction( self, fn ):
+        """Define action to perform if parsing fails at this expression.
+           Fail acton fn is a callable function that takes the arguments
+           C{fn(s,loc,expr,err)} where:
+            - s = string being parsed
+            - loc = location where expression match was attempted and failed
+            - expr = the parse expression that failed
+            - err = the exception thrown
+           The function returns no value.  It may throw C{L{ParseFatalException}}
+           if it is desired to stop parsing immediately."""
+        self.failAction = fn
+        return self
+
+    def _skipIgnorables( self, instring, loc ):
+        exprsFound = True
+        while exprsFound:
+            exprsFound = False
+            for e in self.ignoreExprs:
+                try:
+                    while 1:
+                        loc,dummy = e._parse( instring, loc )
+                        exprsFound = True
+                except ParseException:
+                    pass
+        return loc
+
+    def preParse( self, instring, loc ):
+        if self.ignoreExprs:
+            loc = self._skipIgnorables( instring, loc )
+
+        if self.skipWhitespace:
+            wt = self.whiteChars
+            instrlen = len(instring)
+            while loc < instrlen and instring[loc] in wt:
+                loc += 1
+
+        return loc
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        return loc, []
+
+    def postParse( self, instring, loc, tokenlist ):
+        return tokenlist
+
+    #~ @profile
+    def _parseNoCache( self, instring, loc, doActions=True, callPreParse=True ):
+        debugging = ( self.debug ) #and doActions )
+
+        if debugging or self.failAction:
+            #~ print ("Match",self,"at loc",loc,"(%d,%d)" % ( lineno(loc,instring), col(loc,instring) ))
+            if (self.debugActions[0] ):
+                self.debugActions[0]( instring, loc, self )
+            if callPreParse and self.callPreparse:
+                preloc = self.preParse( instring, loc )
+            else:
+                preloc = loc
+            tokensStart = preloc
+            try:
+                try:
+                    loc,tokens = self.parseImpl( instring, preloc, doActions )
+                except IndexError:
+                    raise ParseException( instring, len(instring), self.errmsg, self )
+            except ParseBaseException as err:
+                #~ print ("Exception raised:", err)
+                if self.debugActions[2]:
+                    self.debugActions[2]( instring, tokensStart, self, err )
+                if self.failAction:
+                    self.failAction( instring, tokensStart, self, err )
+                raise
+        else:
+            if callPreParse and self.callPreparse:
+                preloc = self.preParse( instring, loc )
+            else:
+                preloc = loc
+            tokensStart = preloc
+            if self.mayIndexError or preloc >= len(instring):
+                try:
+                    loc,tokens = self.parseImpl( instring, preloc, doActions )
+                except IndexError:
+                    raise ParseException( instring, len(instring), self.errmsg, self )
+            else:
+                loc,tokens = self.parseImpl( instring, preloc, doActions )
+
+        tokens = self.postParse( instring, loc, tokens )
+
+        retTokens = ParseResults( tokens, self.resultsName, asList=self.saveAsList, modal=self.modalResults )
+        if self.parseAction and (doActions or self.callDuringTry):
+            if debugging:
+                try:
+                    for fn in self.parseAction:
+                        tokens = fn( instring, tokensStart, retTokens )
+                        if tokens is not None:
+                            retTokens = ParseResults( tokens,
+                                                      self.resultsName,
+                                                      asList=self.saveAsList and isinstance(tokens,(ParseResults,list)),
+                                                      modal=self.modalResults )
+                except ParseBaseException as err:
+                    #~ print "Exception raised in user parse action:", err
+                    if (self.debugActions[2] ):
+                        self.debugActions[2]( instring, tokensStart, self, err )
+                    raise
+            else:
+                for fn in self.parseAction:
+                    tokens = fn( instring, tokensStart, retTokens )
+                    if tokens is not None:
+                        retTokens = ParseResults( tokens,
+                                                  self.resultsName,
+                                                  asList=self.saveAsList and isinstance(tokens,(ParseResults,list)),
+                                                  modal=self.modalResults )
+        if debugging:
+            #~ print ("Matched",self,"->",retTokens.asList())
+            if (self.debugActions[1] ):
+                self.debugActions[1]( instring, tokensStart, loc, self, retTokens )
+
+        return loc, retTokens
+
+    def tryParse( self, instring, loc ):
+        try:
+            return self._parse( instring, loc, doActions=False )[0]
+        except ParseFatalException:
+            raise ParseException( instring, loc, self.errmsg, self)
+    
+    def canParseNext(self, instring, loc):
+        try:
+            self.tryParse(instring, loc)
+        except (ParseException, IndexError):
+            return False
+        else:
+            return True
+
+    class _UnboundedCache(object):
+        def __init__(self):
+            cache = {}
+            self.not_in_cache = not_in_cache = object()
+
+            def get(self, key):
+                return cache.get(key, not_in_cache)
+
+            def set(self, key, value):
+                cache[key] = value
+
+            def clear(self):
+                cache.clear()
+                
+            def cache_len(self):
+                return len(cache)
+
+            self.get = types.MethodType(get, self)
+            self.set = types.MethodType(set, self)
+            self.clear = types.MethodType(clear, self)
+            self.__len__ = types.MethodType(cache_len, self)
+
+    if _OrderedDict is not None:
+        class _FifoCache(object):
+            def __init__(self, size):
+                self.not_in_cache = not_in_cache = object()
+
+                cache = _OrderedDict()
+
+                def get(self, key):
+                    return cache.get(key, not_in_cache)
+
+                def set(self, key, value):
+                    cache[key] = value
+                    while len(cache) > size:
+                        try:
+                            cache.popitem(False)
+                        except KeyError:
+                            pass
+
+                def clear(self):
+                    cache.clear()
+
+                def cache_len(self):
+                    return len(cache)
+
+                self.get = types.MethodType(get, self)
+                self.set = types.MethodType(set, self)
+                self.clear = types.MethodType(clear, self)
+                self.__len__ = types.MethodType(cache_len, self)
+
+    else:
+        class _FifoCache(object):
+            def __init__(self, size):
+                self.not_in_cache = not_in_cache = object()
+
+                cache = {}
+                key_fifo = collections.deque([], size)
+
+                def get(self, key):
+                    return cache.get(key, not_in_cache)
+
+                def set(self, key, value):
+                    cache[key] = value
+                    while len(key_fifo) > size:
+                        cache.pop(key_fifo.popleft(), None)
+                    key_fifo.append(key)
+
+                def clear(self):
+                    cache.clear()
+                    key_fifo.clear()
+
+                def cache_len(self):
+                    return len(cache)
+
+                self.get = types.MethodType(get, self)
+                self.set = types.MethodType(set, self)
+                self.clear = types.MethodType(clear, self)
+                self.__len__ = types.MethodType(cache_len, self)
+
+    # argument cache for optimizing repeated calls when backtracking through recursive expressions
+    packrat_cache = {} # this is set later by enabledPackrat(); this is here so that resetCache() doesn't fail
+    packrat_cache_lock = RLock()
+    packrat_cache_stats = [0, 0]
+
+    # this method gets repeatedly called during backtracking with the same arguments -
+    # we can cache these arguments and save ourselves the trouble of re-parsing the contained expression
+    def _parseCache( self, instring, loc, doActions=True, callPreParse=True ):
+        HIT, MISS = 0, 1
+        lookup = (self, instring, loc, callPreParse, doActions)
+        with ParserElement.packrat_cache_lock:
+            cache = ParserElement.packrat_cache
+            value = cache.get(lookup)
+            if value is cache.not_in_cache:
+                ParserElement.packrat_cache_stats[MISS] += 1
+                try:
+                    value = self._parseNoCache(instring, loc, doActions, callPreParse)
+                except ParseBaseException as pe:
+                    # cache a copy of the exception, without the traceback
+                    cache.set(lookup, pe.__class__(*pe.args))
+                    raise
+                else:
+                    cache.set(lookup, (value[0], value[1].copy()))
+                    return value
+            else:
+                ParserElement.packrat_cache_stats[HIT] += 1
+                if isinstance(value, Exception):
+                    raise value
+                return (value[0], value[1].copy())
+
+    _parse = _parseNoCache
+
+    @staticmethod
+    def resetCache():
+        ParserElement.packrat_cache.clear()
+        ParserElement.packrat_cache_stats[:] = [0] * len(ParserElement.packrat_cache_stats)
+
+    _packratEnabled = False
+    @staticmethod
+    def enablePackrat(cache_size_limit=128):
+        """Enables "packrat" parsing, which adds memoizing to the parsing logic.
+           Repeated parse attempts at the same string location (which happens
+           often in many complex grammars) can immediately return a cached value,
+           instead of re-executing parsing/validating code.  Memoizing is done of
+           both valid results and parsing exceptions.
+           
+           Parameters:
+            - cache_size_limit - (default=C{128}) - if an integer value is provided
+              will limit the size of the packrat cache; if None is passed, then
+              the cache size will be unbounded; if 0 is passed, the cache will
+              be effectively disabled.
+            
+           This speedup may break existing programs that use parse actions that
+           have side-effects.  For this reason, packrat parsing is disabled when
+           you first import pyparsing.  To activate the packrat feature, your
+           program must call the class method C{ParserElement.enablePackrat()}.  If
+           your program uses C{psyco} to "compile as you go", you must call
+           C{enablePackrat} before calling C{psyco.full()}.  If you do not do this,
+           Python will crash.  For best results, call C{enablePackrat()} immediately
+           after importing pyparsing.
+           
+           Example::
+               import pyparsing
+               pyparsing.ParserElement.enablePackrat()
+        """
+        if not ParserElement._packratEnabled:
+            ParserElement._packratEnabled = True
+            if cache_size_limit is None:
+                ParserElement.packrat_cache = ParserElement._UnboundedCache()
+            else:
+                ParserElement.packrat_cache = ParserElement._FifoCache(cache_size_limit)
+            ParserElement._parse = ParserElement._parseCache
+
+    def parseString( self, instring, parseAll=False ):
+        """
+        Execute the parse expression with the given string.
+        This is the main interface to the client code, once the complete
+        expression has been built.
+
+        If you want the grammar to require that the entire input string be
+        successfully parsed, then set C{parseAll} to True (equivalent to ending
+        the grammar with C{L{StringEnd()}}).
+
+        Note: C{parseString} implicitly calls C{expandtabs()} on the input string,
+        in order to report proper column numbers in parse actions.
+        If the input string contains tabs and
+        the grammar uses parse actions that use the C{loc} argument to index into the
+        string being parsed, you can ensure you have a consistent view of the input
+        string by:
+         - calling C{parseWithTabs} on your grammar before calling C{parseString}
+           (see L{I{parseWithTabs}})
+         - define your parse action using the full C{(s,loc,toks)} signature, and
+           reference the input string using the parse action's C{s} argument
+         - explictly expand the tabs in your input string before calling
+           C{parseString}
+        
+        Example::
+            Word('a').parseString('aaaaabaaa')  # -> ['aaaaa']
+            Word('a').parseString('aaaaabaaa', parseAll=True)  # -> Exception: Expected end of text
+        """
+        ParserElement.resetCache()
+        if not self.streamlined:
+            self.streamline()
+            #~ self.saveAsList = True
+        for e in self.ignoreExprs:
+            e.streamline()
+        if not self.keepTabs:
+            instring = instring.expandtabs()
+        try:
+            loc, tokens = self._parse( instring, 0 )
+            if parseAll:
+                loc = self.preParse( instring, loc )
+                se = Empty() + StringEnd()
+                se._parse( instring, loc )
+        except ParseBaseException as exc:
+            if ParserElement.verbose_stacktrace:
+                raise
+            else:
+                # catch and re-raise exception from here, clears out pyparsing internal stack trace
+                raise exc
+        else:
+            return tokens
+
+    def scanString( self, instring, maxMatches=_MAX_INT, overlap=False ):
+        """
+        Scan the input string for expression matches.  Each match will return the
+        matching tokens, start location, and end location.  May be called with optional
+        C{maxMatches} argument, to clip scanning after 'n' matches are found.  If
+        C{overlap} is specified, then overlapping matches will be reported.
+
+        Note that the start and end locations are reported relative to the string
+        being parsed.  See L{I{parseString}} for more information on parsing
+        strings with embedded tabs.
+
+        Example::
+            source = "sldjf123lsdjjkf345sldkjf879lkjsfd987"
+            print(source)
+            for tokens,start,end in Word(alphas).scanString(source):
+                print(' '*start + '^'*(end-start))
+                print(' '*start + tokens[0])
+        
+        prints::
+        
+            sldjf123lsdjjkf345sldkjf879lkjsfd987
+            ^^^^^
+            sldjf
+                    ^^^^^^^
+                    lsdjjkf
+                              ^^^^^^
+                              sldkjf
+                                       ^^^^^^
+                                       lkjsfd
+        """
+        if not self.streamlined:
+            self.streamline()
+        for e in self.ignoreExprs:
+            e.streamline()
+
+        if not self.keepTabs:
+            instring = _ustr(instring).expandtabs()
+        instrlen = len(instring)
+        loc = 0
+        preparseFn = self.preParse
+        parseFn = self._parse
+        ParserElement.resetCache()
+        matches = 0
+        try:
+            while loc <= instrlen and matches < maxMatches:
+                try:
+                    preloc = preparseFn( instring, loc )
+                    nextLoc,tokens = parseFn( instring, preloc, callPreParse=False )
+                except ParseException:
+                    loc = preloc+1
+                else:
+                    if nextLoc > loc:
+                        matches += 1
+                        yield tokens, preloc, nextLoc
+                        if overlap:
+                            nextloc = preparseFn( instring, loc )
+                            if nextloc > loc:
+                                loc = nextLoc
+                            else:
+                                loc += 1
+                        else:
+                            loc = nextLoc
+                    else:
+                        loc = preloc+1
+        except ParseBaseException as exc:
+            if ParserElement.verbose_stacktrace:
+                raise
+            else:
+                # catch and re-raise exception from here, clears out pyparsing internal stack trace
+                raise exc
+
+    def transformString( self, instring ):
+        """
+        Extension to C{L{scanString}}, to modify matching text with modified tokens that may
+        be returned from a parse action.  To use C{transformString}, define a grammar and
+        attach a parse action to it that modifies the returned token list.
+        Invoking C{transformString()} on a target string will then scan for matches,
+        and replace the matched text patterns according to the logic in the parse
+        action.  C{transformString()} returns the resulting transformed string.
+        
+        Example::
+            wd = Word(alphas)
+            wd.setParseAction(lambda toks: toks[0].title())
+            
+            print(wd.transformString("now is the winter of our discontent made glorious summer by this sun of york."))
+        Prints::
+            Now Is The Winter Of Our Discontent Made Glorious Summer By This Sun Of York.
+        """
+        out = []
+        lastE = 0
+        # force preservation of s, to minimize unwanted transformation of string, and to
+        # keep string locs straight between transformString and scanString
+        self.keepTabs = True
+        try:
+            for t,s,e in self.scanString( instring ):
+                out.append( instring[lastE:s] )
+                if t:
+                    if isinstance(t,ParseResults):
+                        out += t.asList()
+                    elif isinstance(t,list):
+                        out += t
+                    else:
+                        out.append(t)
+                lastE = e
+            out.append(instring[lastE:])
+            out = [o for o in out if o]
+            return "".join(map(_ustr,_flatten(out)))
+        except ParseBaseException as exc:
+            if ParserElement.verbose_stacktrace:
+                raise
+            else:
+                # catch and re-raise exception from here, clears out pyparsing internal stack trace
+                raise exc
+
+    def searchString( self, instring, maxMatches=_MAX_INT ):
+        """
+        Another extension to C{L{scanString}}, simplifying the access to the tokens found
+        to match the given parse expression.  May be called with optional
+        C{maxMatches} argument, to clip searching after 'n' matches are found.
+        
+        Example::
+            # a capitalized word starts with an uppercase letter, followed by zero or more lowercase letters
+            cap_word = Word(alphas.upper(), alphas.lower())
+            
+            print(cap_word.searchString("More than Iron, more than Lead, more than Gold I need Electricity"))
+
+            # the sum() builtin can be used to merge results into a single ParseResults object
+            print(sum(cap_word.searchString("More than Iron, more than Lead, more than Gold I need Electricity")))
+        prints::
+            [['More'], ['Iron'], ['Lead'], ['Gold'], ['I'], ['Electricity']]
+            ['More', 'Iron', 'Lead', 'Gold', 'I', 'Electricity']
+        """
+        try:
+            return ParseResults([ t for t,s,e in self.scanString( instring, maxMatches ) ])
+        except ParseBaseException as exc:
+            if ParserElement.verbose_stacktrace:
+                raise
+            else:
+                # catch and re-raise exception from here, clears out pyparsing internal stack trace
+                raise exc
+
+    def split(self, instring, maxsplit=_MAX_INT, includeSeparators=False):
+        """
+        Generator method to split a string using the given expression as a separator.
+        May be called with optional C{maxsplit} argument, to limit the number of splits;
+        and the optional C{includeSeparators} argument (default=C{False}), if the separating
+        matching text should be included in the split results.
+        
+        Example::        
+            punc = oneOf(list(".,;:/-!?"))
+            print(list(punc.split("This, this?, this sentence, is badly punctuated!")))
+        prints::
+            ['This', ' this', '', ' this sentence', ' is badly punctuated', '']
+        """
+        splits = 0
+        last = 0
+        for t,s,e in self.scanString(instring, maxMatches=maxsplit):
+            yield instring[last:s]
+            if includeSeparators:
+                yield t[0]
+            last = e
+        yield instring[last:]
+
+    def __add__(self, other ):
+        """
+        Implementation of + operator - returns C{L{And}}. Adding strings to a ParserElement
+        converts them to L{Literal}s by default.
+        
+        Example::
+            greet = Word(alphas) + "," + Word(alphas) + "!"
+            hello = "Hello, World!"
+            print (hello, "->", greet.parseString(hello))
+        Prints::
+            Hello, World! -> ['Hello', ',', 'World', '!']
+        """
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return And( [ self, other ] )
+
+    def __radd__(self, other ):
+        """
+        Implementation of + operator when left operand is not a C{L{ParserElement}}
+        """
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return other + self
+
+    def __sub__(self, other):
+        """
+        Implementation of - operator, returns C{L{And}} with error stop
+        """
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return self + And._ErrorStop() + other
+
+    def __rsub__(self, other ):
+        """
+        Implementation of - operator when left operand is not a C{L{ParserElement}}
+        """
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return other - self
+
+    def __mul__(self,other):
+        """
+        Implementation of * operator, allows use of C{expr * 3} in place of
+        C{expr + expr + expr}.  Expressions may also me multiplied by a 2-integer
+        tuple, similar to C{{min,max}} multipliers in regular expressions.  Tuples
+        may also include C{None} as in:
+         - C{expr*(n,None)} or C{expr*(n,)} is equivalent
+              to C{expr*n + L{ZeroOrMore}(expr)}
+              (read as "at least n instances of C{expr}")
+         - C{expr*(None,n)} is equivalent to C{expr*(0,n)}
+              (read as "0 to n instances of C{expr}")
+         - C{expr*(None,None)} is equivalent to C{L{ZeroOrMore}(expr)}
+         - C{expr*(1,None)} is equivalent to C{L{OneOrMore}(expr)}
+
+        Note that C{expr*(None,n)} does not raise an exception if
+        more than n exprs exist in the input stream; that is,
+        C{expr*(None,n)} does not enforce a maximum number of expr
+        occurrences.  If this behavior is desired, then write
+        C{expr*(None,n) + ~expr}
+        """
+        if isinstance(other,int):
+            minElements, optElements = other,0
+        elif isinstance(other,tuple):
+            other = (other + (None, None))[:2]
+            if other[0] is None:
+                other = (0, other[1])
+            if isinstance(other[0],int) and other[1] is None:
+                if other[0] == 0:
+                    return ZeroOrMore(self)
+                if other[0] == 1:
+                    return OneOrMore(self)
+                else:
+                    return self*other[0] + ZeroOrMore(self)
+            elif isinstance(other[0],int) and isinstance(other[1],int):
+                minElements, optElements = other
+                optElements -= minElements
+            else:
+                raise TypeError("cannot multiply 'ParserElement' and ('%s','%s') objects", type(other[0]),type(other[1]))
+        else:
+            raise TypeError("cannot multiply 'ParserElement' and '%s' objects", type(other))
+
+        if minElements < 0:
+            raise ValueError("cannot multiply ParserElement by negative value")
+        if optElements < 0:
+            raise ValueError("second tuple value must be greater or equal to first tuple value")
+        if minElements == optElements == 0:
+            raise ValueError("cannot multiply ParserElement by 0 or (0,0)")
+
+        if (optElements):
+            def makeOptionalList(n):
+                if n>1:
+                    return Optional(self + makeOptionalList(n-1))
+                else:
+                    return Optional(self)
+            if minElements:
+                if minElements == 1:
+                    ret = self + makeOptionalList(optElements)
+                else:
+                    ret = And([self]*minElements) + makeOptionalList(optElements)
+            else:
+                ret = makeOptionalList(optElements)
+        else:
+            if minElements == 1:
+                ret = self
+            else:
+                ret = And([self]*minElements)
+        return ret
+
+    def __rmul__(self, other):
+        return self.__mul__(other)
+
+    def __or__(self, other ):
+        """
+        Implementation of | operator - returns C{L{MatchFirst}}
+        """
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return MatchFirst( [ self, other ] )
+
+    def __ror__(self, other ):
+        """
+        Implementation of | operator when left operand is not a C{L{ParserElement}}
+        """
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return other | self
+
+    def __xor__(self, other ):
+        """
+        Implementation of ^ operator - returns C{L{Or}}
+        """
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return Or( [ self, other ] )
+
+    def __rxor__(self, other ):
+        """
+        Implementation of ^ operator when left operand is not a C{L{ParserElement}}
+        """
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return other ^ self
+
+    def __and__(self, other ):
+        """
+        Implementation of & operator - returns C{L{Each}}
+        """
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return Each( [ self, other ] )
+
+    def __rand__(self, other ):
+        """
+        Implementation of & operator when left operand is not a C{L{ParserElement}}
+        """
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return other & self
+
+    def __invert__( self ):
+        """
+        Implementation of ~ operator - returns C{L{NotAny}}
+        """
+        return NotAny( self )
+
+    def __call__(self, name=None):
+        """
+        Shortcut for C{L{setResultsName}}, with C{listAllMatches=False}.
+        
+        If C{name} is given with a trailing C{'*'} character, then C{listAllMatches} will be
+        passed as C{True}.
+           
+        If C{name} is omitted, same as calling C{L{copy}}.
+
+        Example::
+            # these are equivalent
+            userdata = Word(alphas).setResultsName("name") + Word(nums+"-").setResultsName("socsecno")
+            userdata = Word(alphas)("name") + Word(nums+"-")("socsecno")             
+        """
+        if name is not None:
+            return self.setResultsName(name)
+        else:
+            return self.copy()
+
+    def suppress( self ):
+        """
+        Suppresses the output of this C{ParserElement}; useful to keep punctuation from
+        cluttering up returned output.
+        """
+        return Suppress( self )
+
+    def leaveWhitespace( self ):
+        """
+        Disables the skipping of whitespace before matching the characters in the
+        C{ParserElement}'s defined pattern.  This is normally only used internally by
+        the pyparsing module, but may be needed in some whitespace-sensitive grammars.
+        """
+        self.skipWhitespace = False
+        return self
+
+    def setWhitespaceChars( self, chars ):
+        """
+        Overrides the default whitespace chars
+        """
+        self.skipWhitespace = True
+        self.whiteChars = chars
+        self.copyDefaultWhiteChars = False
+        return self
+
+    def parseWithTabs( self ):
+        """
+        Overrides default behavior to expand C{}s to spaces before parsing the input string.
+        Must be called before C{parseString} when the input grammar contains elements that
+        match C{} characters.
+        """
+        self.keepTabs = True
+        return self
+
+    def ignore( self, other ):
+        """
+        Define expression to be ignored (e.g., comments) while doing pattern
+        matching; may be called repeatedly, to define multiple comment or other
+        ignorable patterns.
+        
+        Example::
+            patt = OneOrMore(Word(alphas))
+            patt.parseString('ablaj /* comment */ lskjd') # -> ['ablaj']
+            
+            patt.ignore(cStyleComment)
+            patt.parseString('ablaj /* comment */ lskjd') # -> ['ablaj', 'lskjd']
+        """
+        if isinstance(other, basestring):
+            other = Suppress(other)
+
+        if isinstance( other, Suppress ):
+            if other not in self.ignoreExprs:
+                self.ignoreExprs.append(other)
+        else:
+            self.ignoreExprs.append( Suppress( other.copy() ) )
+        return self
+
+    def setDebugActions( self, startAction, successAction, exceptionAction ):
+        """
+        Enable display of debugging messages while doing pattern matching.
+        """
+        self.debugActions = (startAction or _defaultStartDebugAction,
+                             successAction or _defaultSuccessDebugAction,
+                             exceptionAction or _defaultExceptionDebugAction)
+        self.debug = True
+        return self
+
+    def setDebug( self, flag=True ):
+        """
+        Enable display of debugging messages while doing pattern matching.
+        Set C{flag} to True to enable, False to disable.
+
+        Example::
+            wd = Word(alphas).setName("alphaword")
+            integer = Word(nums).setName("numword")
+            term = wd | integer
+            
+            # turn on debugging for wd
+            wd.setDebug()
+
+            OneOrMore(term).parseString("abc 123 xyz 890")
+        
+        prints::
+            Match alphaword at loc 0(1,1)
+            Matched alphaword -> ['abc']
+            Match alphaword at loc 3(1,4)
+            Exception raised:Expected alphaword (at char 4), (line:1, col:5)
+            Match alphaword at loc 7(1,8)
+            Matched alphaword -> ['xyz']
+            Match alphaword at loc 11(1,12)
+            Exception raised:Expected alphaword (at char 12), (line:1, col:13)
+            Match alphaword at loc 15(1,16)
+            Exception raised:Expected alphaword (at char 15), (line:1, col:16)
+
+        The output shown is that produced by the default debug actions - custom debug actions can be
+        specified using L{setDebugActions}. Prior to attempting
+        to match the C{wd} expression, the debugging message C{"Match  at loc (,)"}
+        is shown. Then if the parse succeeds, a C{"Matched"} message is shown, or an C{"Exception raised"}
+        message is shown. Also note the use of L{setName} to assign a human-readable name to the expression,
+        which makes debugging and exception messages easier to understand - for instance, the default
+        name created for the C{Word} expression without calling C{setName} is C{"W:(ABCD...)"}.
+        """
+        if flag:
+            self.setDebugActions( _defaultStartDebugAction, _defaultSuccessDebugAction, _defaultExceptionDebugAction )
+        else:
+            self.debug = False
+        return self
+
+    def __str__( self ):
+        return self.name
+
+    def __repr__( self ):
+        return _ustr(self)
+
+    def streamline( self ):
+        self.streamlined = True
+        self.strRepr = None
+        return self
+
+    def checkRecursion( self, parseElementList ):
+        pass
+
+    def validate( self, validateTrace=[] ):
+        """
+        Check defined expressions for valid structure, check for infinite recursive definitions.
+        """
+        self.checkRecursion( [] )
+
+    def parseFile( self, file_or_filename, parseAll=False ):
+        """
+        Execute the parse expression on the given file or filename.
+        If a filename is specified (instead of a file object),
+        the entire file is opened, read, and closed before parsing.
+        """
+        try:
+            file_contents = file_or_filename.read()
+        except AttributeError:
+            with open(file_or_filename, "r") as f:
+                file_contents = f.read()
+        try:
+            return self.parseString(file_contents, parseAll)
+        except ParseBaseException as exc:
+            if ParserElement.verbose_stacktrace:
+                raise
+            else:
+                # catch and re-raise exception from here, clears out pyparsing internal stack trace
+                raise exc
+
+    def __eq__(self,other):
+        if isinstance(other, ParserElement):
+            return self is other or vars(self) == vars(other)
+        elif isinstance(other, basestring):
+            return self.matches(other)
+        else:
+            return super(ParserElement,self)==other
+
+    def __ne__(self,other):
+        return not (self == other)
+
+    def __hash__(self):
+        return hash(id(self))
+
+    def __req__(self,other):
+        return self == other
+
+    def __rne__(self,other):
+        return not (self == other)
+
+    def matches(self, testString, parseAll=True):
+        """
+        Method for quick testing of a parser against a test string. Good for simple 
+        inline microtests of sub expressions while building up larger parser.
+           
+        Parameters:
+         - testString - to test against this expression for a match
+         - parseAll - (default=C{True}) - flag to pass to C{L{parseString}} when running tests
+            
+        Example::
+            expr = Word(nums)
+            assert expr.matches("100")
+        """
+        try:
+            self.parseString(_ustr(testString), parseAll=parseAll)
+            return True
+        except ParseBaseException:
+            return False
+                
+    def runTests(self, tests, parseAll=True, comment='#', fullDump=True, printResults=True, failureTests=False):
+        """
+        Execute the parse expression on a series of test strings, showing each
+        test, the parsed results or where the parse failed. Quick and easy way to
+        run a parse expression against a list of sample strings.
+           
+        Parameters:
+         - tests - a list of separate test strings, or a multiline string of test strings
+         - parseAll - (default=C{True}) - flag to pass to C{L{parseString}} when running tests           
+         - comment - (default=C{'#'}) - expression for indicating embedded comments in the test 
+              string; pass None to disable comment filtering
+         - fullDump - (default=C{True}) - dump results as list followed by results names in nested outline;
+              if False, only dump nested list
+         - printResults - (default=C{True}) prints test output to stdout
+         - failureTests - (default=C{False}) indicates if these tests are expected to fail parsing
+
+        Returns: a (success, results) tuple, where success indicates that all tests succeeded
+        (or failed if C{failureTests} is True), and the results contain a list of lines of each 
+        test's output
+        
+        Example::
+            number_expr = pyparsing_common.number.copy()
+
+            result = number_expr.runTests('''
+                # unsigned integer
+                100
+                # negative integer
+                -100
+                # float with scientific notation
+                6.02e23
+                # integer with scientific notation
+                1e-12
+                ''')
+            print("Success" if result[0] else "Failed!")
+
+            result = number_expr.runTests('''
+                # stray character
+                100Z
+                # missing leading digit before '.'
+                -.100
+                # too many '.'
+                3.14.159
+                ''', failureTests=True)
+            print("Success" if result[0] else "Failed!")
+        prints::
+            # unsigned integer
+            100
+            [100]
+
+            # negative integer
+            -100
+            [-100]
+
+            # float with scientific notation
+            6.02e23
+            [6.02e+23]
+
+            # integer with scientific notation
+            1e-12
+            [1e-12]
+
+            Success
+            
+            # stray character
+            100Z
+               ^
+            FAIL: Expected end of text (at char 3), (line:1, col:4)
+
+            # missing leading digit before '.'
+            -.100
+            ^
+            FAIL: Expected {real number with scientific notation | real number | signed integer} (at char 0), (line:1, col:1)
+
+            # too many '.'
+            3.14.159
+                ^
+            FAIL: Expected end of text (at char 4), (line:1, col:5)
+
+            Success
+
+        Each test string must be on a single line. If you want to test a string that spans multiple
+        lines, create a test like this::
+
+            expr.runTest(r"this is a test\\n of strings that spans \\n 3 lines")
+        
+        (Note that this is a raw string literal, you must include the leading 'r'.)
+        """
+        if isinstance(tests, basestring):
+            tests = list(map(str.strip, tests.rstrip().splitlines()))
+        if isinstance(comment, basestring):
+            comment = Literal(comment)
+        allResults = []
+        comments = []
+        success = True
+        for t in tests:
+            if comment is not None and comment.matches(t, False) or comments and not t:
+                comments.append(t)
+                continue
+            if not t:
+                continue
+            out = ['\n'.join(comments), t]
+            comments = []
+            try:
+                t = t.replace(r'\n','\n')
+                result = self.parseString(t, parseAll=parseAll)
+                out.append(result.dump(full=fullDump))
+                success = success and not failureTests
+            except ParseBaseException as pe:
+                fatal = "(FATAL)" if isinstance(pe, ParseFatalException) else ""
+                if '\n' in t:
+                    out.append(line(pe.loc, t))
+                    out.append(' '*(col(pe.loc,t)-1) + '^' + fatal)
+                else:
+                    out.append(' '*pe.loc + '^' + fatal)
+                out.append("FAIL: " + str(pe))
+                success = success and failureTests
+                result = pe
+            except Exception as exc:
+                out.append("FAIL-EXCEPTION: " + str(exc))
+                success = success and failureTests
+                result = exc
+
+            if printResults:
+                if fullDump:
+                    out.append('')
+                print('\n'.join(out))
+
+            allResults.append((t, result))
+        
+        return success, allResults
+
+        
+class Token(ParserElement):
+    """
+    Abstract C{ParserElement} subclass, for defining atomic matching patterns.
+    """
+    def __init__( self ):
+        super(Token,self).__init__( savelist=False )
+
+
+class Empty(Token):
+    """
+    An empty token, will always match.
+    """
+    def __init__( self ):
+        super(Empty,self).__init__()
+        self.name = "Empty"
+        self.mayReturnEmpty = True
+        self.mayIndexError = False
+
+
+class NoMatch(Token):
+    """
+    A token that will never match.
+    """
+    def __init__( self ):
+        super(NoMatch,self).__init__()
+        self.name = "NoMatch"
+        self.mayReturnEmpty = True
+        self.mayIndexError = False
+        self.errmsg = "Unmatchable token"
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        raise ParseException(instring, loc, self.errmsg, self)
+
+
+class Literal(Token):
+    """
+    Token to exactly match a specified string.
+    
+    Example::
+        Literal('blah').parseString('blah')  # -> ['blah']
+        Literal('blah').parseString('blahfooblah')  # -> ['blah']
+        Literal('blah').parseString('bla')  # -> Exception: Expected "blah"
+    
+    For case-insensitive matching, use L{CaselessLiteral}.
+    
+    For keyword matching (force word break before and after the matched string),
+    use L{Keyword} or L{CaselessKeyword}.
+    """
+    def __init__( self, matchString ):
+        super(Literal,self).__init__()
+        self.match = matchString
+        self.matchLen = len(matchString)
+        try:
+            self.firstMatchChar = matchString[0]
+        except IndexError:
+            warnings.warn("null string passed to Literal; use Empty() instead",
+                            SyntaxWarning, stacklevel=2)
+            self.__class__ = Empty
+        self.name = '"%s"' % _ustr(self.match)
+        self.errmsg = "Expected " + self.name
+        self.mayReturnEmpty = False
+        self.mayIndexError = False
+
+    # Performance tuning: this routine gets called a *lot*
+    # if this is a single character match string  and the first character matches,
+    # short-circuit as quickly as possible, and avoid calling startswith
+    #~ @profile
+    def parseImpl( self, instring, loc, doActions=True ):
+        if (instring[loc] == self.firstMatchChar and
+            (self.matchLen==1 or instring.startswith(self.match,loc)) ):
+            return loc+self.matchLen, self.match
+        raise ParseException(instring, loc, self.errmsg, self)
+_L = Literal
+ParserElement._literalStringClass = Literal
+
+class Keyword(Token):
+    """
+    Token to exactly match a specified string as a keyword, that is, it must be
+    immediately followed by a non-keyword character.  Compare with C{L{Literal}}:
+     - C{Literal("if")} will match the leading C{'if'} in C{'ifAndOnlyIf'}.
+     - C{Keyword("if")} will not; it will only match the leading C{'if'} in C{'if x=1'}, or C{'if(y==2)'}
+    Accepts two optional constructor arguments in addition to the keyword string:
+     - C{identChars} is a string of characters that would be valid identifier characters,
+          defaulting to all alphanumerics + "_" and "$"
+     - C{caseless} allows case-insensitive matching, default is C{False}.
+       
+    Example::
+        Keyword("start").parseString("start")  # -> ['start']
+        Keyword("start").parseString("starting")  # -> Exception
+
+    For case-insensitive matching, use L{CaselessKeyword}.
+    """
+    DEFAULT_KEYWORD_CHARS = alphanums+"_$"
+
+    def __init__( self, matchString, identChars=None, caseless=False ):
+        super(Keyword,self).__init__()
+        if identChars is None:
+            identChars = Keyword.DEFAULT_KEYWORD_CHARS
+        self.match = matchString
+        self.matchLen = len(matchString)
+        try:
+            self.firstMatchChar = matchString[0]
+        except IndexError:
+            warnings.warn("null string passed to Keyword; use Empty() instead",
+                            SyntaxWarning, stacklevel=2)
+        self.name = '"%s"' % self.match
+        self.errmsg = "Expected " + self.name
+        self.mayReturnEmpty = False
+        self.mayIndexError = False
+        self.caseless = caseless
+        if caseless:
+            self.caselessmatch = matchString.upper()
+            identChars = identChars.upper()
+        self.identChars = set(identChars)
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if self.caseless:
+            if ( (instring[ loc:loc+self.matchLen ].upper() == self.caselessmatch) and
+                 (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen].upper() not in self.identChars) and
+                 (loc == 0 or instring[loc-1].upper() not in self.identChars) ):
+                return loc+self.matchLen, self.match
+        else:
+            if (instring[loc] == self.firstMatchChar and
+                (self.matchLen==1 or instring.startswith(self.match,loc)) and
+                (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen] not in self.identChars) and
+                (loc == 0 or instring[loc-1] not in self.identChars) ):
+                return loc+self.matchLen, self.match
+        raise ParseException(instring, loc, self.errmsg, self)
+
+    def copy(self):
+        c = super(Keyword,self).copy()
+        c.identChars = Keyword.DEFAULT_KEYWORD_CHARS
+        return c
+
+    @staticmethod
+    def setDefaultKeywordChars( chars ):
+        """Overrides the default Keyword chars
+        """
+        Keyword.DEFAULT_KEYWORD_CHARS = chars
+
+class CaselessLiteral(Literal):
+    """
+    Token to match a specified string, ignoring case of letters.
+    Note: the matched results will always be in the case of the given
+    match string, NOT the case of the input text.
+
+    Example::
+        OneOrMore(CaselessLiteral("CMD")).parseString("cmd CMD Cmd10") # -> ['CMD', 'CMD', 'CMD']
+        
+    (Contrast with example for L{CaselessKeyword}.)
+    """
+    def __init__( self, matchString ):
+        super(CaselessLiteral,self).__init__( matchString.upper() )
+        # Preserve the defining literal.
+        self.returnString = matchString
+        self.name = "'%s'" % self.returnString
+        self.errmsg = "Expected " + self.name
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if instring[ loc:loc+self.matchLen ].upper() == self.match:
+            return loc+self.matchLen, self.returnString
+        raise ParseException(instring, loc, self.errmsg, self)
+
+class CaselessKeyword(Keyword):
+    """
+    Caseless version of L{Keyword}.
+
+    Example::
+        OneOrMore(CaselessKeyword("CMD")).parseString("cmd CMD Cmd10") # -> ['CMD', 'CMD']
+        
+    (Contrast with example for L{CaselessLiteral}.)
+    """
+    def __init__( self, matchString, identChars=None ):
+        super(CaselessKeyword,self).__init__( matchString, identChars, caseless=True )
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if ( (instring[ loc:loc+self.matchLen ].upper() == self.caselessmatch) and
+             (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen].upper() not in self.identChars) ):
+            return loc+self.matchLen, self.match
+        raise ParseException(instring, loc, self.errmsg, self)
+
+class CloseMatch(Token):
+    """
+    A variation on L{Literal} which matches "close" matches, that is, 
+    strings with at most 'n' mismatching characters. C{CloseMatch} takes parameters:
+     - C{match_string} - string to be matched
+     - C{maxMismatches} - (C{default=1}) maximum number of mismatches allowed to count as a match
+    
+    The results from a successful parse will contain the matched text from the input string and the following named results:
+     - C{mismatches} - a list of the positions within the match_string where mismatches were found
+     - C{original} - the original match_string used to compare against the input string
+    
+    If C{mismatches} is an empty list, then the match was an exact match.
+    
+    Example::
+        patt = CloseMatch("ATCATCGAATGGA")
+        patt.parseString("ATCATCGAAXGGA") # -> (['ATCATCGAAXGGA'], {'mismatches': [[9]], 'original': ['ATCATCGAATGGA']})
+        patt.parseString("ATCAXCGAAXGGA") # -> Exception: Expected 'ATCATCGAATGGA' (with up to 1 mismatches) (at char 0), (line:1, col:1)
+
+        # exact match
+        patt.parseString("ATCATCGAATGGA") # -> (['ATCATCGAATGGA'], {'mismatches': [[]], 'original': ['ATCATCGAATGGA']})
+
+        # close match allowing up to 2 mismatches
+        patt = CloseMatch("ATCATCGAATGGA", maxMismatches=2)
+        patt.parseString("ATCAXCGAAXGGA") # -> (['ATCAXCGAAXGGA'], {'mismatches': [[4, 9]], 'original': ['ATCATCGAATGGA']})
+    """
+    def __init__(self, match_string, maxMismatches=1):
+        super(CloseMatch,self).__init__()
+        self.name = match_string
+        self.match_string = match_string
+        self.maxMismatches = maxMismatches
+        self.errmsg = "Expected %r (with up to %d mismatches)" % (self.match_string, self.maxMismatches)
+        self.mayIndexError = False
+        self.mayReturnEmpty = False
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        start = loc
+        instrlen = len(instring)
+        maxloc = start + len(self.match_string)
+
+        if maxloc <= instrlen:
+            match_string = self.match_string
+            match_stringloc = 0
+            mismatches = []
+            maxMismatches = self.maxMismatches
+
+            for match_stringloc,s_m in enumerate(zip(instring[loc:maxloc], self.match_string)):
+                src,mat = s_m
+                if src != mat:
+                    mismatches.append(match_stringloc)
+                    if len(mismatches) > maxMismatches:
+                        break
+            else:
+                loc = match_stringloc + 1
+                results = ParseResults([instring[start:loc]])
+                results['original'] = self.match_string
+                results['mismatches'] = mismatches
+                return loc, results
+
+        raise ParseException(instring, loc, self.errmsg, self)
+
+
+class Word(Token):
+    """
+    Token for matching words composed of allowed character sets.
+    Defined with string containing all allowed initial characters,
+    an optional string containing allowed body characters (if omitted,
+    defaults to the initial character set), and an optional minimum,
+    maximum, and/or exact length.  The default value for C{min} is 1 (a
+    minimum value < 1 is not valid); the default values for C{max} and C{exact}
+    are 0, meaning no maximum or exact length restriction. An optional
+    C{excludeChars} parameter can list characters that might be found in 
+    the input C{bodyChars} string; useful to define a word of all printables
+    except for one or two characters, for instance.
+    
+    L{srange} is useful for defining custom character set strings for defining 
+    C{Word} expressions, using range notation from regular expression character sets.
+    
+    A common mistake is to use C{Word} to match a specific literal string, as in 
+    C{Word("Address")}. Remember that C{Word} uses the string argument to define
+    I{sets} of matchable characters. This expression would match "Add", "AAA",
+    "dAred", or any other word made up of the characters 'A', 'd', 'r', 'e', and 's'.
+    To match an exact literal string, use L{Literal} or L{Keyword}.
+
+    pyparsing includes helper strings for building Words:
+     - L{alphas}
+     - L{nums}
+     - L{alphanums}
+     - L{hexnums}
+     - L{alphas8bit} (alphabetic characters in ASCII range 128-255 - accented, tilded, umlauted, etc.)
+     - L{punc8bit} (non-alphabetic characters in ASCII range 128-255 - currency, symbols, superscripts, diacriticals, etc.)
+     - L{printables} (any non-whitespace character)
+
+    Example::
+        # a word composed of digits
+        integer = Word(nums) # equivalent to Word("0123456789") or Word(srange("0-9"))
+        
+        # a word with a leading capital, and zero or more lowercase
+        capital_word = Word(alphas.upper(), alphas.lower())
+
+        # hostnames are alphanumeric, with leading alpha, and '-'
+        hostname = Word(alphas, alphanums+'-')
+        
+        # roman numeral (not a strict parser, accepts invalid mix of characters)
+        roman = Word("IVXLCDM")
+        
+        # any string of non-whitespace characters, except for ','
+        csv_value = Word(printables, excludeChars=",")
+    """
+    def __init__( self, initChars, bodyChars=None, min=1, max=0, exact=0, asKeyword=False, excludeChars=None ):
+        super(Word,self).__init__()
+        if excludeChars:
+            initChars = ''.join(c for c in initChars if c not in excludeChars)
+            if bodyChars:
+                bodyChars = ''.join(c for c in bodyChars if c not in excludeChars)
+        self.initCharsOrig = initChars
+        self.initChars = set(initChars)
+        if bodyChars :
+            self.bodyCharsOrig = bodyChars
+            self.bodyChars = set(bodyChars)
+        else:
+            self.bodyCharsOrig = initChars
+            self.bodyChars = set(initChars)
+
+        self.maxSpecified = max > 0
+
+        if min < 1:
+            raise ValueError("cannot specify a minimum length < 1; use Optional(Word()) if zero-length word is permitted")
+
+        self.minLen = min
+
+        if max > 0:
+            self.maxLen = max
+        else:
+            self.maxLen = _MAX_INT
+
+        if exact > 0:
+            self.maxLen = exact
+            self.minLen = exact
+
+        self.name = _ustr(self)
+        self.errmsg = "Expected " + self.name
+        self.mayIndexError = False
+        self.asKeyword = asKeyword
+
+        if ' ' not in self.initCharsOrig+self.bodyCharsOrig and (min==1 and max==0 and exact==0):
+            if self.bodyCharsOrig == self.initCharsOrig:
+                self.reString = "[%s]+" % _escapeRegexRangeChars(self.initCharsOrig)
+            elif len(self.initCharsOrig) == 1:
+                self.reString = "%s[%s]*" % \
+                                      (re.escape(self.initCharsOrig),
+                                      _escapeRegexRangeChars(self.bodyCharsOrig),)
+            else:
+                self.reString = "[%s][%s]*" % \
+                                      (_escapeRegexRangeChars(self.initCharsOrig),
+                                      _escapeRegexRangeChars(self.bodyCharsOrig),)
+            if self.asKeyword:
+                self.reString = r"\b"+self.reString+r"\b"
+            try:
+                self.re = re.compile( self.reString )
+            except Exception:
+                self.re = None
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if self.re:
+            result = self.re.match(instring,loc)
+            if not result:
+                raise ParseException(instring, loc, self.errmsg, self)
+
+            loc = result.end()
+            return loc, result.group()
+
+        if not(instring[ loc ] in self.initChars):
+            raise ParseException(instring, loc, self.errmsg, self)
+
+        start = loc
+        loc += 1
+        instrlen = len(instring)
+        bodychars = self.bodyChars
+        maxloc = start + self.maxLen
+        maxloc = min( maxloc, instrlen )
+        while loc < maxloc and instring[loc] in bodychars:
+            loc += 1
+
+        throwException = False
+        if loc - start < self.minLen:
+            throwException = True
+        if self.maxSpecified and loc < instrlen and instring[loc] in bodychars:
+            throwException = True
+        if self.asKeyword:
+            if (start>0 and instring[start-1] in bodychars) or (loc4:
+                    return s[:4]+"..."
+                else:
+                    return s
+
+            if ( self.initCharsOrig != self.bodyCharsOrig ):
+                self.strRepr = "W:(%s,%s)" % ( charsAsStr(self.initCharsOrig), charsAsStr(self.bodyCharsOrig) )
+            else:
+                self.strRepr = "W:(%s)" % charsAsStr(self.initCharsOrig)
+
+        return self.strRepr
+
+
+class Regex(Token):
+    r"""
+    Token for matching strings that match a given regular expression.
+    Defined with string specifying the regular expression in a form recognized by the inbuilt Python re module.
+    If the given regex contains named groups (defined using C{(?P...)}), these will be preserved as 
+    named parse results.
+
+    Example::
+        realnum = Regex(r"[+-]?\d+\.\d*")
+        date = Regex(r'(?P\d{4})-(?P\d\d?)-(?P\d\d?)')
+        # ref: http://stackoverflow.com/questions/267399/how-do-you-match-only-valid-roman-numerals-with-a-regular-expression
+        roman = Regex(r"M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})")
+    """
+    compiledREtype = type(re.compile("[A-Z]"))
+    def __init__( self, pattern, flags=0):
+        """The parameters C{pattern} and C{flags} are passed to the C{re.compile()} function as-is. See the Python C{re} module for an explanation of the acceptable patterns and flags."""
+        super(Regex,self).__init__()
+
+        if isinstance(pattern, basestring):
+            if not pattern:
+                warnings.warn("null string passed to Regex; use Empty() instead",
+                        SyntaxWarning, stacklevel=2)
+
+            self.pattern = pattern
+            self.flags = flags
+
+            try:
+                self.re = re.compile(self.pattern, self.flags)
+                self.reString = self.pattern
+            except sre_constants.error:
+                warnings.warn("invalid pattern (%s) passed to Regex" % pattern,
+                    SyntaxWarning, stacklevel=2)
+                raise
+
+        elif isinstance(pattern, Regex.compiledREtype):
+            self.re = pattern
+            self.pattern = \
+            self.reString = str(pattern)
+            self.flags = flags
+            
+        else:
+            raise ValueError("Regex may only be constructed with a string or a compiled RE object")
+
+        self.name = _ustr(self)
+        self.errmsg = "Expected " + self.name
+        self.mayIndexError = False
+        self.mayReturnEmpty = True
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        result = self.re.match(instring,loc)
+        if not result:
+            raise ParseException(instring, loc, self.errmsg, self)
+
+        loc = result.end()
+        d = result.groupdict()
+        ret = ParseResults(result.group())
+        if d:
+            for k in d:
+                ret[k] = d[k]
+        return loc,ret
+
+    def __str__( self ):
+        try:
+            return super(Regex,self).__str__()
+        except Exception:
+            pass
+
+        if self.strRepr is None:
+            self.strRepr = "Re:(%s)" % repr(self.pattern)
+
+        return self.strRepr
+
+
+class QuotedString(Token):
+    r"""
+    Token for matching strings that are delimited by quoting characters.
+    
+    Defined with the following parameters:
+        - quoteChar - string of one or more characters defining the quote delimiting string
+        - escChar - character to escape quotes, typically backslash (default=C{None})
+        - escQuote - special quote sequence to escape an embedded quote string (such as SQL's "" to escape an embedded ") (default=C{None})
+        - multiline - boolean indicating whether quotes can span multiple lines (default=C{False})
+        - unquoteResults - boolean indicating whether the matched text should be unquoted (default=C{True})
+        - endQuoteChar - string of one or more characters defining the end of the quote delimited string (default=C{None} => same as quoteChar)
+        - convertWhitespaceEscapes - convert escaped whitespace (C{'\t'}, C{'\n'}, etc.) to actual whitespace (default=C{True})
+
+    Example::
+        qs = QuotedString('"')
+        print(qs.searchString('lsjdf "This is the quote" sldjf'))
+        complex_qs = QuotedString('{{', endQuoteChar='}}')
+        print(complex_qs.searchString('lsjdf {{This is the "quote"}} sldjf'))
+        sql_qs = QuotedString('"', escQuote='""')
+        print(sql_qs.searchString('lsjdf "This is the quote with ""embedded"" quotes" sldjf'))
+    prints::
+        [['This is the quote']]
+        [['This is the "quote"']]
+        [['This is the quote with "embedded" quotes']]
+    """
+    def __init__( self, quoteChar, escChar=None, escQuote=None, multiline=False, unquoteResults=True, endQuoteChar=None, convertWhitespaceEscapes=True):
+        super(QuotedString,self).__init__()
+
+        # remove white space from quote chars - wont work anyway
+        quoteChar = quoteChar.strip()
+        if not quoteChar:
+            warnings.warn("quoteChar cannot be the empty string",SyntaxWarning,stacklevel=2)
+            raise SyntaxError()
+
+        if endQuoteChar is None:
+            endQuoteChar = quoteChar
+        else:
+            endQuoteChar = endQuoteChar.strip()
+            if not endQuoteChar:
+                warnings.warn("endQuoteChar cannot be the empty string",SyntaxWarning,stacklevel=2)
+                raise SyntaxError()
+
+        self.quoteChar = quoteChar
+        self.quoteCharLen = len(quoteChar)
+        self.firstQuoteChar = quoteChar[0]
+        self.endQuoteChar = endQuoteChar
+        self.endQuoteCharLen = len(endQuoteChar)
+        self.escChar = escChar
+        self.escQuote = escQuote
+        self.unquoteResults = unquoteResults
+        self.convertWhitespaceEscapes = convertWhitespaceEscapes
+
+        if multiline:
+            self.flags = re.MULTILINE | re.DOTALL
+            self.pattern = r'%s(?:[^%s%s]' % \
+                ( re.escape(self.quoteChar),
+                  _escapeRegexRangeChars(self.endQuoteChar[0]),
+                  (escChar is not None and _escapeRegexRangeChars(escChar) or '') )
+        else:
+            self.flags = 0
+            self.pattern = r'%s(?:[^%s\n\r%s]' % \
+                ( re.escape(self.quoteChar),
+                  _escapeRegexRangeChars(self.endQuoteChar[0]),
+                  (escChar is not None and _escapeRegexRangeChars(escChar) or '') )
+        if len(self.endQuoteChar) > 1:
+            self.pattern += (
+                '|(?:' + ')|(?:'.join("%s[^%s]" % (re.escape(self.endQuoteChar[:i]),
+                                               _escapeRegexRangeChars(self.endQuoteChar[i]))
+                                    for i in range(len(self.endQuoteChar)-1,0,-1)) + ')'
+                )
+        if escQuote:
+            self.pattern += (r'|(?:%s)' % re.escape(escQuote))
+        if escChar:
+            self.pattern += (r'|(?:%s.)' % re.escape(escChar))
+            self.escCharReplacePattern = re.escape(self.escChar)+"(.)"
+        self.pattern += (r')*%s' % re.escape(self.endQuoteChar))
+
+        try:
+            self.re = re.compile(self.pattern, self.flags)
+            self.reString = self.pattern
+        except sre_constants.error:
+            warnings.warn("invalid pattern (%s) passed to Regex" % self.pattern,
+                SyntaxWarning, stacklevel=2)
+            raise
+
+        self.name = _ustr(self)
+        self.errmsg = "Expected " + self.name
+        self.mayIndexError = False
+        self.mayReturnEmpty = True
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        result = instring[loc] == self.firstQuoteChar and self.re.match(instring,loc) or None
+        if not result:
+            raise ParseException(instring, loc, self.errmsg, self)
+
+        loc = result.end()
+        ret = result.group()
+
+        if self.unquoteResults:
+
+            # strip off quotes
+            ret = ret[self.quoteCharLen:-self.endQuoteCharLen]
+
+            if isinstance(ret,basestring):
+                # replace escaped whitespace
+                if '\\' in ret and self.convertWhitespaceEscapes:
+                    ws_map = {
+                        r'\t' : '\t',
+                        r'\n' : '\n',
+                        r'\f' : '\f',
+                        r'\r' : '\r',
+                    }
+                    for wslit,wschar in ws_map.items():
+                        ret = ret.replace(wslit, wschar)
+
+                # replace escaped characters
+                if self.escChar:
+                    ret = re.sub(self.escCharReplacePattern, r"\g<1>", ret)
+
+                # replace escaped quotes
+                if self.escQuote:
+                    ret = ret.replace(self.escQuote, self.endQuoteChar)
+
+        return loc, ret
+
+    def __str__( self ):
+        try:
+            return super(QuotedString,self).__str__()
+        except Exception:
+            pass
+
+        if self.strRepr is None:
+            self.strRepr = "quoted string, starting with %s ending with %s" % (self.quoteChar, self.endQuoteChar)
+
+        return self.strRepr
+
+
+class CharsNotIn(Token):
+    """
+    Token for matching words composed of characters I{not} in a given set (will
+    include whitespace in matched characters if not listed in the provided exclusion set - see example).
+    Defined with string containing all disallowed characters, and an optional
+    minimum, maximum, and/or exact length.  The default value for C{min} is 1 (a
+    minimum value < 1 is not valid); the default values for C{max} and C{exact}
+    are 0, meaning no maximum or exact length restriction.
+
+    Example::
+        # define a comma-separated-value as anything that is not a ','
+        csv_value = CharsNotIn(',')
+        print(delimitedList(csv_value).parseString("dkls,lsdkjf,s12 34,@!#,213"))
+    prints::
+        ['dkls', 'lsdkjf', 's12 34', '@!#', '213']
+    """
+    def __init__( self, notChars, min=1, max=0, exact=0 ):
+        super(CharsNotIn,self).__init__()
+        self.skipWhitespace = False
+        self.notChars = notChars
+
+        if min < 1:
+            raise ValueError("cannot specify a minimum length < 1; use Optional(CharsNotIn()) if zero-length char group is permitted")
+
+        self.minLen = min
+
+        if max > 0:
+            self.maxLen = max
+        else:
+            self.maxLen = _MAX_INT
+
+        if exact > 0:
+            self.maxLen = exact
+            self.minLen = exact
+
+        self.name = _ustr(self)
+        self.errmsg = "Expected " + self.name
+        self.mayReturnEmpty = ( self.minLen == 0 )
+        self.mayIndexError = False
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if instring[loc] in self.notChars:
+            raise ParseException(instring, loc, self.errmsg, self)
+
+        start = loc
+        loc += 1
+        notchars = self.notChars
+        maxlen = min( start+self.maxLen, len(instring) )
+        while loc < maxlen and \
+              (instring[loc] not in notchars):
+            loc += 1
+
+        if loc - start < self.minLen:
+            raise ParseException(instring, loc, self.errmsg, self)
+
+        return loc, instring[start:loc]
+
+    def __str__( self ):
+        try:
+            return super(CharsNotIn, self).__str__()
+        except Exception:
+            pass
+
+        if self.strRepr is None:
+            if len(self.notChars) > 4:
+                self.strRepr = "!W:(%s...)" % self.notChars[:4]
+            else:
+                self.strRepr = "!W:(%s)" % self.notChars
+
+        return self.strRepr
+
+class White(Token):
+    """
+    Special matching class for matching whitespace.  Normally, whitespace is ignored
+    by pyparsing grammars.  This class is included when some whitespace structures
+    are significant.  Define with a string containing the whitespace characters to be
+    matched; default is C{" \\t\\r\\n"}.  Also takes optional C{min}, C{max}, and C{exact} arguments,
+    as defined for the C{L{Word}} class.
+    """
+    whiteStrs = {
+        " " : "",
+        "\t": "",
+        "\n": "",
+        "\r": "",
+        "\f": "",
+        }
+    def __init__(self, ws=" \t\r\n", min=1, max=0, exact=0):
+        super(White,self).__init__()
+        self.matchWhite = ws
+        self.setWhitespaceChars( "".join(c for c in self.whiteChars if c not in self.matchWhite) )
+        #~ self.leaveWhitespace()
+        self.name = ("".join(White.whiteStrs[c] for c in self.matchWhite))
+        self.mayReturnEmpty = True
+        self.errmsg = "Expected " + self.name
+
+        self.minLen = min
+
+        if max > 0:
+            self.maxLen = max
+        else:
+            self.maxLen = _MAX_INT
+
+        if exact > 0:
+            self.maxLen = exact
+            self.minLen = exact
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if not(instring[ loc ] in self.matchWhite):
+            raise ParseException(instring, loc, self.errmsg, self)
+        start = loc
+        loc += 1
+        maxloc = start + self.maxLen
+        maxloc = min( maxloc, len(instring) )
+        while loc < maxloc and instring[loc] in self.matchWhite:
+            loc += 1
+
+        if loc - start < self.minLen:
+            raise ParseException(instring, loc, self.errmsg, self)
+
+        return loc, instring[start:loc]
+
+
+class _PositionToken(Token):
+    def __init__( self ):
+        super(_PositionToken,self).__init__()
+        self.name=self.__class__.__name__
+        self.mayReturnEmpty = True
+        self.mayIndexError = False
+
+class GoToColumn(_PositionToken):
+    """
+    Token to advance to a specific column of input text; useful for tabular report scraping.
+    """
+    def __init__( self, colno ):
+        super(GoToColumn,self).__init__()
+        self.col = colno
+
+    def preParse( self, instring, loc ):
+        if col(loc,instring) != self.col:
+            instrlen = len(instring)
+            if self.ignoreExprs:
+                loc = self._skipIgnorables( instring, loc )
+            while loc < instrlen and instring[loc].isspace() and col( loc, instring ) != self.col :
+                loc += 1
+        return loc
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        thiscol = col( loc, instring )
+        if thiscol > self.col:
+            raise ParseException( instring, loc, "Text not in expected column", self )
+        newloc = loc + self.col - thiscol
+        ret = instring[ loc: newloc ]
+        return newloc, ret
+
+
+class LineStart(_PositionToken):
+    """
+    Matches if current position is at the beginning of a line within the parse string
+    
+    Example::
+    
+        test = '''\
+        AAA this line
+        AAA and this line
+          AAA but not this one
+        B AAA and definitely not this one
+        '''
+
+        for t in (LineStart() + 'AAA' + restOfLine).searchString(test):
+            print(t)
+    
+    Prints::
+        ['AAA', ' this line']
+        ['AAA', ' and this line']    
+
+    """
+    def __init__( self ):
+        super(LineStart,self).__init__()
+        self.errmsg = "Expected start of line"
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if col(loc, instring) == 1:
+            return loc, []
+        raise ParseException(instring, loc, self.errmsg, self)
+
+class LineEnd(_PositionToken):
+    """
+    Matches if current position is at the end of a line within the parse string
+    """
+    def __init__( self ):
+        super(LineEnd,self).__init__()
+        self.setWhitespaceChars( ParserElement.DEFAULT_WHITE_CHARS.replace("\n","") )
+        self.errmsg = "Expected end of line"
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if loc len(instring):
+            return loc, []
+        else:
+            raise ParseException(instring, loc, self.errmsg, self)
+
+class WordStart(_PositionToken):
+    """
+    Matches if the current position is at the beginning of a Word, and
+    is not preceded by any character in a given set of C{wordChars}
+    (default=C{printables}). To emulate the C{\b} behavior of regular expressions,
+    use C{WordStart(alphanums)}. C{WordStart} will also match at the beginning of
+    the string being parsed, or at the beginning of a line.
+    """
+    def __init__(self, wordChars = printables):
+        super(WordStart,self).__init__()
+        self.wordChars = set(wordChars)
+        self.errmsg = "Not at the start of a word"
+
+    def parseImpl(self, instring, loc, doActions=True ):
+        if loc != 0:
+            if (instring[loc-1] in self.wordChars or
+                instring[loc] not in self.wordChars):
+                raise ParseException(instring, loc, self.errmsg, self)
+        return loc, []
+
+class WordEnd(_PositionToken):
+    """
+    Matches if the current position is at the end of a Word, and
+    is not followed by any character in a given set of C{wordChars}
+    (default=C{printables}). To emulate the C{\b} behavior of regular expressions,
+    use C{WordEnd(alphanums)}. C{WordEnd} will also match at the end of
+    the string being parsed, or at the end of a line.
+    """
+    def __init__(self, wordChars = printables):
+        super(WordEnd,self).__init__()
+        self.wordChars = set(wordChars)
+        self.skipWhitespace = False
+        self.errmsg = "Not at the end of a word"
+
+    def parseImpl(self, instring, loc, doActions=True ):
+        instrlen = len(instring)
+        if instrlen>0 and loc maxExcLoc:
+                    maxException = err
+                    maxExcLoc = err.loc
+            except IndexError:
+                if len(instring) > maxExcLoc:
+                    maxException = ParseException(instring,len(instring),e.errmsg,self)
+                    maxExcLoc = len(instring)
+            else:
+                # save match among all matches, to retry longest to shortest
+                matches.append((loc2, e))
+
+        if matches:
+            matches.sort(key=lambda x: -x[0])
+            for _,e in matches:
+                try:
+                    return e._parse( instring, loc, doActions )
+                except ParseException as err:
+                    err.__traceback__ = None
+                    if err.loc > maxExcLoc:
+                        maxException = err
+                        maxExcLoc = err.loc
+
+        if maxException is not None:
+            maxException.msg = self.errmsg
+            raise maxException
+        else:
+            raise ParseException(instring, loc, "no defined alternatives to match", self)
+
+
+    def __ixor__(self, other ):
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        return self.append( other ) #Or( [ self, other ] )
+
+    def __str__( self ):
+        if hasattr(self,"name"):
+            return self.name
+
+        if self.strRepr is None:
+            self.strRepr = "{" + " ^ ".join(_ustr(e) for e in self.exprs) + "}"
+
+        return self.strRepr
+
+    def checkRecursion( self, parseElementList ):
+        subRecCheckList = parseElementList[:] + [ self ]
+        for e in self.exprs:
+            e.checkRecursion( subRecCheckList )
+
+
+class MatchFirst(ParseExpression):
+    """
+    Requires that at least one C{ParseExpression} is found.
+    If two expressions match, the first one listed is the one that will match.
+    May be constructed using the C{'|'} operator.
+
+    Example::
+        # construct MatchFirst using '|' operator
+        
+        # watch the order of expressions to match
+        number = Word(nums) | Combine(Word(nums) + '.' + Word(nums))
+        print(number.searchString("123 3.1416 789")) #  Fail! -> [['123'], ['3'], ['1416'], ['789']]
+
+        # put more selective expression first
+        number = Combine(Word(nums) + '.' + Word(nums)) | Word(nums)
+        print(number.searchString("123 3.1416 789")) #  Better -> [['123'], ['3.1416'], ['789']]
+    """
+    def __init__( self, exprs, savelist = False ):
+        super(MatchFirst,self).__init__(exprs, savelist)
+        if self.exprs:
+            self.mayReturnEmpty = any(e.mayReturnEmpty for e in self.exprs)
+        else:
+            self.mayReturnEmpty = True
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        maxExcLoc = -1
+        maxException = None
+        for e in self.exprs:
+            try:
+                ret = e._parse( instring, loc, doActions )
+                return ret
+            except ParseException as err:
+                if err.loc > maxExcLoc:
+                    maxException = err
+                    maxExcLoc = err.loc
+            except IndexError:
+                if len(instring) > maxExcLoc:
+                    maxException = ParseException(instring,len(instring),e.errmsg,self)
+                    maxExcLoc = len(instring)
+
+        # only got here if no expression matched, raise exception for match that made it the furthest
+        else:
+            if maxException is not None:
+                maxException.msg = self.errmsg
+                raise maxException
+            else:
+                raise ParseException(instring, loc, "no defined alternatives to match", self)
+
+    def __ior__(self, other ):
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        return self.append( other ) #MatchFirst( [ self, other ] )
+
+    def __str__( self ):
+        if hasattr(self,"name"):
+            return self.name
+
+        if self.strRepr is None:
+            self.strRepr = "{" + " | ".join(_ustr(e) for e in self.exprs) + "}"
+
+        return self.strRepr
+
+    def checkRecursion( self, parseElementList ):
+        subRecCheckList = parseElementList[:] + [ self ]
+        for e in self.exprs:
+            e.checkRecursion( subRecCheckList )
+
+
+class Each(ParseExpression):
+    """
+    Requires all given C{ParseExpression}s to be found, but in any order.
+    Expressions may be separated by whitespace.
+    May be constructed using the C{'&'} operator.
+
+    Example::
+        color = oneOf("RED ORANGE YELLOW GREEN BLUE PURPLE BLACK WHITE BROWN")
+        shape_type = oneOf("SQUARE CIRCLE TRIANGLE STAR HEXAGON OCTAGON")
+        integer = Word(nums)
+        shape_attr = "shape:" + shape_type("shape")
+        posn_attr = "posn:" + Group(integer("x") + ',' + integer("y"))("posn")
+        color_attr = "color:" + color("color")
+        size_attr = "size:" + integer("size")
+
+        # use Each (using operator '&') to accept attributes in any order 
+        # (shape and posn are required, color and size are optional)
+        shape_spec = shape_attr & posn_attr & Optional(color_attr) & Optional(size_attr)
+
+        shape_spec.runTests('''
+            shape: SQUARE color: BLACK posn: 100, 120
+            shape: CIRCLE size: 50 color: BLUE posn: 50,80
+            color:GREEN size:20 shape:TRIANGLE posn:20,40
+            '''
+            )
+    prints::
+        shape: SQUARE color: BLACK posn: 100, 120
+        ['shape:', 'SQUARE', 'color:', 'BLACK', 'posn:', ['100', ',', '120']]
+        - color: BLACK
+        - posn: ['100', ',', '120']
+          - x: 100
+          - y: 120
+        - shape: SQUARE
+
+
+        shape: CIRCLE size: 50 color: BLUE posn: 50,80
+        ['shape:', 'CIRCLE', 'size:', '50', 'color:', 'BLUE', 'posn:', ['50', ',', '80']]
+        - color: BLUE
+        - posn: ['50', ',', '80']
+          - x: 50
+          - y: 80
+        - shape: CIRCLE
+        - size: 50
+
+
+        color: GREEN size: 20 shape: TRIANGLE posn: 20,40
+        ['color:', 'GREEN', 'size:', '20', 'shape:', 'TRIANGLE', 'posn:', ['20', ',', '40']]
+        - color: GREEN
+        - posn: ['20', ',', '40']
+          - x: 20
+          - y: 40
+        - shape: TRIANGLE
+        - size: 20
+    """
+    def __init__( self, exprs, savelist = True ):
+        super(Each,self).__init__(exprs, savelist)
+        self.mayReturnEmpty = all(e.mayReturnEmpty for e in self.exprs)
+        self.skipWhitespace = True
+        self.initExprGroups = True
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if self.initExprGroups:
+            self.opt1map = dict((id(e.expr),e) for e in self.exprs if isinstance(e,Optional))
+            opt1 = [ e.expr for e in self.exprs if isinstance(e,Optional) ]
+            opt2 = [ e for e in self.exprs if e.mayReturnEmpty and not isinstance(e,Optional)]
+            self.optionals = opt1 + opt2
+            self.multioptionals = [ e.expr for e in self.exprs if isinstance(e,ZeroOrMore) ]
+            self.multirequired = [ e.expr for e in self.exprs if isinstance(e,OneOrMore) ]
+            self.required = [ e for e in self.exprs if not isinstance(e,(Optional,ZeroOrMore,OneOrMore)) ]
+            self.required += self.multirequired
+            self.initExprGroups = False
+        tmpLoc = loc
+        tmpReqd = self.required[:]
+        tmpOpt  = self.optionals[:]
+        matchOrder = []
+
+        keepMatching = True
+        while keepMatching:
+            tmpExprs = tmpReqd + tmpOpt + self.multioptionals + self.multirequired
+            failed = []
+            for e in tmpExprs:
+                try:
+                    tmpLoc = e.tryParse( instring, tmpLoc )
+                except ParseException:
+                    failed.append(e)
+                else:
+                    matchOrder.append(self.opt1map.get(id(e),e))
+                    if e in tmpReqd:
+                        tmpReqd.remove(e)
+                    elif e in tmpOpt:
+                        tmpOpt.remove(e)
+            if len(failed) == len(tmpExprs):
+                keepMatching = False
+
+        if tmpReqd:
+            missing = ", ".join(_ustr(e) for e in tmpReqd)
+            raise ParseException(instring,loc,"Missing one or more required elements (%s)" % missing )
+
+        # add any unmatched Optionals, in case they have default values defined
+        matchOrder += [e for e in self.exprs if isinstance(e,Optional) and e.expr in tmpOpt]
+
+        resultlist = []
+        for e in matchOrder:
+            loc,results = e._parse(instring,loc,doActions)
+            resultlist.append(results)
+
+        finalResults = sum(resultlist, ParseResults([]))
+        return loc, finalResults
+
+    def __str__( self ):
+        if hasattr(self,"name"):
+            return self.name
+
+        if self.strRepr is None:
+            self.strRepr = "{" + " & ".join(_ustr(e) for e in self.exprs) + "}"
+
+        return self.strRepr
+
+    def checkRecursion( self, parseElementList ):
+        subRecCheckList = parseElementList[:] + [ self ]
+        for e in self.exprs:
+            e.checkRecursion( subRecCheckList )
+
+
+class ParseElementEnhance(ParserElement):
+    """
+    Abstract subclass of C{ParserElement}, for combining and post-processing parsed tokens.
+    """
+    def __init__( self, expr, savelist=False ):
+        super(ParseElementEnhance,self).__init__(savelist)
+        if isinstance( expr, basestring ):
+            if issubclass(ParserElement._literalStringClass, Token):
+                expr = ParserElement._literalStringClass(expr)
+            else:
+                expr = ParserElement._literalStringClass(Literal(expr))
+        self.expr = expr
+        self.strRepr = None
+        if expr is not None:
+            self.mayIndexError = expr.mayIndexError
+            self.mayReturnEmpty = expr.mayReturnEmpty
+            self.setWhitespaceChars( expr.whiteChars )
+            self.skipWhitespace = expr.skipWhitespace
+            self.saveAsList = expr.saveAsList
+            self.callPreparse = expr.callPreparse
+            self.ignoreExprs.extend(expr.ignoreExprs)
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if self.expr is not None:
+            return self.expr._parse( instring, loc, doActions, callPreParse=False )
+        else:
+            raise ParseException("",loc,self.errmsg,self)
+
+    def leaveWhitespace( self ):
+        self.skipWhitespace = False
+        self.expr = self.expr.copy()
+        if self.expr is not None:
+            self.expr.leaveWhitespace()
+        return self
+
+    def ignore( self, other ):
+        if isinstance( other, Suppress ):
+            if other not in self.ignoreExprs:
+                super( ParseElementEnhance, self).ignore( other )
+                if self.expr is not None:
+                    self.expr.ignore( self.ignoreExprs[-1] )
+        else:
+            super( ParseElementEnhance, self).ignore( other )
+            if self.expr is not None:
+                self.expr.ignore( self.ignoreExprs[-1] )
+        return self
+
+    def streamline( self ):
+        super(ParseElementEnhance,self).streamline()
+        if self.expr is not None:
+            self.expr.streamline()
+        return self
+
+    def checkRecursion( self, parseElementList ):
+        if self in parseElementList:
+            raise RecursiveGrammarException( parseElementList+[self] )
+        subRecCheckList = parseElementList[:] + [ self ]
+        if self.expr is not None:
+            self.expr.checkRecursion( subRecCheckList )
+
+    def validate( self, validateTrace=[] ):
+        tmp = validateTrace[:]+[self]
+        if self.expr is not None:
+            self.expr.validate(tmp)
+        self.checkRecursion( [] )
+
+    def __str__( self ):
+        try:
+            return super(ParseElementEnhance,self).__str__()
+        except Exception:
+            pass
+
+        if self.strRepr is None and self.expr is not None:
+            self.strRepr = "%s:(%s)" % ( self.__class__.__name__, _ustr(self.expr) )
+        return self.strRepr
+
+
+class FollowedBy(ParseElementEnhance):
+    """
+    Lookahead matching of the given parse expression.  C{FollowedBy}
+    does I{not} advance the parsing position within the input string, it only
+    verifies that the specified parse expression matches at the current
+    position.  C{FollowedBy} always returns a null token list.
+
+    Example::
+        # use FollowedBy to match a label only if it is followed by a ':'
+        data_word = Word(alphas)
+        label = data_word + FollowedBy(':')
+        attr_expr = Group(label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join))
+        
+        OneOrMore(attr_expr).parseString("shape: SQUARE color: BLACK posn: upper left").pprint()
+    prints::
+        [['shape', 'SQUARE'], ['color', 'BLACK'], ['posn', 'upper left']]
+    """
+    def __init__( self, expr ):
+        super(FollowedBy,self).__init__(expr)
+        self.mayReturnEmpty = True
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        self.expr.tryParse( instring, loc )
+        return loc, []
+
+
+class NotAny(ParseElementEnhance):
+    """
+    Lookahead to disallow matching with the given parse expression.  C{NotAny}
+    does I{not} advance the parsing position within the input string, it only
+    verifies that the specified parse expression does I{not} match at the current
+    position.  Also, C{NotAny} does I{not} skip over leading whitespace. C{NotAny}
+    always returns a null token list.  May be constructed using the '~' operator.
+
+    Example::
+        
+    """
+    def __init__( self, expr ):
+        super(NotAny,self).__init__(expr)
+        #~ self.leaveWhitespace()
+        self.skipWhitespace = False  # do NOT use self.leaveWhitespace(), don't want to propagate to exprs
+        self.mayReturnEmpty = True
+        self.errmsg = "Found unwanted token, "+_ustr(self.expr)
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if self.expr.canParseNext(instring, loc):
+            raise ParseException(instring, loc, self.errmsg, self)
+        return loc, []
+
+    def __str__( self ):
+        if hasattr(self,"name"):
+            return self.name
+
+        if self.strRepr is None:
+            self.strRepr = "~{" + _ustr(self.expr) + "}"
+
+        return self.strRepr
+
+class _MultipleMatch(ParseElementEnhance):
+    def __init__( self, expr, stopOn=None):
+        super(_MultipleMatch, self).__init__(expr)
+        self.saveAsList = True
+        ender = stopOn
+        if isinstance(ender, basestring):
+            ender = ParserElement._literalStringClass(ender)
+        self.not_ender = ~ender if ender is not None else None
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        self_expr_parse = self.expr._parse
+        self_skip_ignorables = self._skipIgnorables
+        check_ender = self.not_ender is not None
+        if check_ender:
+            try_not_ender = self.not_ender.tryParse
+        
+        # must be at least one (but first see if we are the stopOn sentinel;
+        # if so, fail)
+        if check_ender:
+            try_not_ender(instring, loc)
+        loc, tokens = self_expr_parse( instring, loc, doActions, callPreParse=False )
+        try:
+            hasIgnoreExprs = (not not self.ignoreExprs)
+            while 1:
+                if check_ender:
+                    try_not_ender(instring, loc)
+                if hasIgnoreExprs:
+                    preloc = self_skip_ignorables( instring, loc )
+                else:
+                    preloc = loc
+                loc, tmptokens = self_expr_parse( instring, preloc, doActions )
+                if tmptokens or tmptokens.haskeys():
+                    tokens += tmptokens
+        except (ParseException,IndexError):
+            pass
+
+        return loc, tokens
+        
+class OneOrMore(_MultipleMatch):
+    """
+    Repetition of one or more of the given expression.
+    
+    Parameters:
+     - expr - expression that must match one or more times
+     - stopOn - (default=C{None}) - expression for a terminating sentinel
+          (only required if the sentinel would ordinarily match the repetition 
+          expression)          
+
+    Example::
+        data_word = Word(alphas)
+        label = data_word + FollowedBy(':')
+        attr_expr = Group(label + Suppress(':') + OneOrMore(data_word).setParseAction(' '.join))
+
+        text = "shape: SQUARE posn: upper left color: BLACK"
+        OneOrMore(attr_expr).parseString(text).pprint()  # Fail! read 'color' as data instead of next label -> [['shape', 'SQUARE color']]
+
+        # use stopOn attribute for OneOrMore to avoid reading label string as part of the data
+        attr_expr = Group(label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join))
+        OneOrMore(attr_expr).parseString(text).pprint() # Better -> [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'BLACK']]
+        
+        # could also be written as
+        (attr_expr * (1,)).parseString(text).pprint()
+    """
+
+    def __str__( self ):
+        if hasattr(self,"name"):
+            return self.name
+
+        if self.strRepr is None:
+            self.strRepr = "{" + _ustr(self.expr) + "}..."
+
+        return self.strRepr
+
+class ZeroOrMore(_MultipleMatch):
+    """
+    Optional repetition of zero or more of the given expression.
+    
+    Parameters:
+     - expr - expression that must match zero or more times
+     - stopOn - (default=C{None}) - expression for a terminating sentinel
+          (only required if the sentinel would ordinarily match the repetition 
+          expression)          
+
+    Example: similar to L{OneOrMore}
+    """
+    def __init__( self, expr, stopOn=None):
+        super(ZeroOrMore,self).__init__(expr, stopOn=stopOn)
+        self.mayReturnEmpty = True
+        
+    def parseImpl( self, instring, loc, doActions=True ):
+        try:
+            return super(ZeroOrMore, self).parseImpl(instring, loc, doActions)
+        except (ParseException,IndexError):
+            return loc, []
+
+    def __str__( self ):
+        if hasattr(self,"name"):
+            return self.name
+
+        if self.strRepr is None:
+            self.strRepr = "[" + _ustr(self.expr) + "]..."
+
+        return self.strRepr
+
+class _NullToken(object):
+    def __bool__(self):
+        return False
+    __nonzero__ = __bool__
+    def __str__(self):
+        return ""
+
+_optionalNotMatched = _NullToken()
+class Optional(ParseElementEnhance):
+    """
+    Optional matching of the given expression.
+
+    Parameters:
+     - expr - expression that must match zero or more times
+     - default (optional) - value to be returned if the optional expression is not found.
+
+    Example::
+        # US postal code can be a 5-digit zip, plus optional 4-digit qualifier
+        zip = Combine(Word(nums, exact=5) + Optional('-' + Word(nums, exact=4)))
+        zip.runTests('''
+            # traditional ZIP code
+            12345
+            
+            # ZIP+4 form
+            12101-0001
+            
+            # invalid ZIP
+            98765-
+            ''')
+    prints::
+        # traditional ZIP code
+        12345
+        ['12345']
+
+        # ZIP+4 form
+        12101-0001
+        ['12101-0001']
+
+        # invalid ZIP
+        98765-
+             ^
+        FAIL: Expected end of text (at char 5), (line:1, col:6)
+    """
+    def __init__( self, expr, default=_optionalNotMatched ):
+        super(Optional,self).__init__( expr, savelist=False )
+        self.saveAsList = self.expr.saveAsList
+        self.defaultValue = default
+        self.mayReturnEmpty = True
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        try:
+            loc, tokens = self.expr._parse( instring, loc, doActions, callPreParse=False )
+        except (ParseException,IndexError):
+            if self.defaultValue is not _optionalNotMatched:
+                if self.expr.resultsName:
+                    tokens = ParseResults([ self.defaultValue ])
+                    tokens[self.expr.resultsName] = self.defaultValue
+                else:
+                    tokens = [ self.defaultValue ]
+            else:
+                tokens = []
+        return loc, tokens
+
+    def __str__( self ):
+        if hasattr(self,"name"):
+            return self.name
+
+        if self.strRepr is None:
+            self.strRepr = "[" + _ustr(self.expr) + "]"
+
+        return self.strRepr
+
+class SkipTo(ParseElementEnhance):
+    """
+    Token for skipping over all undefined text until the matched expression is found.
+
+    Parameters:
+     - expr - target expression marking the end of the data to be skipped
+     - include - (default=C{False}) if True, the target expression is also parsed 
+          (the skipped text and target expression are returned as a 2-element list).
+     - ignore - (default=C{None}) used to define grammars (typically quoted strings and 
+          comments) that might contain false matches to the target expression
+     - failOn - (default=C{None}) define expressions that are not allowed to be 
+          included in the skipped test; if found before the target expression is found, 
+          the SkipTo is not a match
+
+    Example::
+        report = '''
+            Outstanding Issues Report - 1 Jan 2000
+
+               # | Severity | Description                               |  Days Open
+            -----+----------+-------------------------------------------+-----------
+             101 | Critical | Intermittent system crash                 |          6
+              94 | Cosmetic | Spelling error on Login ('log|n')         |         14
+              79 | Minor    | System slow when running too many reports |         47
+            '''
+        integer = Word(nums)
+        SEP = Suppress('|')
+        # use SkipTo to simply match everything up until the next SEP
+        # - ignore quoted strings, so that a '|' character inside a quoted string does not match
+        # - parse action will call token.strip() for each matched token, i.e., the description body
+        string_data = SkipTo(SEP, ignore=quotedString)
+        string_data.setParseAction(tokenMap(str.strip))
+        ticket_expr = (integer("issue_num") + SEP 
+                      + string_data("sev") + SEP 
+                      + string_data("desc") + SEP 
+                      + integer("days_open"))
+        
+        for tkt in ticket_expr.searchString(report):
+            print tkt.dump()
+    prints::
+        ['101', 'Critical', 'Intermittent system crash', '6']
+        - days_open: 6
+        - desc: Intermittent system crash
+        - issue_num: 101
+        - sev: Critical
+        ['94', 'Cosmetic', "Spelling error on Login ('log|n')", '14']
+        - days_open: 14
+        - desc: Spelling error on Login ('log|n')
+        - issue_num: 94
+        - sev: Cosmetic
+        ['79', 'Minor', 'System slow when running too many reports', '47']
+        - days_open: 47
+        - desc: System slow when running too many reports
+        - issue_num: 79
+        - sev: Minor
+    """
+    def __init__( self, other, include=False, ignore=None, failOn=None ):
+        super( SkipTo, self ).__init__( other )
+        self.ignoreExpr = ignore
+        self.mayReturnEmpty = True
+        self.mayIndexError = False
+        self.includeMatch = include
+        self.asList = False
+        if isinstance(failOn, basestring):
+            self.failOn = ParserElement._literalStringClass(failOn)
+        else:
+            self.failOn = failOn
+        self.errmsg = "No match found for "+_ustr(self.expr)
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        startloc = loc
+        instrlen = len(instring)
+        expr = self.expr
+        expr_parse = self.expr._parse
+        self_failOn_canParseNext = self.failOn.canParseNext if self.failOn is not None else None
+        self_ignoreExpr_tryParse = self.ignoreExpr.tryParse if self.ignoreExpr is not None else None
+        
+        tmploc = loc
+        while tmploc <= instrlen:
+            if self_failOn_canParseNext is not None:
+                # break if failOn expression matches
+                if self_failOn_canParseNext(instring, tmploc):
+                    break
+                    
+            if self_ignoreExpr_tryParse is not None:
+                # advance past ignore expressions
+                while 1:
+                    try:
+                        tmploc = self_ignoreExpr_tryParse(instring, tmploc)
+                    except ParseBaseException:
+                        break
+            
+            try:
+                expr_parse(instring, tmploc, doActions=False, callPreParse=False)
+            except (ParseException, IndexError):
+                # no match, advance loc in string
+                tmploc += 1
+            else:
+                # matched skipto expr, done
+                break
+
+        else:
+            # ran off the end of the input string without matching skipto expr, fail
+            raise ParseException(instring, loc, self.errmsg, self)
+
+        # build up return values
+        loc = tmploc
+        skiptext = instring[startloc:loc]
+        skipresult = ParseResults(skiptext)
+        
+        if self.includeMatch:
+            loc, mat = expr_parse(instring,loc,doActions,callPreParse=False)
+            skipresult += mat
+
+        return loc, skipresult
+
+class Forward(ParseElementEnhance):
+    """
+    Forward declaration of an expression to be defined later -
+    used for recursive grammars, such as algebraic infix notation.
+    When the expression is known, it is assigned to the C{Forward} variable using the '<<' operator.
+
+    Note: take care when assigning to C{Forward} not to overlook precedence of operators.
+    Specifically, '|' has a lower precedence than '<<', so that::
+        fwdExpr << a | b | c
+    will actually be evaluated as::
+        (fwdExpr << a) | b | c
+    thereby leaving b and c out as parseable alternatives.  It is recommended that you
+    explicitly group the values inserted into the C{Forward}::
+        fwdExpr << (a | b | c)
+    Converting to use the '<<=' operator instead will avoid this problem.
+
+    See L{ParseResults.pprint} for an example of a recursive parser created using
+    C{Forward}.
+    """
+    def __init__( self, other=None ):
+        super(Forward,self).__init__( other, savelist=False )
+
+    def __lshift__( self, other ):
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass(other)
+        self.expr = other
+        self.strRepr = None
+        self.mayIndexError = self.expr.mayIndexError
+        self.mayReturnEmpty = self.expr.mayReturnEmpty
+        self.setWhitespaceChars( self.expr.whiteChars )
+        self.skipWhitespace = self.expr.skipWhitespace
+        self.saveAsList = self.expr.saveAsList
+        self.ignoreExprs.extend(self.expr.ignoreExprs)
+        return self
+        
+    def __ilshift__(self, other):
+        return self << other
+    
+    def leaveWhitespace( self ):
+        self.skipWhitespace = False
+        return self
+
+    def streamline( self ):
+        if not self.streamlined:
+            self.streamlined = True
+            if self.expr is not None:
+                self.expr.streamline()
+        return self
+
+    def validate( self, validateTrace=[] ):
+        if self not in validateTrace:
+            tmp = validateTrace[:]+[self]
+            if self.expr is not None:
+                self.expr.validate(tmp)
+        self.checkRecursion([])
+
+    def __str__( self ):
+        if hasattr(self,"name"):
+            return self.name
+        return self.__class__.__name__ + ": ..."
+
+        # stubbed out for now - creates awful memory and perf issues
+        self._revertClass = self.__class__
+        self.__class__ = _ForwardNoRecurse
+        try:
+            if self.expr is not None:
+                retString = _ustr(self.expr)
+            else:
+                retString = "None"
+        finally:
+            self.__class__ = self._revertClass
+        return self.__class__.__name__ + ": " + retString
+
+    def copy(self):
+        if self.expr is not None:
+            return super(Forward,self).copy()
+        else:
+            ret = Forward()
+            ret <<= self
+            return ret
+
+class _ForwardNoRecurse(Forward):
+    def __str__( self ):
+        return "..."
+
+class TokenConverter(ParseElementEnhance):
+    """
+    Abstract subclass of C{ParseExpression}, for converting parsed results.
+    """
+    def __init__( self, expr, savelist=False ):
+        super(TokenConverter,self).__init__( expr )#, savelist )
+        self.saveAsList = False
+
+class Combine(TokenConverter):
+    """
+    Converter to concatenate all matching tokens to a single string.
+    By default, the matching patterns must also be contiguous in the input string;
+    this can be disabled by specifying C{'adjacent=False'} in the constructor.
+
+    Example::
+        real = Word(nums) + '.' + Word(nums)
+        print(real.parseString('3.1416')) # -> ['3', '.', '1416']
+        # will also erroneously match the following
+        print(real.parseString('3. 1416')) # -> ['3', '.', '1416']
+
+        real = Combine(Word(nums) + '.' + Word(nums))
+        print(real.parseString('3.1416')) # -> ['3.1416']
+        # no match when there are internal spaces
+        print(real.parseString('3. 1416')) # -> Exception: Expected W:(0123...)
+    """
+    def __init__( self, expr, joinString="", adjacent=True ):
+        super(Combine,self).__init__( expr )
+        # suppress whitespace-stripping in contained parse expressions, but re-enable it on the Combine itself
+        if adjacent:
+            self.leaveWhitespace()
+        self.adjacent = adjacent
+        self.skipWhitespace = True
+        self.joinString = joinString
+        self.callPreparse = True
+
+    def ignore( self, other ):
+        if self.adjacent:
+            ParserElement.ignore(self, other)
+        else:
+            super( Combine, self).ignore( other )
+        return self
+
+    def postParse( self, instring, loc, tokenlist ):
+        retToks = tokenlist.copy()
+        del retToks[:]
+        retToks += ParseResults([ "".join(tokenlist._asStringList(self.joinString)) ], modal=self.modalResults)
+
+        if self.resultsName and retToks.haskeys():
+            return [ retToks ]
+        else:
+            return retToks
+
+class Group(TokenConverter):
+    """
+    Converter to return the matched tokens as a list - useful for returning tokens of C{L{ZeroOrMore}} and C{L{OneOrMore}} expressions.
+
+    Example::
+        ident = Word(alphas)
+        num = Word(nums)
+        term = ident | num
+        func = ident + Optional(delimitedList(term))
+        print(func.parseString("fn a,b,100"))  # -> ['fn', 'a', 'b', '100']
+
+        func = ident + Group(Optional(delimitedList(term)))
+        print(func.parseString("fn a,b,100"))  # -> ['fn', ['a', 'b', '100']]
+    """
+    def __init__( self, expr ):
+        super(Group,self).__init__( expr )
+        self.saveAsList = True
+
+    def postParse( self, instring, loc, tokenlist ):
+        return [ tokenlist ]
+
+class Dict(TokenConverter):
+    """
+    Converter to return a repetitive expression as a list, but also as a dictionary.
+    Each element can also be referenced using the first token in the expression as its key.
+    Useful for tabular report scraping when the first column can be used as a item key.
+
+    Example::
+        data_word = Word(alphas)
+        label = data_word + FollowedBy(':')
+        attr_expr = Group(label + Suppress(':') + OneOrMore(data_word).setParseAction(' '.join))
+
+        text = "shape: SQUARE posn: upper left color: light blue texture: burlap"
+        attr_expr = (label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join))
+        
+        # print attributes as plain groups
+        print(OneOrMore(attr_expr).parseString(text).dump())
+        
+        # instead of OneOrMore(expr), parse using Dict(OneOrMore(Group(expr))) - Dict will auto-assign names
+        result = Dict(OneOrMore(Group(attr_expr))).parseString(text)
+        print(result.dump())
+        
+        # access named fields as dict entries, or output as dict
+        print(result['shape'])        
+        print(result.asDict())
+    prints::
+        ['shape', 'SQUARE', 'posn', 'upper left', 'color', 'light blue', 'texture', 'burlap']
+
+        [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'light blue'], ['texture', 'burlap']]
+        - color: light blue
+        - posn: upper left
+        - shape: SQUARE
+        - texture: burlap
+        SQUARE
+        {'color': 'light blue', 'posn': 'upper left', 'texture': 'burlap', 'shape': 'SQUARE'}
+    See more examples at L{ParseResults} of accessing fields by results name.
+    """
+    def __init__( self, expr ):
+        super(Dict,self).__init__( expr )
+        self.saveAsList = True
+
+    def postParse( self, instring, loc, tokenlist ):
+        for i,tok in enumerate(tokenlist):
+            if len(tok) == 0:
+                continue
+            ikey = tok[0]
+            if isinstance(ikey,int):
+                ikey = _ustr(tok[0]).strip()
+            if len(tok)==1:
+                tokenlist[ikey] = _ParseResultsWithOffset("",i)
+            elif len(tok)==2 and not isinstance(tok[1],ParseResults):
+                tokenlist[ikey] = _ParseResultsWithOffset(tok[1],i)
+            else:
+                dictvalue = tok.copy() #ParseResults(i)
+                del dictvalue[0]
+                if len(dictvalue)!= 1 or (isinstance(dictvalue,ParseResults) and dictvalue.haskeys()):
+                    tokenlist[ikey] = _ParseResultsWithOffset(dictvalue,i)
+                else:
+                    tokenlist[ikey] = _ParseResultsWithOffset(dictvalue[0],i)
+
+        if self.resultsName:
+            return [ tokenlist ]
+        else:
+            return tokenlist
+
+
+class Suppress(TokenConverter):
+    """
+    Converter for ignoring the results of a parsed expression.
+
+    Example::
+        source = "a, b, c,d"
+        wd = Word(alphas)
+        wd_list1 = wd + ZeroOrMore(',' + wd)
+        print(wd_list1.parseString(source))
+
+        # often, delimiters that are useful during parsing are just in the
+        # way afterward - use Suppress to keep them out of the parsed output
+        wd_list2 = wd + ZeroOrMore(Suppress(',') + wd)
+        print(wd_list2.parseString(source))
+    prints::
+        ['a', ',', 'b', ',', 'c', ',', 'd']
+        ['a', 'b', 'c', 'd']
+    (See also L{delimitedList}.)
+    """
+    def postParse( self, instring, loc, tokenlist ):
+        return []
+
+    def suppress( self ):
+        return self
+
+
+class OnlyOnce(object):
+    """
+    Wrapper for parse actions, to ensure they are only called once.
+    """
+    def __init__(self, methodCall):
+        self.callable = _trim_arity(methodCall)
+        self.called = False
+    def __call__(self,s,l,t):
+        if not self.called:
+            results = self.callable(s,l,t)
+            self.called = True
+            return results
+        raise ParseException(s,l,"")
+    def reset(self):
+        self.called = False
+
+def traceParseAction(f):
+    """
+    Decorator for debugging parse actions. 
+    
+    When the parse action is called, this decorator will print C{">> entering I{method-name}(line:I{current_source_line}, I{parse_location}, I{matched_tokens})".}
+    When the parse action completes, the decorator will print C{"<<"} followed by the returned value, or any exception that the parse action raised.
+
+    Example::
+        wd = Word(alphas)
+
+        @traceParseAction
+        def remove_duplicate_chars(tokens):
+            return ''.join(sorted(set(''.join(tokens))))
+
+        wds = OneOrMore(wd).setParseAction(remove_duplicate_chars)
+        print(wds.parseString("slkdjs sld sldd sdlf sdljf"))
+    prints::
+        >>entering remove_duplicate_chars(line: 'slkdjs sld sldd sdlf sdljf', 0, (['slkdjs', 'sld', 'sldd', 'sdlf', 'sdljf'], {}))
+        <3:
+            thisFunc = paArgs[0].__class__.__name__ + '.' + thisFunc
+        sys.stderr.write( ">>entering %s(line: '%s', %d, %r)\n" % (thisFunc,line(l,s),l,t) )
+        try:
+            ret = f(*paArgs)
+        except Exception as exc:
+            sys.stderr.write( "< ['aa', 'bb', 'cc']
+        delimitedList(Word(hexnums), delim=':', combine=True).parseString("AA:BB:CC:DD:EE") # -> ['AA:BB:CC:DD:EE']
+    """
+    dlName = _ustr(expr)+" ["+_ustr(delim)+" "+_ustr(expr)+"]..."
+    if combine:
+        return Combine( expr + ZeroOrMore( delim + expr ) ).setName(dlName)
+    else:
+        return ( expr + ZeroOrMore( Suppress( delim ) + expr ) ).setName(dlName)
+
+def countedArray( expr, intExpr=None ):
+    """
+    Helper to define a counted list of expressions.
+    This helper defines a pattern of the form::
+        integer expr expr expr...
+    where the leading integer tells how many expr expressions follow.
+    The matched tokens returns the array of expr tokens as a list - the leading count token is suppressed.
+    
+    If C{intExpr} is specified, it should be a pyparsing expression that produces an integer value.
+
+    Example::
+        countedArray(Word(alphas)).parseString('2 ab cd ef')  # -> ['ab', 'cd']
+
+        # in this parser, the leading integer value is given in binary,
+        # '10' indicating that 2 values are in the array
+        binaryConstant = Word('01').setParseAction(lambda t: int(t[0], 2))
+        countedArray(Word(alphas), intExpr=binaryConstant).parseString('10 ab cd ef')  # -> ['ab', 'cd']
+    """
+    arrayExpr = Forward()
+    def countFieldParseAction(s,l,t):
+        n = t[0]
+        arrayExpr << (n and Group(And([expr]*n)) or Group(empty))
+        return []
+    if intExpr is None:
+        intExpr = Word(nums).setParseAction(lambda t:int(t[0]))
+    else:
+        intExpr = intExpr.copy()
+    intExpr.setName("arrayLen")
+    intExpr.addParseAction(countFieldParseAction, callDuringTry=True)
+    return ( intExpr + arrayExpr ).setName('(len) ' + _ustr(expr) + '...')
+
+def _flatten(L):
+    ret = []
+    for i in L:
+        if isinstance(i,list):
+            ret.extend(_flatten(i))
+        else:
+            ret.append(i)
+    return ret
+
+def matchPreviousLiteral(expr):
+    """
+    Helper to define an expression that is indirectly defined from
+    the tokens matched in a previous expression, that is, it looks
+    for a 'repeat' of a previous expression.  For example::
+        first = Word(nums)
+        second = matchPreviousLiteral(first)
+        matchExpr = first + ":" + second
+    will match C{"1:1"}, but not C{"1:2"}.  Because this matches a
+    previous literal, will also match the leading C{"1:1"} in C{"1:10"}.
+    If this is not desired, use C{matchPreviousExpr}.
+    Do I{not} use with packrat parsing enabled.
+    """
+    rep = Forward()
+    def copyTokenToRepeater(s,l,t):
+        if t:
+            if len(t) == 1:
+                rep << t[0]
+            else:
+                # flatten t tokens
+                tflat = _flatten(t.asList())
+                rep << And(Literal(tt) for tt in tflat)
+        else:
+            rep << Empty()
+    expr.addParseAction(copyTokenToRepeater, callDuringTry=True)
+    rep.setName('(prev) ' + _ustr(expr))
+    return rep
+
+def matchPreviousExpr(expr):
+    """
+    Helper to define an expression that is indirectly defined from
+    the tokens matched in a previous expression, that is, it looks
+    for a 'repeat' of a previous expression.  For example::
+        first = Word(nums)
+        second = matchPreviousExpr(first)
+        matchExpr = first + ":" + second
+    will match C{"1:1"}, but not C{"1:2"}.  Because this matches by
+    expressions, will I{not} match the leading C{"1:1"} in C{"1:10"};
+    the expressions are evaluated first, and then compared, so
+    C{"1"} is compared with C{"10"}.
+    Do I{not} use with packrat parsing enabled.
+    """
+    rep = Forward()
+    e2 = expr.copy()
+    rep <<= e2
+    def copyTokenToRepeater(s,l,t):
+        matchTokens = _flatten(t.asList())
+        def mustMatchTheseTokens(s,l,t):
+            theseTokens = _flatten(t.asList())
+            if  theseTokens != matchTokens:
+                raise ParseException("",0,"")
+        rep.setParseAction( mustMatchTheseTokens, callDuringTry=True )
+    expr.addParseAction(copyTokenToRepeater, callDuringTry=True)
+    rep.setName('(prev) ' + _ustr(expr))
+    return rep
+
+def _escapeRegexRangeChars(s):
+    #~  escape these chars: ^-]
+    for c in r"\^-]":
+        s = s.replace(c,_bslash+c)
+    s = s.replace("\n",r"\n")
+    s = s.replace("\t",r"\t")
+    return _ustr(s)
+
+def oneOf( strs, caseless=False, useRegex=True ):
+    """
+    Helper to quickly define a set of alternative Literals, and makes sure to do
+    longest-first testing when there is a conflict, regardless of the input order,
+    but returns a C{L{MatchFirst}} for best performance.
+
+    Parameters:
+     - strs - a string of space-delimited literals, or a collection of string literals
+     - caseless - (default=C{False}) - treat all literals as caseless
+     - useRegex - (default=C{True}) - as an optimization, will generate a Regex
+          object; otherwise, will generate a C{MatchFirst} object (if C{caseless=True}, or
+          if creating a C{Regex} raises an exception)
+
+    Example::
+        comp_oper = oneOf("< = > <= >= !=")
+        var = Word(alphas)
+        number = Word(nums)
+        term = var | number
+        comparison_expr = term + comp_oper + term
+        print(comparison_expr.searchString("B = 12  AA=23 B<=AA AA>12"))
+    prints::
+        [['B', '=', '12'], ['AA', '=', '23'], ['B', '<=', 'AA'], ['AA', '>', '12']]
+    """
+    if caseless:
+        isequal = ( lambda a,b: a.upper() == b.upper() )
+        masks = ( lambda a,b: b.upper().startswith(a.upper()) )
+        parseElementClass = CaselessLiteral
+    else:
+        isequal = ( lambda a,b: a == b )
+        masks = ( lambda a,b: b.startswith(a) )
+        parseElementClass = Literal
+
+    symbols = []
+    if isinstance(strs,basestring):
+        symbols = strs.split()
+    elif isinstance(strs, Iterable):
+        symbols = list(strs)
+    else:
+        warnings.warn("Invalid argument to oneOf, expected string or iterable",
+                SyntaxWarning, stacklevel=2)
+    if not symbols:
+        return NoMatch()
+
+    i = 0
+    while i < len(symbols)-1:
+        cur = symbols[i]
+        for j,other in enumerate(symbols[i+1:]):
+            if ( isequal(other, cur) ):
+                del symbols[i+j+1]
+                break
+            elif ( masks(cur, other) ):
+                del symbols[i+j+1]
+                symbols.insert(i,other)
+                cur = other
+                break
+        else:
+            i += 1
+
+    if not caseless and useRegex:
+        #~ print (strs,"->", "|".join( [ _escapeRegexChars(sym) for sym in symbols] ))
+        try:
+            if len(symbols)==len("".join(symbols)):
+                return Regex( "[%s]" % "".join(_escapeRegexRangeChars(sym) for sym in symbols) ).setName(' | '.join(symbols))
+            else:
+                return Regex( "|".join(re.escape(sym) for sym in symbols) ).setName(' | '.join(symbols))
+        except Exception:
+            warnings.warn("Exception creating Regex for oneOf, building MatchFirst",
+                    SyntaxWarning, stacklevel=2)
+
+
+    # last resort, just use MatchFirst
+    return MatchFirst(parseElementClass(sym) for sym in symbols).setName(' | '.join(symbols))
+
+def dictOf( key, value ):
+    """
+    Helper to easily and clearly define a dictionary by specifying the respective patterns
+    for the key and value.  Takes care of defining the C{L{Dict}}, C{L{ZeroOrMore}}, and C{L{Group}} tokens
+    in the proper order.  The key pattern can include delimiting markers or punctuation,
+    as long as they are suppressed, thereby leaving the significant key text.  The value
+    pattern can include named results, so that the C{Dict} results can include named token
+    fields.
+
+    Example::
+        text = "shape: SQUARE posn: upper left color: light blue texture: burlap"
+        attr_expr = (label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join))
+        print(OneOrMore(attr_expr).parseString(text).dump())
+        
+        attr_label = label
+        attr_value = Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)
+
+        # similar to Dict, but simpler call format
+        result = dictOf(attr_label, attr_value).parseString(text)
+        print(result.dump())
+        print(result['shape'])
+        print(result.shape)  # object attribute access works too
+        print(result.asDict())
+    prints::
+        [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'light blue'], ['texture', 'burlap']]
+        - color: light blue
+        - posn: upper left
+        - shape: SQUARE
+        - texture: burlap
+        SQUARE
+        SQUARE
+        {'color': 'light blue', 'shape': 'SQUARE', 'posn': 'upper left', 'texture': 'burlap'}
+    """
+    return Dict( ZeroOrMore( Group ( key + value ) ) )
+
+def originalTextFor(expr, asString=True):
+    """
+    Helper to return the original, untokenized text for a given expression.  Useful to
+    restore the parsed fields of an HTML start tag into the raw tag text itself, or to
+    revert separate tokens with intervening whitespace back to the original matching
+    input text. By default, returns astring containing the original parsed text.  
+       
+    If the optional C{asString} argument is passed as C{False}, then the return value is a 
+    C{L{ParseResults}} containing any results names that were originally matched, and a 
+    single token containing the original matched text from the input string.  So if 
+    the expression passed to C{L{originalTextFor}} contains expressions with defined
+    results names, you must set C{asString} to C{False} if you want to preserve those
+    results name values.
+
+    Example::
+        src = "this is test  bold text  normal text "
+        for tag in ("b","i"):
+            opener,closer = makeHTMLTags(tag)
+            patt = originalTextFor(opener + SkipTo(closer) + closer)
+            print(patt.searchString(src)[0])
+    prints::
+        [' bold text ']
+        ['text']
+    """
+    locMarker = Empty().setParseAction(lambda s,loc,t: loc)
+    endlocMarker = locMarker.copy()
+    endlocMarker.callPreparse = False
+    matchExpr = locMarker("_original_start") + expr + endlocMarker("_original_end")
+    if asString:
+        extractText = lambda s,l,t: s[t._original_start:t._original_end]
+    else:
+        def extractText(s,l,t):
+            t[:] = [s[t.pop('_original_start'):t.pop('_original_end')]]
+    matchExpr.setParseAction(extractText)
+    matchExpr.ignoreExprs = expr.ignoreExprs
+    return matchExpr
+
+def ungroup(expr): 
+    """
+    Helper to undo pyparsing's default grouping of And expressions, even
+    if all but one are non-empty.
+    """
+    return TokenConverter(expr).setParseAction(lambda t:t[0])
+
+def locatedExpr(expr):
+    """
+    Helper to decorate a returned token with its starting and ending locations in the input string.
+    This helper adds the following results names:
+     - locn_start = location where matched expression begins
+     - locn_end = location where matched expression ends
+     - value = the actual parsed results
+
+    Be careful if the input text contains C{} characters, you may want to call
+    C{L{ParserElement.parseWithTabs}}
+
+    Example::
+        wd = Word(alphas)
+        for match in locatedExpr(wd).searchString("ljsdf123lksdjjf123lkkjj1222"):
+            print(match)
+    prints::
+        [[0, 'ljsdf', 5]]
+        [[8, 'lksdjjf', 15]]
+        [[18, 'lkkjj', 23]]
+    """
+    locator = Empty().setParseAction(lambda s,l,t: l)
+    return Group(locator("locn_start") + expr("value") + locator.copy().leaveWhitespace()("locn_end"))
+
+
+# convenience constants for positional expressions
+empty       = Empty().setName("empty")
+lineStart   = LineStart().setName("lineStart")
+lineEnd     = LineEnd().setName("lineEnd")
+stringStart = StringStart().setName("stringStart")
+stringEnd   = StringEnd().setName("stringEnd")
+
+_escapedPunc = Word( _bslash, r"\[]-*.$+^?()~ ", exact=2 ).setParseAction(lambda s,l,t:t[0][1])
+_escapedHexChar = Regex(r"\\0?[xX][0-9a-fA-F]+").setParseAction(lambda s,l,t:unichr(int(t[0].lstrip(r'\0x'),16)))
+_escapedOctChar = Regex(r"\\0[0-7]+").setParseAction(lambda s,l,t:unichr(int(t[0][1:],8)))
+_singleChar = _escapedPunc | _escapedHexChar | _escapedOctChar | CharsNotIn(r'\]', exact=1)
+_charRange = Group(_singleChar + Suppress("-") + _singleChar)
+_reBracketExpr = Literal("[") + Optional("^").setResultsName("negate") + Group( OneOrMore( _charRange | _singleChar ) ).setResultsName("body") + "]"
+
+def srange(s):
+    r"""
+    Helper to easily define string ranges for use in Word construction.  Borrows
+    syntax from regexp '[]' string range definitions::
+        srange("[0-9]")   -> "0123456789"
+        srange("[a-z]")   -> "abcdefghijklmnopqrstuvwxyz"
+        srange("[a-z$_]") -> "abcdefghijklmnopqrstuvwxyz$_"
+    The input string must be enclosed in []'s, and the returned string is the expanded
+    character set joined into a single string.
+    The values enclosed in the []'s may be:
+     - a single character
+     - an escaped character with a leading backslash (such as C{\-} or C{\]})
+     - an escaped hex character with a leading C{'\x'} (C{\x21}, which is a C{'!'} character) 
+         (C{\0x##} is also supported for backwards compatibility) 
+     - an escaped octal character with a leading C{'\0'} (C{\041}, which is a C{'!'} character)
+     - a range of any of the above, separated by a dash (C{'a-z'}, etc.)
+     - any combination of the above (C{'aeiouy'}, C{'a-zA-Z0-9_$'}, etc.)
+    """
+    _expanded = lambda p: p if not isinstance(p,ParseResults) else ''.join(unichr(c) for c in range(ord(p[0]),ord(p[1])+1))
+    try:
+        return "".join(_expanded(part) for part in _reBracketExpr.parseString(s).body)
+    except Exception:
+        return ""
+
+def matchOnlyAtCol(n):
+    """
+    Helper method for defining parse actions that require matching at a specific
+    column in the input text.
+    """
+    def verifyCol(strg,locn,toks):
+        if col(locn,strg) != n:
+            raise ParseException(strg,locn,"matched token not at column %d" % n)
+    return verifyCol
+
+def replaceWith(replStr):
+    """
+    Helper method for common parse actions that simply return a literal value.  Especially
+    useful when used with C{L{transformString}()}.
+
+    Example::
+        num = Word(nums).setParseAction(lambda toks: int(toks[0]))
+        na = oneOf("N/A NA").setParseAction(replaceWith(math.nan))
+        term = na | num
+        
+        OneOrMore(term).parseString("324 234 N/A 234") # -> [324, 234, nan, 234]
+    """
+    return lambda s,l,t: [replStr]
+
+def removeQuotes(s,l,t):
+    """
+    Helper parse action for removing quotation marks from parsed quoted strings.
+
+    Example::
+        # by default, quotation marks are included in parsed results
+        quotedString.parseString("'Now is the Winter of our Discontent'") # -> ["'Now is the Winter of our Discontent'"]
+
+        # use removeQuotes to strip quotation marks from parsed results
+        quotedString.setParseAction(removeQuotes)
+        quotedString.parseString("'Now is the Winter of our Discontent'") # -> ["Now is the Winter of our Discontent"]
+    """
+    return t[0][1:-1]
+
+def tokenMap(func, *args):
+    """
+    Helper to define a parse action by mapping a function to all elements of a ParseResults list.If any additional 
+    args are passed, they are forwarded to the given function as additional arguments after
+    the token, as in C{hex_integer = Word(hexnums).setParseAction(tokenMap(int, 16))}, which will convert the
+    parsed data to an integer using base 16.
+
+    Example (compare the last to example in L{ParserElement.transformString}::
+        hex_ints = OneOrMore(Word(hexnums)).setParseAction(tokenMap(int, 16))
+        hex_ints.runTests('''
+            00 11 22 aa FF 0a 0d 1a
+            ''')
+        
+        upperword = Word(alphas).setParseAction(tokenMap(str.upper))
+        OneOrMore(upperword).runTests('''
+            my kingdom for a horse
+            ''')
+
+        wd = Word(alphas).setParseAction(tokenMap(str.title))
+        OneOrMore(wd).setParseAction(' '.join).runTests('''
+            now is the winter of our discontent made glorious summer by this sun of york
+            ''')
+    prints::
+        00 11 22 aa FF 0a 0d 1a
+        [0, 17, 34, 170, 255, 10, 13, 26]
+
+        my kingdom for a horse
+        ['MY', 'KINGDOM', 'FOR', 'A', 'HORSE']
+
+        now is the winter of our discontent made glorious summer by this sun of york
+        ['Now Is The Winter Of Our Discontent Made Glorious Summer By This Sun Of York']
+    """
+    def pa(s,l,t):
+        return [func(tokn, *args) for tokn in t]
+
+    try:
+        func_name = getattr(func, '__name__', 
+                            getattr(func, '__class__').__name__)
+    except Exception:
+        func_name = str(func)
+    pa.__name__ = func_name
+
+    return pa
+
+upcaseTokens = tokenMap(lambda t: _ustr(t).upper())
+"""(Deprecated) Helper parse action to convert tokens to upper case. Deprecated in favor of L{pyparsing_common.upcaseTokens}"""
+
+downcaseTokens = tokenMap(lambda t: _ustr(t).lower())
+"""(Deprecated) Helper parse action to convert tokens to lower case. Deprecated in favor of L{pyparsing_common.downcaseTokens}"""
+    
+def _makeTags(tagStr, xml):
+    """Internal helper to construct opening and closing tag expressions, given a tag name"""
+    if isinstance(tagStr,basestring):
+        resname = tagStr
+        tagStr = Keyword(tagStr, caseless=not xml)
+    else:
+        resname = tagStr.name
+
+    tagAttrName = Word(alphas,alphanums+"_-:")
+    if (xml):
+        tagAttrValue = dblQuotedString.copy().setParseAction( removeQuotes )
+        openTag = Suppress("<") + tagStr("tag") + \
+                Dict(ZeroOrMore(Group( tagAttrName + Suppress("=") + tagAttrValue ))) + \
+                Optional("/",default=[False]).setResultsName("empty").setParseAction(lambda s,l,t:t[0]=='/') + Suppress(">")
+    else:
+        printablesLessRAbrack = "".join(c for c in printables if c not in ">")
+        tagAttrValue = quotedString.copy().setParseAction( removeQuotes ) | Word(printablesLessRAbrack)
+        openTag = Suppress("<") + tagStr("tag") + \
+                Dict(ZeroOrMore(Group( tagAttrName.setParseAction(downcaseTokens) + \
+                Optional( Suppress("=") + tagAttrValue ) ))) + \
+                Optional("/",default=[False]).setResultsName("empty").setParseAction(lambda s,l,t:t[0]=='/') + Suppress(">")
+    closeTag = Combine(_L("")
+
+    openTag = openTag.setResultsName("start"+"".join(resname.replace(":"," ").title().split())).setName("<%s>" % resname)
+    closeTag = closeTag.setResultsName("end"+"".join(resname.replace(":"," ").title().split())).setName("" % resname)
+    openTag.tag = resname
+    closeTag.tag = resname
+    return openTag, closeTag
+
+def makeHTMLTags(tagStr):
+    """
+    Helper to construct opening and closing tag expressions for HTML, given a tag name. Matches
+    tags in either upper or lower case, attributes with namespaces and with quoted or unquoted values.
+
+    Example::
+        text = 'More info at the pyparsing wiki page'
+        # makeHTMLTags returns pyparsing expressions for the opening and closing tags as a 2-tuple
+        a,a_end = makeHTMLTags("A")
+        link_expr = a + SkipTo(a_end)("link_text") + a_end
+        
+        for link in link_expr.searchString(text):
+            # attributes in the  tag (like "href" shown here) are also accessible as named results
+            print(link.link_text, '->', link.href)
+    prints::
+        pyparsing -> http://pyparsing.wikispaces.com
+    """
+    return _makeTags( tagStr, False )
+
+def makeXMLTags(tagStr):
+    """
+    Helper to construct opening and closing tag expressions for XML, given a tag name. Matches
+    tags only in the given upper/lower case.
+
+    Example: similar to L{makeHTMLTags}
+    """
+    return _makeTags( tagStr, True )
+
+def withAttribute(*args,**attrDict):
+    """
+    Helper to create a validating parse action to be used with start tags created
+    with C{L{makeXMLTags}} or C{L{makeHTMLTags}}. Use C{withAttribute} to qualify a starting tag
+    with a required attribute value, to avoid false matches on common tags such as
+    C{} or C{
}. + + Call C{withAttribute} with a series of attribute names and values. Specify the list + of filter attributes names and values as: + - keyword arguments, as in C{(align="right")}, or + - as an explicit dict with C{**} operator, when an attribute name is also a Python + reserved word, as in C{**{"class":"Customer", "align":"right"}} + - a list of name-value tuples, as in ( ("ns1:class", "Customer"), ("ns2:align","right") ) + For attribute names with a namespace prefix, you must use the second form. Attribute + names are matched insensitive to upper/lower case. + + If just testing for C{class} (with or without a namespace), use C{L{withClass}}. + + To verify that the attribute exists, but without specifying a value, pass + C{withAttribute.ANY_VALUE} as the value. + + Example:: + html = ''' +
+ Some text +
1 4 0 1 0
+
1,3 2,3 1,1
+
this has no type
+
+ + ''' + div,div_end = makeHTMLTags("div") + + # only match div tag having a type attribute with value "grid" + div_grid = div().setParseAction(withAttribute(type="grid")) + grid_expr = div_grid + SkipTo(div | div_end)("body") + for grid_header in grid_expr.searchString(html): + print(grid_header.body) + + # construct a match with any div tag having a type attribute, regardless of the value + div_any_type = div().setParseAction(withAttribute(type=withAttribute.ANY_VALUE)) + div_expr = div_any_type + SkipTo(div | div_end)("body") + for div_header in div_expr.searchString(html): + print(div_header.body) + prints:: + 1 4 0 1 0 + + 1 4 0 1 0 + 1,3 2,3 1,1 + """ + if args: + attrs = args[:] + else: + attrs = attrDict.items() + attrs = [(k,v) for k,v in attrs] + def pa(s,l,tokens): + for attrName,attrValue in attrs: + if attrName not in tokens: + raise ParseException(s,l,"no matching attribute " + attrName) + if attrValue != withAttribute.ANY_VALUE and tokens[attrName] != attrValue: + raise ParseException(s,l,"attribute '%s' has value '%s', must be '%s'" % + (attrName, tokens[attrName], attrValue)) + return pa +withAttribute.ANY_VALUE = object() + +def withClass(classname, namespace=''): + """ + Simplified version of C{L{withAttribute}} when matching on a div class - made + difficult because C{class} is a reserved word in Python. + + Example:: + html = ''' +
+ Some text +
1 4 0 1 0
+
1,3 2,3 1,1
+
this <div> has no class
+
+ + ''' + div,div_end = makeHTMLTags("div") + div_grid = div().setParseAction(withClass("grid")) + + grid_expr = div_grid + SkipTo(div | div_end)("body") + for grid_header in grid_expr.searchString(html): + print(grid_header.body) + + div_any_type = div().setParseAction(withClass(withAttribute.ANY_VALUE)) + div_expr = div_any_type + SkipTo(div | div_end)("body") + for div_header in div_expr.searchString(html): + print(div_header.body) + prints:: + 1 4 0 1 0 + + 1 4 0 1 0 + 1,3 2,3 1,1 + """ + classattr = "%s:class" % namespace if namespace else "class" + return withAttribute(**{classattr : classname}) + +opAssoc = _Constants() +opAssoc.LEFT = object() +opAssoc.RIGHT = object() + +def infixNotation( baseExpr, opList, lpar=Suppress('('), rpar=Suppress(')') ): + """ + Helper method for constructing grammars of expressions made up of + operators working in a precedence hierarchy. Operators may be unary or + binary, left- or right-associative. Parse actions can also be attached + to operator expressions. The generated parser will also recognize the use + of parentheses to override operator precedences (see example below). + + Note: if you define a deep operator list, you may see performance issues + when using infixNotation. See L{ParserElement.enablePackrat} for a + mechanism to potentially improve your parser performance. + + Parameters: + - baseExpr - expression representing the most basic element for the nested + - opList - list of tuples, one for each operator precedence level in the + expression grammar; each tuple is of the form + (opExpr, numTerms, rightLeftAssoc, parseAction), where: + - opExpr is the pyparsing expression for the operator; + may also be a string, which will be converted to a Literal; + if numTerms is 3, opExpr is a tuple of two expressions, for the + two operators separating the 3 terms + - numTerms is the number of terms for this operator (must + be 1, 2, or 3) + - rightLeftAssoc is the indicator whether the operator is + right or left associative, using the pyparsing-defined + constants C{opAssoc.RIGHT} and C{opAssoc.LEFT}. + - parseAction is the parse action to be associated with + expressions matching this operator expression (the + parse action tuple member may be omitted); if the parse action + is passed a tuple or list of functions, this is equivalent to + calling C{setParseAction(*fn)} (L{ParserElement.setParseAction}) + - lpar - expression for matching left-parentheses (default=C{Suppress('(')}) + - rpar - expression for matching right-parentheses (default=C{Suppress(')')}) + + Example:: + # simple example of four-function arithmetic with ints and variable names + integer = pyparsing_common.signed_integer + varname = pyparsing_common.identifier + + arith_expr = infixNotation(integer | varname, + [ + ('-', 1, opAssoc.RIGHT), + (oneOf('* /'), 2, opAssoc.LEFT), + (oneOf('+ -'), 2, opAssoc.LEFT), + ]) + + arith_expr.runTests(''' + 5+3*6 + (5+3)*6 + -2--11 + ''', fullDump=False) + prints:: + 5+3*6 + [[5, '+', [3, '*', 6]]] + + (5+3)*6 + [[[5, '+', 3], '*', 6]] + + -2--11 + [[['-', 2], '-', ['-', 11]]] + """ + ret = Forward() + lastExpr = baseExpr | ( lpar + ret + rpar ) + for i,operDef in enumerate(opList): + opExpr,arity,rightLeftAssoc,pa = (operDef + (None,))[:4] + termName = "%s term" % opExpr if arity < 3 else "%s%s term" % opExpr + if arity == 3: + if opExpr is None or len(opExpr) != 2: + raise ValueError("if numterms=3, opExpr must be a tuple or list of two expressions") + opExpr1, opExpr2 = opExpr + thisExpr = Forward().setName(termName) + if rightLeftAssoc == opAssoc.LEFT: + if arity == 1: + matchExpr = FollowedBy(lastExpr + opExpr) + Group( lastExpr + OneOrMore( opExpr ) ) + elif arity == 2: + if opExpr is not None: + matchExpr = FollowedBy(lastExpr + opExpr + lastExpr) + Group( lastExpr + OneOrMore( opExpr + lastExpr ) ) + else: + matchExpr = FollowedBy(lastExpr+lastExpr) + Group( lastExpr + OneOrMore(lastExpr) ) + elif arity == 3: + matchExpr = FollowedBy(lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr) + \ + Group( lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr ) + else: + raise ValueError("operator must be unary (1), binary (2), or ternary (3)") + elif rightLeftAssoc == opAssoc.RIGHT: + if arity == 1: + # try to avoid LR with this extra test + if not isinstance(opExpr, Optional): + opExpr = Optional(opExpr) + matchExpr = FollowedBy(opExpr.expr + thisExpr) + Group( opExpr + thisExpr ) + elif arity == 2: + if opExpr is not None: + matchExpr = FollowedBy(lastExpr + opExpr + thisExpr) + Group( lastExpr + OneOrMore( opExpr + thisExpr ) ) + else: + matchExpr = FollowedBy(lastExpr + thisExpr) + Group( lastExpr + OneOrMore( thisExpr ) ) + elif arity == 3: + matchExpr = FollowedBy(lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr) + \ + Group( lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr ) + else: + raise ValueError("operator must be unary (1), binary (2), or ternary (3)") + else: + raise ValueError("operator must indicate right or left associativity") + if pa: + if isinstance(pa, (tuple, list)): + matchExpr.setParseAction(*pa) + else: + matchExpr.setParseAction(pa) + thisExpr <<= ( matchExpr.setName(termName) | lastExpr ) + lastExpr = thisExpr + ret <<= lastExpr + return ret + +operatorPrecedence = infixNotation +"""(Deprecated) Former name of C{L{infixNotation}}, will be dropped in a future release.""" + +dblQuotedString = Combine(Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*')+'"').setName("string enclosed in double quotes") +sglQuotedString = Combine(Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*")+"'").setName("string enclosed in single quotes") +quotedString = Combine(Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*')+'"'| + Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*")+"'").setName("quotedString using single or double quotes") +unicodeString = Combine(_L('u') + quotedString.copy()).setName("unicode string literal") + +def nestedExpr(opener="(", closer=")", content=None, ignoreExpr=quotedString.copy()): + """ + Helper method for defining nested lists enclosed in opening and closing + delimiters ("(" and ")" are the default). + + Parameters: + - opener - opening character for a nested list (default=C{"("}); can also be a pyparsing expression + - closer - closing character for a nested list (default=C{")"}); can also be a pyparsing expression + - content - expression for items within the nested lists (default=C{None}) + - ignoreExpr - expression for ignoring opening and closing delimiters (default=C{quotedString}) + + If an expression is not provided for the content argument, the nested + expression will capture all whitespace-delimited content between delimiters + as a list of separate values. + + Use the C{ignoreExpr} argument to define expressions that may contain + opening or closing characters that should not be treated as opening + or closing characters for nesting, such as quotedString or a comment + expression. Specify multiple expressions using an C{L{Or}} or C{L{MatchFirst}}. + The default is L{quotedString}, but if no expressions are to be ignored, + then pass C{None} for this argument. + + Example:: + data_type = oneOf("void int short long char float double") + decl_data_type = Combine(data_type + Optional(Word('*'))) + ident = Word(alphas+'_', alphanums+'_') + number = pyparsing_common.number + arg = Group(decl_data_type + ident) + LPAR,RPAR = map(Suppress, "()") + + code_body = nestedExpr('{', '}', ignoreExpr=(quotedString | cStyleComment)) + + c_function = (decl_data_type("type") + + ident("name") + + LPAR + Optional(delimitedList(arg), [])("args") + RPAR + + code_body("body")) + c_function.ignore(cStyleComment) + + source_code = ''' + int is_odd(int x) { + return (x%2); + } + + int dec_to_hex(char hchar) { + if (hchar >= '0' && hchar <= '9') { + return (ord(hchar)-ord('0')); + } else { + return (10+ord(hchar)-ord('A')); + } + } + ''' + for func in c_function.searchString(source_code): + print("%(name)s (%(type)s) args: %(args)s" % func) + + prints:: + is_odd (int) args: [['int', 'x']] + dec_to_hex (int) args: [['char', 'hchar']] + """ + if opener == closer: + raise ValueError("opening and closing strings cannot be the same") + if content is None: + if isinstance(opener,basestring) and isinstance(closer,basestring): + if len(opener) == 1 and len(closer)==1: + if ignoreExpr is not None: + content = (Combine(OneOrMore(~ignoreExpr + + CharsNotIn(opener+closer+ParserElement.DEFAULT_WHITE_CHARS,exact=1)) + ).setParseAction(lambda t:t[0].strip())) + else: + content = (empty.copy()+CharsNotIn(opener+closer+ParserElement.DEFAULT_WHITE_CHARS + ).setParseAction(lambda t:t[0].strip())) + else: + if ignoreExpr is not None: + content = (Combine(OneOrMore(~ignoreExpr + + ~Literal(opener) + ~Literal(closer) + + CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS,exact=1)) + ).setParseAction(lambda t:t[0].strip())) + else: + content = (Combine(OneOrMore(~Literal(opener) + ~Literal(closer) + + CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS,exact=1)) + ).setParseAction(lambda t:t[0].strip())) + else: + raise ValueError("opening and closing arguments must be strings if no content expression is given") + ret = Forward() + if ignoreExpr is not None: + ret <<= Group( Suppress(opener) + ZeroOrMore( ignoreExpr | ret | content ) + Suppress(closer) ) + else: + ret <<= Group( Suppress(opener) + ZeroOrMore( ret | content ) + Suppress(closer) ) + ret.setName('nested %s%s expression' % (opener,closer)) + return ret + +def indentedBlock(blockStatementExpr, indentStack, indent=True): + """ + Helper method for defining space-delimited indentation blocks, such as + those used to define block statements in Python source code. + + Parameters: + - blockStatementExpr - expression defining syntax of statement that + is repeated within the indented block + - indentStack - list created by caller to manage indentation stack + (multiple statementWithIndentedBlock expressions within a single grammar + should share a common indentStack) + - indent - boolean indicating whether block must be indented beyond the + the current level; set to False for block of left-most statements + (default=C{True}) + + A valid block must contain at least one C{blockStatement}. + + Example:: + data = ''' + def A(z): + A1 + B = 100 + G = A2 + A2 + A3 + B + def BB(a,b,c): + BB1 + def BBA(): + bba1 + bba2 + bba3 + C + D + def spam(x,y): + def eggs(z): + pass + ''' + + + indentStack = [1] + stmt = Forward() + + identifier = Word(alphas, alphanums) + funcDecl = ("def" + identifier + Group( "(" + Optional( delimitedList(identifier) ) + ")" ) + ":") + func_body = indentedBlock(stmt, indentStack) + funcDef = Group( funcDecl + func_body ) + + rvalue = Forward() + funcCall = Group(identifier + "(" + Optional(delimitedList(rvalue)) + ")") + rvalue << (funcCall | identifier | Word(nums)) + assignment = Group(identifier + "=" + rvalue) + stmt << ( funcDef | assignment | identifier ) + + module_body = OneOrMore(stmt) + + parseTree = module_body.parseString(data) + parseTree.pprint() + prints:: + [['def', + 'A', + ['(', 'z', ')'], + ':', + [['A1'], [['B', '=', '100']], [['G', '=', 'A2']], ['A2'], ['A3']]], + 'B', + ['def', + 'BB', + ['(', 'a', 'b', 'c', ')'], + ':', + [['BB1'], [['def', 'BBA', ['(', ')'], ':', [['bba1'], ['bba2'], ['bba3']]]]]], + 'C', + 'D', + ['def', + 'spam', + ['(', 'x', 'y', ')'], + ':', + [[['def', 'eggs', ['(', 'z', ')'], ':', [['pass']]]]]]] + """ + def checkPeerIndent(s,l,t): + if l >= len(s): return + curCol = col(l,s) + if curCol != indentStack[-1]: + if curCol > indentStack[-1]: + raise ParseFatalException(s,l,"illegal nesting") + raise ParseException(s,l,"not a peer entry") + + def checkSubIndent(s,l,t): + curCol = col(l,s) + if curCol > indentStack[-1]: + indentStack.append( curCol ) + else: + raise ParseException(s,l,"not a subentry") + + def checkUnindent(s,l,t): + if l >= len(s): return + curCol = col(l,s) + if not(indentStack and curCol < indentStack[-1] and curCol <= indentStack[-2]): + raise ParseException(s,l,"not an unindent") + indentStack.pop() + + NL = OneOrMore(LineEnd().setWhitespaceChars("\t ").suppress()) + INDENT = (Empty() + Empty().setParseAction(checkSubIndent)).setName('INDENT') + PEER = Empty().setParseAction(checkPeerIndent).setName('') + UNDENT = Empty().setParseAction(checkUnindent).setName('UNINDENT') + if indent: + smExpr = Group( Optional(NL) + + #~ FollowedBy(blockStatementExpr) + + INDENT + (OneOrMore( PEER + Group(blockStatementExpr) + Optional(NL) )) + UNDENT) + else: + smExpr = Group( Optional(NL) + + (OneOrMore( PEER + Group(blockStatementExpr) + Optional(NL) )) ) + blockStatementExpr.ignore(_bslash + LineEnd()) + return smExpr.setName('indented block') + +alphas8bit = srange(r"[\0xc0-\0xd6\0xd8-\0xf6\0xf8-\0xff]") +punc8bit = srange(r"[\0xa1-\0xbf\0xd7\0xf7]") + +anyOpenTag,anyCloseTag = makeHTMLTags(Word(alphas,alphanums+"_:").setName('any tag')) +_htmlEntityMap = dict(zip("gt lt amp nbsp quot apos".split(),'><& "\'')) +commonHTMLEntity = Regex('&(?P' + '|'.join(_htmlEntityMap.keys()) +");").setName("common HTML entity") +def replaceHTMLEntity(t): + """Helper parser action to replace common HTML entities with their special characters""" + return _htmlEntityMap.get(t.entity) + +# it's easy to get these comment structures wrong - they're very common, so may as well make them available +cStyleComment = Combine(Regex(r"/\*(?:[^*]|\*(?!/))*") + '*/').setName("C style comment") +"Comment of the form C{/* ... */}" + +htmlComment = Regex(r"").setName("HTML comment") +"Comment of the form C{}" + +restOfLine = Regex(r".*").leaveWhitespace().setName("rest of line") +dblSlashComment = Regex(r"//(?:\\\n|[^\n])*").setName("// comment") +"Comment of the form C{// ... (to end of line)}" + +cppStyleComment = Combine(Regex(r"/\*(?:[^*]|\*(?!/))*") + '*/'| dblSlashComment).setName("C++ style comment") +"Comment of either form C{L{cStyleComment}} or C{L{dblSlashComment}}" + +javaStyleComment = cppStyleComment +"Same as C{L{cppStyleComment}}" + +pythonStyleComment = Regex(r"#.*").setName("Python style comment") +"Comment of the form C{# ... (to end of line)}" + +_commasepitem = Combine(OneOrMore(Word(printables, excludeChars=',') + + Optional( Word(" \t") + + ~Literal(",") + ~LineEnd() ) ) ).streamline().setName("commaItem") +commaSeparatedList = delimitedList( Optional( quotedString.copy() | _commasepitem, default="") ).setName("commaSeparatedList") +"""(Deprecated) Predefined expression of 1 or more printable words or quoted strings, separated by commas. + This expression is deprecated in favor of L{pyparsing_common.comma_separated_list}.""" + +# some other useful expressions - using lower-case class name since we are really using this as a namespace +class pyparsing_common: + """ + Here are some common low-level expressions that may be useful in jump-starting parser development: + - numeric forms (L{integers}, L{reals}, L{scientific notation}) + - common L{programming identifiers} + - network addresses (L{MAC}, L{IPv4}, L{IPv6}) + - ISO8601 L{dates} and L{datetime} + - L{UUID} + - L{comma-separated list} + Parse actions: + - C{L{convertToInteger}} + - C{L{convertToFloat}} + - C{L{convertToDate}} + - C{L{convertToDatetime}} + - C{L{stripHTMLTags}} + - C{L{upcaseTokens}} + - C{L{downcaseTokens}} + + Example:: + pyparsing_common.number.runTests(''' + # any int or real number, returned as the appropriate type + 100 + -100 + +100 + 3.14159 + 6.02e23 + 1e-12 + ''') + + pyparsing_common.fnumber.runTests(''' + # any int or real number, returned as float + 100 + -100 + +100 + 3.14159 + 6.02e23 + 1e-12 + ''') + + pyparsing_common.hex_integer.runTests(''' + # hex numbers + 100 + FF + ''') + + pyparsing_common.fraction.runTests(''' + # fractions + 1/2 + -3/4 + ''') + + pyparsing_common.mixed_integer.runTests(''' + # mixed fractions + 1 + 1/2 + -3/4 + 1-3/4 + ''') + + import uuid + pyparsing_common.uuid.setParseAction(tokenMap(uuid.UUID)) + pyparsing_common.uuid.runTests(''' + # uuid + 12345678-1234-5678-1234-567812345678 + ''') + prints:: + # any int or real number, returned as the appropriate type + 100 + [100] + + -100 + [-100] + + +100 + [100] + + 3.14159 + [3.14159] + + 6.02e23 + [6.02e+23] + + 1e-12 + [1e-12] + + # any int or real number, returned as float + 100 + [100.0] + + -100 + [-100.0] + + +100 + [100.0] + + 3.14159 + [3.14159] + + 6.02e23 + [6.02e+23] + + 1e-12 + [1e-12] + + # hex numbers + 100 + [256] + + FF + [255] + + # fractions + 1/2 + [0.5] + + -3/4 + [-0.75] + + # mixed fractions + 1 + [1] + + 1/2 + [0.5] + + -3/4 + [-0.75] + + 1-3/4 + [1.75] + + # uuid + 12345678-1234-5678-1234-567812345678 + [UUID('12345678-1234-5678-1234-567812345678')] + """ + + convertToInteger = tokenMap(int) + """ + Parse action for converting parsed integers to Python int + """ + + convertToFloat = tokenMap(float) + """ + Parse action for converting parsed numbers to Python float + """ + + integer = Word(nums).setName("integer").setParseAction(convertToInteger) + """expression that parses an unsigned integer, returns an int""" + + hex_integer = Word(hexnums).setName("hex integer").setParseAction(tokenMap(int,16)) + """expression that parses a hexadecimal integer, returns an int""" + + signed_integer = Regex(r'[+-]?\d+').setName("signed integer").setParseAction(convertToInteger) + """expression that parses an integer with optional leading sign, returns an int""" + + fraction = (signed_integer().setParseAction(convertToFloat) + '/' + signed_integer().setParseAction(convertToFloat)).setName("fraction") + """fractional expression of an integer divided by an integer, returns a float""" + fraction.addParseAction(lambda t: t[0]/t[-1]) + + mixed_integer = (fraction | signed_integer + Optional(Optional('-').suppress() + fraction)).setName("fraction or mixed integer-fraction") + """mixed integer of the form 'integer - fraction', with optional leading integer, returns float""" + mixed_integer.addParseAction(sum) + + real = Regex(r'[+-]?\d+\.\d*').setName("real number").setParseAction(convertToFloat) + """expression that parses a floating point number and returns a float""" + + sci_real = Regex(r'[+-]?\d+([eE][+-]?\d+|\.\d*([eE][+-]?\d+)?)').setName("real number with scientific notation").setParseAction(convertToFloat) + """expression that parses a floating point number with optional scientific notation and returns a float""" + + # streamlining this expression makes the docs nicer-looking + number = (sci_real | real | signed_integer).streamline() + """any numeric expression, returns the corresponding Python type""" + + fnumber = Regex(r'[+-]?\d+\.?\d*([eE][+-]?\d+)?').setName("fnumber").setParseAction(convertToFloat) + """any int or real number, returned as float""" + + identifier = Word(alphas+'_', alphanums+'_').setName("identifier") + """typical code identifier (leading alpha or '_', followed by 0 or more alphas, nums, or '_')""" + + ipv4_address = Regex(r'(25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})(\.(25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})){3}').setName("IPv4 address") + "IPv4 address (C{0.0.0.0 - 255.255.255.255})" + + _ipv6_part = Regex(r'[0-9a-fA-F]{1,4}').setName("hex_integer") + _full_ipv6_address = (_ipv6_part + (':' + _ipv6_part)*7).setName("full IPv6 address") + _short_ipv6_address = (Optional(_ipv6_part + (':' + _ipv6_part)*(0,6)) + "::" + Optional(_ipv6_part + (':' + _ipv6_part)*(0,6))).setName("short IPv6 address") + _short_ipv6_address.addCondition(lambda t: sum(1 for tt in t if pyparsing_common._ipv6_part.matches(tt)) < 8) + _mixed_ipv6_address = ("::ffff:" + ipv4_address).setName("mixed IPv6 address") + ipv6_address = Combine((_full_ipv6_address | _mixed_ipv6_address | _short_ipv6_address).setName("IPv6 address")).setName("IPv6 address") + "IPv6 address (long, short, or mixed form)" + + mac_address = Regex(r'[0-9a-fA-F]{2}([:.-])[0-9a-fA-F]{2}(?:\1[0-9a-fA-F]{2}){4}').setName("MAC address") + "MAC address xx:xx:xx:xx:xx (may also have '-' or '.' delimiters)" + + @staticmethod + def convertToDate(fmt="%Y-%m-%d"): + """ + Helper to create a parse action for converting parsed date string to Python datetime.date + + Params - + - fmt - format to be passed to datetime.strptime (default=C{"%Y-%m-%d"}) + + Example:: + date_expr = pyparsing_common.iso8601_date.copy() + date_expr.setParseAction(pyparsing_common.convertToDate()) + print(date_expr.parseString("1999-12-31")) + prints:: + [datetime.date(1999, 12, 31)] + """ + def cvt_fn(s,l,t): + try: + return datetime.strptime(t[0], fmt).date() + except ValueError as ve: + raise ParseException(s, l, str(ve)) + return cvt_fn + + @staticmethod + def convertToDatetime(fmt="%Y-%m-%dT%H:%M:%S.%f"): + """ + Helper to create a parse action for converting parsed datetime string to Python datetime.datetime + + Params - + - fmt - format to be passed to datetime.strptime (default=C{"%Y-%m-%dT%H:%M:%S.%f"}) + + Example:: + dt_expr = pyparsing_common.iso8601_datetime.copy() + dt_expr.setParseAction(pyparsing_common.convertToDatetime()) + print(dt_expr.parseString("1999-12-31T23:59:59.999")) + prints:: + [datetime.datetime(1999, 12, 31, 23, 59, 59, 999000)] + """ + def cvt_fn(s,l,t): + try: + return datetime.strptime(t[0], fmt) + except ValueError as ve: + raise ParseException(s, l, str(ve)) + return cvt_fn + + iso8601_date = Regex(r'(?P\d{4})(?:-(?P\d\d)(?:-(?P\d\d))?)?').setName("ISO8601 date") + "ISO8601 date (C{yyyy-mm-dd})" + + iso8601_datetime = Regex(r'(?P\d{4})-(?P\d\d)-(?P\d\d)[T ](?P\d\d):(?P\d\d)(:(?P\d\d(\.\d*)?)?)?(?PZ|[+-]\d\d:?\d\d)?').setName("ISO8601 datetime") + "ISO8601 datetime (C{yyyy-mm-ddThh:mm:ss.s(Z|+-00:00)}) - trailing seconds, milliseconds, and timezone optional; accepts separating C{'T'} or C{' '}" + + uuid = Regex(r'[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}').setName("UUID") + "UUID (C{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx})" + + _html_stripper = anyOpenTag.suppress() | anyCloseTag.suppress() + @staticmethod + def stripHTMLTags(s, l, tokens): + """ + Parse action to remove HTML tags from web page HTML source + + Example:: + # strip HTML links from normal text + text = 'More info at the
pyparsing wiki page' + td,td_end = makeHTMLTags("TD") + table_text = td + SkipTo(td_end).setParseAction(pyparsing_common.stripHTMLTags)("body") + td_end + + print(table_text.parseString(text).body) # -> 'More info at the pyparsing wiki page' + """ + return pyparsing_common._html_stripper.transformString(tokens[0]) + + _commasepitem = Combine(OneOrMore(~Literal(",") + ~LineEnd() + Word(printables, excludeChars=',') + + Optional( White(" \t") ) ) ).streamline().setName("commaItem") + comma_separated_list = delimitedList( Optional( quotedString.copy() | _commasepitem, default="") ).setName("comma separated list") + """Predefined expression of 1 or more printable words or quoted strings, separated by commas.""" + + upcaseTokens = staticmethod(tokenMap(lambda t: _ustr(t).upper())) + """Parse action to convert tokens to upper case.""" + + downcaseTokens = staticmethod(tokenMap(lambda t: _ustr(t).lower())) + """Parse action to convert tokens to lower case.""" + + +if __name__ == "__main__": + + selectToken = CaselessLiteral("select") + fromToken = CaselessLiteral("from") + + ident = Word(alphas, alphanums + "_$") + + columnName = delimitedList(ident, ".", combine=True).setParseAction(upcaseTokens) + columnNameList = Group(delimitedList(columnName)).setName("columns") + columnSpec = ('*' | columnNameList) + + tableName = delimitedList(ident, ".", combine=True).setParseAction(upcaseTokens) + tableNameList = Group(delimitedList(tableName)).setName("tables") + + simpleSQL = selectToken("command") + columnSpec("columns") + fromToken + tableNameList("tables") + + # demo runTests method, including embedded comments in test string + simpleSQL.runTests(""" + # '*' as column list and dotted table name + select * from SYS.XYZZY + + # caseless match on "SELECT", and casts back to "select" + SELECT * from XYZZY, ABC + + # list of column names, and mixed case SELECT keyword + Select AA,BB,CC from Sys.dual + + # multiple tables + Select A, B, C from Sys.dual, Table2 + + # invalid SELECT keyword - should fail + Xelect A, B, C from Sys.dual + + # incomplete command - should fail + Select + + # invalid column name - should fail + Select ^^^ frox Sys.dual + + """) + + pyparsing_common.number.runTests(""" + 100 + -100 + +100 + 3.14159 + 6.02e23 + 1e-12 + """) + + # any int or real number, returned as float + pyparsing_common.fnumber.runTests(""" + 100 + -100 + +100 + 3.14159 + 6.02e23 + 1e-12 + """) + + pyparsing_common.hex_integer.runTests(""" + 100 + FF + """) + + import uuid + pyparsing_common.uuid.setParseAction(tokenMap(uuid.UUID)) + pyparsing_common.uuid.runTests(""" + 12345678-1234-5678-1234-567812345678 + """) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/six.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/six.py new file mode 100644 index 0000000000..190c0239cd --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/_vendor/six.py @@ -0,0 +1,868 @@ +"""Utilities for writing code that runs on Python 2 and 3""" + +# Copyright (c) 2010-2015 Benjamin Peterson +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from __future__ import absolute_import + +import functools +import itertools +import operator +import sys +import types + +__author__ = "Benjamin Peterson " +__version__ = "1.10.0" + + +# Useful for very coarse version differentiation. +PY2 = sys.version_info[0] == 2 +PY3 = sys.version_info[0] == 3 +PY34 = sys.version_info[0:2] >= (3, 4) + +if PY3: + string_types = str, + integer_types = int, + class_types = type, + text_type = str + binary_type = bytes + + MAXSIZE = sys.maxsize +else: + string_types = basestring, + integer_types = (int, long) + class_types = (type, types.ClassType) + text_type = unicode + binary_type = str + + if sys.platform.startswith("java"): + # Jython always uses 32 bits. + MAXSIZE = int((1 << 31) - 1) + else: + # It's possible to have sizeof(long) != sizeof(Py_ssize_t). + class X(object): + + def __len__(self): + return 1 << 31 + try: + len(X()) + except OverflowError: + # 32-bit + MAXSIZE = int((1 << 31) - 1) + else: + # 64-bit + MAXSIZE = int((1 << 63) - 1) + del X + + +def _add_doc(func, doc): + """Add documentation to a function.""" + func.__doc__ = doc + + +def _import_module(name): + """Import module, returning the module after the last dot.""" + __import__(name) + return sys.modules[name] + + +class _LazyDescr(object): + + def __init__(self, name): + self.name = name + + def __get__(self, obj, tp): + result = self._resolve() + setattr(obj, self.name, result) # Invokes __set__. + try: + # This is a bit ugly, but it avoids running this again by + # removing this descriptor. + delattr(obj.__class__, self.name) + except AttributeError: + pass + return result + + +class MovedModule(_LazyDescr): + + def __init__(self, name, old, new=None): + super(MovedModule, self).__init__(name) + if PY3: + if new is None: + new = name + self.mod = new + else: + self.mod = old + + def _resolve(self): + return _import_module(self.mod) + + def __getattr__(self, attr): + _module = self._resolve() + value = getattr(_module, attr) + setattr(self, attr, value) + return value + + +class _LazyModule(types.ModuleType): + + def __init__(self, name): + super(_LazyModule, self).__init__(name) + self.__doc__ = self.__class__.__doc__ + + def __dir__(self): + attrs = ["__doc__", "__name__"] + attrs += [attr.name for attr in self._moved_attributes] + return attrs + + # Subclasses should override this + _moved_attributes = [] + + +class MovedAttribute(_LazyDescr): + + def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): + super(MovedAttribute, self).__init__(name) + if PY3: + if new_mod is None: + new_mod = name + self.mod = new_mod + if new_attr is None: + if old_attr is None: + new_attr = name + else: + new_attr = old_attr + self.attr = new_attr + else: + self.mod = old_mod + if old_attr is None: + old_attr = name + self.attr = old_attr + + def _resolve(self): + module = _import_module(self.mod) + return getattr(module, self.attr) + + +class _SixMetaPathImporter(object): + + """ + A meta path importer to import six.moves and its submodules. + + This class implements a PEP302 finder and loader. It should be compatible + with Python 2.5 and all existing versions of Python3 + """ + + def __init__(self, six_module_name): + self.name = six_module_name + self.known_modules = {} + + def _add_module(self, mod, *fullnames): + for fullname in fullnames: + self.known_modules[self.name + "." + fullname] = mod + + def _get_module(self, fullname): + return self.known_modules[self.name + "." + fullname] + + def find_module(self, fullname, path=None): + if fullname in self.known_modules: + return self + return None + + def __get_module(self, fullname): + try: + return self.known_modules[fullname] + except KeyError: + raise ImportError("This loader does not know module " + fullname) + + def load_module(self, fullname): + try: + # in case of a reload + return sys.modules[fullname] + except KeyError: + pass + mod = self.__get_module(fullname) + if isinstance(mod, MovedModule): + mod = mod._resolve() + else: + mod.__loader__ = self + sys.modules[fullname] = mod + return mod + + def is_package(self, fullname): + """ + Return true, if the named module is a package. + + We need this method to get correct spec objects with + Python 3.4 (see PEP451) + """ + return hasattr(self.__get_module(fullname), "__path__") + + def get_code(self, fullname): + """Return None + + Required, if is_package is implemented""" + self.__get_module(fullname) # eventually raises ImportError + return None + get_source = get_code # same as get_code + +_importer = _SixMetaPathImporter(__name__) + + +class _MovedItems(_LazyModule): + + """Lazy loading of moved objects""" + __path__ = [] # mark as package + + +_moved_attributes = [ + MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), + MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), + MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), + MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), + MovedAttribute("intern", "__builtin__", "sys"), + MovedAttribute("map", "itertools", "builtins", "imap", "map"), + MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"), + MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"), + MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), + MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"), + MovedAttribute("reduce", "__builtin__", "functools"), + MovedAttribute("shlex_quote", "pipes", "shlex", "quote"), + MovedAttribute("StringIO", "StringIO", "io"), + MovedAttribute("UserDict", "UserDict", "collections"), + MovedAttribute("UserList", "UserList", "collections"), + MovedAttribute("UserString", "UserString", "collections"), + MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), + MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), + MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), + MovedModule("builtins", "__builtin__"), + MovedModule("configparser", "ConfigParser"), + MovedModule("copyreg", "copy_reg"), + MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), + MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"), + MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), + MovedModule("http_cookies", "Cookie", "http.cookies"), + MovedModule("html_entities", "htmlentitydefs", "html.entities"), + MovedModule("html_parser", "HTMLParser", "html.parser"), + MovedModule("http_client", "httplib", "http.client"), + MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), + MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"), + MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), + MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), + MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), + MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), + MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), + MovedModule("cPickle", "cPickle", "pickle"), + MovedModule("queue", "Queue"), + MovedModule("reprlib", "repr"), + MovedModule("socketserver", "SocketServer"), + MovedModule("_thread", "thread", "_thread"), + MovedModule("tkinter", "Tkinter"), + MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"), + MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"), + MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"), + MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"), + MovedModule("tkinter_tix", "Tix", "tkinter.tix"), + MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"), + MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), + MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), + MovedModule("tkinter_colorchooser", "tkColorChooser", + "tkinter.colorchooser"), + MovedModule("tkinter_commondialog", "tkCommonDialog", + "tkinter.commondialog"), + MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), + MovedModule("tkinter_font", "tkFont", "tkinter.font"), + MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), + MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", + "tkinter.simpledialog"), + MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"), + MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"), + MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), + MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), + MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"), + MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"), +] +# Add windows specific modules. +if sys.platform == "win32": + _moved_attributes += [ + MovedModule("winreg", "_winreg"), + ] + +for attr in _moved_attributes: + setattr(_MovedItems, attr.name, attr) + if isinstance(attr, MovedModule): + _importer._add_module(attr, "moves." + attr.name) +del attr + +_MovedItems._moved_attributes = _moved_attributes + +moves = _MovedItems(__name__ + ".moves") +_importer._add_module(moves, "moves") + + +class Module_six_moves_urllib_parse(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_parse""" + + +_urllib_parse_moved_attributes = [ + MovedAttribute("ParseResult", "urlparse", "urllib.parse"), + MovedAttribute("SplitResult", "urlparse", "urllib.parse"), + MovedAttribute("parse_qs", "urlparse", "urllib.parse"), + MovedAttribute("parse_qsl", "urlparse", "urllib.parse"), + MovedAttribute("urldefrag", "urlparse", "urllib.parse"), + MovedAttribute("urljoin", "urlparse", "urllib.parse"), + MovedAttribute("urlparse", "urlparse", "urllib.parse"), + MovedAttribute("urlsplit", "urlparse", "urllib.parse"), + MovedAttribute("urlunparse", "urlparse", "urllib.parse"), + MovedAttribute("urlunsplit", "urlparse", "urllib.parse"), + MovedAttribute("quote", "urllib", "urllib.parse"), + MovedAttribute("quote_plus", "urllib", "urllib.parse"), + MovedAttribute("unquote", "urllib", "urllib.parse"), + MovedAttribute("unquote_plus", "urllib", "urllib.parse"), + MovedAttribute("urlencode", "urllib", "urllib.parse"), + MovedAttribute("splitquery", "urllib", "urllib.parse"), + MovedAttribute("splittag", "urllib", "urllib.parse"), + MovedAttribute("splituser", "urllib", "urllib.parse"), + MovedAttribute("uses_fragment", "urlparse", "urllib.parse"), + MovedAttribute("uses_netloc", "urlparse", "urllib.parse"), + MovedAttribute("uses_params", "urlparse", "urllib.parse"), + MovedAttribute("uses_query", "urlparse", "urllib.parse"), + MovedAttribute("uses_relative", "urlparse", "urllib.parse"), +] +for attr in _urllib_parse_moved_attributes: + setattr(Module_six_moves_urllib_parse, attr.name, attr) +del attr + +Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes + +_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), + "moves.urllib_parse", "moves.urllib.parse") + + +class Module_six_moves_urllib_error(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_error""" + + +_urllib_error_moved_attributes = [ + MovedAttribute("URLError", "urllib2", "urllib.error"), + MovedAttribute("HTTPError", "urllib2", "urllib.error"), + MovedAttribute("ContentTooShortError", "urllib", "urllib.error"), +] +for attr in _urllib_error_moved_attributes: + setattr(Module_six_moves_urllib_error, attr.name, attr) +del attr + +Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes + +_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), + "moves.urllib_error", "moves.urllib.error") + + +class Module_six_moves_urllib_request(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_request""" + + +_urllib_request_moved_attributes = [ + MovedAttribute("urlopen", "urllib2", "urllib.request"), + MovedAttribute("install_opener", "urllib2", "urllib.request"), + MovedAttribute("build_opener", "urllib2", "urllib.request"), + MovedAttribute("pathname2url", "urllib", "urllib.request"), + MovedAttribute("url2pathname", "urllib", "urllib.request"), + MovedAttribute("getproxies", "urllib", "urllib.request"), + MovedAttribute("Request", "urllib2", "urllib.request"), + MovedAttribute("OpenerDirector", "urllib2", "urllib.request"), + MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"), + MovedAttribute("ProxyHandler", "urllib2", "urllib.request"), + MovedAttribute("BaseHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"), + MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"), + MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"), + MovedAttribute("FileHandler", "urllib2", "urllib.request"), + MovedAttribute("FTPHandler", "urllib2", "urllib.request"), + MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"), + MovedAttribute("UnknownHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"), + MovedAttribute("urlretrieve", "urllib", "urllib.request"), + MovedAttribute("urlcleanup", "urllib", "urllib.request"), + MovedAttribute("URLopener", "urllib", "urllib.request"), + MovedAttribute("FancyURLopener", "urllib", "urllib.request"), + MovedAttribute("proxy_bypass", "urllib", "urllib.request"), +] +for attr in _urllib_request_moved_attributes: + setattr(Module_six_moves_urllib_request, attr.name, attr) +del attr + +Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes + +_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), + "moves.urllib_request", "moves.urllib.request") + + +class Module_six_moves_urllib_response(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_response""" + + +_urllib_response_moved_attributes = [ + MovedAttribute("addbase", "urllib", "urllib.response"), + MovedAttribute("addclosehook", "urllib", "urllib.response"), + MovedAttribute("addinfo", "urllib", "urllib.response"), + MovedAttribute("addinfourl", "urllib", "urllib.response"), +] +for attr in _urllib_response_moved_attributes: + setattr(Module_six_moves_urllib_response, attr.name, attr) +del attr + +Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes + +_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), + "moves.urllib_response", "moves.urllib.response") + + +class Module_six_moves_urllib_robotparser(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_robotparser""" + + +_urllib_robotparser_moved_attributes = [ + MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"), +] +for attr in _urllib_robotparser_moved_attributes: + setattr(Module_six_moves_urllib_robotparser, attr.name, attr) +del attr + +Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes + +_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), + "moves.urllib_robotparser", "moves.urllib.robotparser") + + +class Module_six_moves_urllib(types.ModuleType): + + """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" + __path__ = [] # mark as package + parse = _importer._get_module("moves.urllib_parse") + error = _importer._get_module("moves.urllib_error") + request = _importer._get_module("moves.urllib_request") + response = _importer._get_module("moves.urllib_response") + robotparser = _importer._get_module("moves.urllib_robotparser") + + def __dir__(self): + return ['parse', 'error', 'request', 'response', 'robotparser'] + +_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"), + "moves.urllib") + + +def add_move(move): + """Add an item to six.moves.""" + setattr(_MovedItems, move.name, move) + + +def remove_move(name): + """Remove item from six.moves.""" + try: + delattr(_MovedItems, name) + except AttributeError: + try: + del moves.__dict__[name] + except KeyError: + raise AttributeError("no such move, %r" % (name,)) + + +if PY3: + _meth_func = "__func__" + _meth_self = "__self__" + + _func_closure = "__closure__" + _func_code = "__code__" + _func_defaults = "__defaults__" + _func_globals = "__globals__" +else: + _meth_func = "im_func" + _meth_self = "im_self" + + _func_closure = "func_closure" + _func_code = "func_code" + _func_defaults = "func_defaults" + _func_globals = "func_globals" + + +try: + advance_iterator = next +except NameError: + def advance_iterator(it): + return it.next() +next = advance_iterator + + +try: + callable = callable +except NameError: + def callable(obj): + return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) + + +if PY3: + def get_unbound_function(unbound): + return unbound + + create_bound_method = types.MethodType + + def create_unbound_method(func, cls): + return func + + Iterator = object +else: + def get_unbound_function(unbound): + return unbound.im_func + + def create_bound_method(func, obj): + return types.MethodType(func, obj, obj.__class__) + + def create_unbound_method(func, cls): + return types.MethodType(func, None, cls) + + class Iterator(object): + + def next(self): + return type(self).__next__(self) + + callable = callable +_add_doc(get_unbound_function, + """Get the function out of a possibly unbound function""") + + +get_method_function = operator.attrgetter(_meth_func) +get_method_self = operator.attrgetter(_meth_self) +get_function_closure = operator.attrgetter(_func_closure) +get_function_code = operator.attrgetter(_func_code) +get_function_defaults = operator.attrgetter(_func_defaults) +get_function_globals = operator.attrgetter(_func_globals) + + +if PY3: + def iterkeys(d, **kw): + return iter(d.keys(**kw)) + + def itervalues(d, **kw): + return iter(d.values(**kw)) + + def iteritems(d, **kw): + return iter(d.items(**kw)) + + def iterlists(d, **kw): + return iter(d.lists(**kw)) + + viewkeys = operator.methodcaller("keys") + + viewvalues = operator.methodcaller("values") + + viewitems = operator.methodcaller("items") +else: + def iterkeys(d, **kw): + return d.iterkeys(**kw) + + def itervalues(d, **kw): + return d.itervalues(**kw) + + def iteritems(d, **kw): + return d.iteritems(**kw) + + def iterlists(d, **kw): + return d.iterlists(**kw) + + viewkeys = operator.methodcaller("viewkeys") + + viewvalues = operator.methodcaller("viewvalues") + + viewitems = operator.methodcaller("viewitems") + +_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.") +_add_doc(itervalues, "Return an iterator over the values of a dictionary.") +_add_doc(iteritems, + "Return an iterator over the (key, value) pairs of a dictionary.") +_add_doc(iterlists, + "Return an iterator over the (key, [values]) pairs of a dictionary.") + + +if PY3: + def b(s): + return s.encode("latin-1") + + def u(s): + return s + unichr = chr + import struct + int2byte = struct.Struct(">B").pack + del struct + byte2int = operator.itemgetter(0) + indexbytes = operator.getitem + iterbytes = iter + import io + StringIO = io.StringIO + BytesIO = io.BytesIO + _assertCountEqual = "assertCountEqual" + if sys.version_info[1] <= 1: + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" + else: + _assertRaisesRegex = "assertRaisesRegex" + _assertRegex = "assertRegex" +else: + def b(s): + return s + # Workaround for standalone backslash + + def u(s): + return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape") + unichr = unichr + int2byte = chr + + def byte2int(bs): + return ord(bs[0]) + + def indexbytes(buf, i): + return ord(buf[i]) + iterbytes = functools.partial(itertools.imap, ord) + import StringIO + StringIO = BytesIO = StringIO.StringIO + _assertCountEqual = "assertItemsEqual" + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" +_add_doc(b, """Byte literal""") +_add_doc(u, """Text literal""") + + +def assertCountEqual(self, *args, **kwargs): + return getattr(self, _assertCountEqual)(*args, **kwargs) + + +def assertRaisesRegex(self, *args, **kwargs): + return getattr(self, _assertRaisesRegex)(*args, **kwargs) + + +def assertRegex(self, *args, **kwargs): + return getattr(self, _assertRegex)(*args, **kwargs) + + +if PY3: + exec_ = getattr(moves.builtins, "exec") + + def reraise(tp, value, tb=None): + if value is None: + value = tp() + if value.__traceback__ is not tb: + raise value.with_traceback(tb) + raise value + +else: + def exec_(_code_, _globs_=None, _locs_=None): + """Execute code in a namespace.""" + if _globs_ is None: + frame = sys._getframe(1) + _globs_ = frame.f_globals + if _locs_ is None: + _locs_ = frame.f_locals + del frame + elif _locs_ is None: + _locs_ = _globs_ + exec("""exec _code_ in _globs_, _locs_""") + + exec_("""def reraise(tp, value, tb=None): + raise tp, value, tb +""") + + +if sys.version_info[:2] == (3, 2): + exec_("""def raise_from(value, from_value): + if from_value is None: + raise value + raise value from from_value +""") +elif sys.version_info[:2] > (3, 2): + exec_("""def raise_from(value, from_value): + raise value from from_value +""") +else: + def raise_from(value, from_value): + raise value + + +print_ = getattr(moves.builtins, "print", None) +if print_ is None: + def print_(*args, **kwargs): + """The new-style print function for Python 2.4 and 2.5.""" + fp = kwargs.pop("file", sys.stdout) + if fp is None: + return + + def write(data): + if not isinstance(data, basestring): + data = str(data) + # If the file has an encoding, encode unicode with it. + if (isinstance(fp, file) and + isinstance(data, unicode) and + fp.encoding is not None): + errors = getattr(fp, "errors", None) + if errors is None: + errors = "strict" + data = data.encode(fp.encoding, errors) + fp.write(data) + want_unicode = False + sep = kwargs.pop("sep", None) + if sep is not None: + if isinstance(sep, unicode): + want_unicode = True + elif not isinstance(sep, str): + raise TypeError("sep must be None or a string") + end = kwargs.pop("end", None) + if end is not None: + if isinstance(end, unicode): + want_unicode = True + elif not isinstance(end, str): + raise TypeError("end must be None or a string") + if kwargs: + raise TypeError("invalid keyword arguments to print()") + if not want_unicode: + for arg in args: + if isinstance(arg, unicode): + want_unicode = True + break + if want_unicode: + newline = unicode("\n") + space = unicode(" ") + else: + newline = "\n" + space = " " + if sep is None: + sep = space + if end is None: + end = newline + for i, arg in enumerate(args): + if i: + write(sep) + write(arg) + write(end) +if sys.version_info[:2] < (3, 3): + _print = print_ + + def print_(*args, **kwargs): + fp = kwargs.get("file", sys.stdout) + flush = kwargs.pop("flush", False) + _print(*args, **kwargs) + if flush and fp is not None: + fp.flush() + +_add_doc(reraise, """Reraise an exception.""") + +if sys.version_info[0:2] < (3, 4): + def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, + updated=functools.WRAPPER_UPDATES): + def wrapper(f): + f = functools.wraps(wrapped, assigned, updated)(f) + f.__wrapped__ = wrapped + return f + return wrapper +else: + wraps = functools.wraps + + +def with_metaclass(meta, *bases): + """Create a base class with a metaclass.""" + # This requires a bit of explanation: the basic idea is to make a dummy + # metaclass for one level of class instantiation that replaces itself with + # the actual metaclass. + class metaclass(meta): + + def __new__(cls, name, this_bases, d): + return meta(name, bases, d) + return type.__new__(metaclass, 'temporary_class', (), {}) + + +def add_metaclass(metaclass): + """Class decorator for creating a class with a metaclass.""" + def wrapper(cls): + orig_vars = cls.__dict__.copy() + slots = orig_vars.get('__slots__') + if slots is not None: + if isinstance(slots, str): + slots = [slots] + for slots_var in slots: + orig_vars.pop(slots_var) + orig_vars.pop('__dict__', None) + orig_vars.pop('__weakref__', None) + return metaclass(cls.__name__, cls.__bases__, orig_vars) + return wrapper + + +def python_2_unicode_compatible(klass): + """ + A decorator that defines __unicode__ and __str__ methods under Python 2. + Under Python 3 it does nothing. + + To support Python 2 and 3 with a single code base, define a __str__ method + returning text and apply this decorator to the class. + """ + if PY2: + if '__str__' not in klass.__dict__: + raise ValueError("@python_2_unicode_compatible cannot be applied " + "to %s because it doesn't define __str__()." % + klass.__name__) + klass.__unicode__ = klass.__str__ + klass.__str__ = lambda self: self.__unicode__().encode('utf-8') + return klass + + +# Complete the moves implementation. +# This code is at the end of this module to speed up module loading. +# Turn this module into a package. +__path__ = [] # required for PEP 302 and PEP 451 +__package__ = __name__ # see PEP 366 @ReservedAssignment +if globals().get("__spec__") is not None: + __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable +# Remove other six meta path importers, since they cause problems. This can +# happen if six is removed from sys.modules and then reloaded. (Setuptools does +# this for some reason.) +if sys.meta_path: + for i, importer in enumerate(sys.meta_path): + # Here's some real nastiness: Another "instance" of the six module might + # be floating around. Therefore, we can't use isinstance() to check for + # the six meta path importer, since the other six instance will have + # inserted an importer with different class. + if (type(importer).__name__ == "_SixMetaPathImporter" and + importer.name == __name__): + del sys.meta_path[i] + break + del i, importer +# Finally, add the importer to the meta path import hook. +sys.meta_path.append(_importer) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/extern/__init__.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/extern/__init__.py new file mode 100644 index 0000000000..c1eb9e998f --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/extern/__init__.py @@ -0,0 +1,73 @@ +import sys + + +class VendorImporter: + """ + A PEP 302 meta path importer for finding optionally-vendored + or otherwise naturally-installed packages from root_name. + """ + + def __init__(self, root_name, vendored_names=(), vendor_pkg=None): + self.root_name = root_name + self.vendored_names = set(vendored_names) + self.vendor_pkg = vendor_pkg or root_name.replace('extern', '_vendor') + + @property + def search_path(self): + """ + Search first the vendor package then as a natural package. + """ + yield self.vendor_pkg + '.' + yield '' + + def find_module(self, fullname, path=None): + """ + Return self when fullname starts with root_name and the + target module is one vendored through this importer. + """ + root, base, target = fullname.partition(self.root_name + '.') + if root: + return + if not any(map(target.startswith, self.vendored_names)): + return + return self + + def load_module(self, fullname): + """ + Iterate over the search path to locate and load fullname. + """ + root, base, target = fullname.partition(self.root_name + '.') + for prefix in self.search_path: + try: + extant = prefix + target + __import__(extant) + mod = sys.modules[extant] + sys.modules[fullname] = mod + # mysterious hack: + # Remove the reference to the extant package/module + # on later Python versions to cause relative imports + # in the vendor package to resolve the same modules + # as those going through this importer. + if prefix and sys.version_info > (3, 3): + del sys.modules[extant] + return mod + except ImportError: + pass + else: + raise ImportError( + "The '{target}' package is required; " + "normally this is bundled with this package so if you get " + "this warning, consult the packager of your " + "distribution.".format(**locals()) + ) + + def install(self): + """ + Install this importer into sys.meta_path if not already present. + """ + if self not in sys.meta_path: + sys.meta_path.append(self) + + +names = 'packaging', 'pyparsing', 'six', 'appdirs' +VendorImporter(__name__, names).install() diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/py31compat.py b/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/py31compat.py new file mode 100644 index 0000000000..a381c424f9 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/pkg_resources/py31compat.py @@ -0,0 +1,23 @@ +import os +import errno +import sys + +from .extern import six + + +def _makedirs_31(path, exist_ok=False): + try: + os.makedirs(path) + except OSError as exc: + if not exist_ok or exc.errno != errno.EEXIST: + raise + + +# rely on compatibility behavior until mode considerations +# and exists_ok considerations are disentangled. +# See https://github.com/pypa/setuptools/pull/1083#issuecomment-315168663 +needs_makedirs = ( + six.PY2 or + (3, 4) <= sys.version_info < (3, 4, 1) +) +makedirs = _makedirs_31 if needs_makedirs else os.makedirs diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/AUTHORS.txt b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/AUTHORS.txt new file mode 100644 index 0000000000..0360f988f2 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/AUTHORS.txt @@ -0,0 +1,590 @@ +@Switch01 +A_Rog +Aakanksha Agrawal +Abhinav Sagar +ABHYUDAY PRATAP SINGH +abs51295 +AceGentile +Adam Chainz +Adam Tse +Adam Wentz +admin +Adrien Morison +ahayrapetyan +Ahilya +AinsworthK +Akash Srivastava +Alan Yee +Albert Tugushev +Albert-Guan +albertg +Aleks Bunin +Alethea Flowers +Alex Gaynor +Alex Grönholm +Alex Loosley +Alex Morega +Alex Stachowiak +Alexander Shtyrov +Alexandre Conrad +Alexey Popravka +Alli +Ami Fischman +Ananya Maiti +Anatoly Techtonik +Anders Kaseorg +Andre Aguiar +Andreas Lutro +Andrei Geacar +Andrew Gaul +Andrey Bulgakov +Andrés Delfino +Andy Freeland +Andy Kluger +Ani Hayrapetyan +Aniruddha Basak +Anish Tambe +Anrs Hu +Anthony Sottile +Antoine Musso +Anton Ovchinnikov +Anton Patrushev +Antonio Alvarado Hernandez +Antony Lee +Antti Kaihola +Anubhav Patel +Anudit Nagar +Anuj Godase +AQNOUCH Mohammed +AraHaan +Arindam Choudhury +Armin Ronacher +Artem +Ashley Manton +Ashwin Ramaswami +atse +Atsushi Odagiri +Avinash Karhana +Avner Cohen +Baptiste Mispelon +Barney Gale +barneygale +Bartek Ogryczak +Bastian Venthur +Ben Darnell +Ben Hoyt +Ben Rosser +Bence Nagy +Benjamin Peterson +Benjamin VanEvery +Benoit Pierre +Berker Peksag +Bernard +Bernard Tyers +Bernardo B. Marques +Bernhard M. Wiedemann +Bertil Hatt +Bhavam Vidyarthi +Bogdan Opanchuk +BorisZZZ +Brad Erickson +Bradley Ayers +Brandon L. Reiss +Brandt Bucher +Brett Randall +Brian Cristante +Brian Rosner +BrownTruck +Bruno Oliveira +Bruno Renié +Bstrdsmkr +Buck Golemon +burrows +Bussonnier Matthias +c22 +Caleb Martinez +Calvin Smith +Carl Meyer +Carlos Liam +Carol Willing +Carter Thayer +Cass +Chandrasekhar Atina +Chih-Hsuan Yen +Chris Brinker +Chris Hunt +Chris Jerdonek +Chris McDonough +Chris Wolfe +Christian Clauss +Christian Heimes +Christian Oudard +Christoph Reiter +Christopher Hunt +Christopher Snyder +cjc7373 +Clark Boylan +Clay McClure +Cody +Cody Soyland +Colin Watson +Connor Osborn +Cooper Lees +Cooper Ry Lees +Cory Benfield +Cory Wright +Craig Kerstiens +Cristian Sorinel +Cristina +Cristina Muñoz +Curtis Doty +cytolentino +Damian Quiroga +Dan Black +Dan Savilonis +Dan Sully +daniel +Daniel Collins +Daniel Hahler +Daniel Holth +Daniel Jost +Daniel Katz +Daniel Shaulov +Daniele Esposti +Daniele Procida +Danny Hermes +Danny McClanahan +Dav Clark +Dave Abrahams +Dave Jones +David Aguilar +David Black +David Bordeynik +David Caro +David Evans +David Linke +David Poggi +David Pursehouse +David Tucker +David Wales +Davidovich +Deepak Sharma +derwolfe +Desetude +Devesh Kumar Singh +Diego Caraballo +DiegoCaraballo +Dmitry Gladkov +Domen Kožar +Donald Stufft +Dongweiming +Douglas Thor +DrFeathers +Dustin Ingram +Dwayne Bailey +Ed Morley +Eitan Adler +ekristina +elainechan +Eli Schwartz +Elisha Hollander +Ellen Marie Dash +Emil Burzo +Emil Styrke +Emmanuel Arias +Endoh Takanao +enoch +Erdinc Mutlu +Eric Gillingham +Eric Hanchrow +Eric Hopper +Erik M. Bray +Erik Rose +Ernest W Durbin III +Ernest W. Durbin III +Erwin Janssen +Eugene Vereshchagin +everdimension +Felix Yan +fiber-space +Filip Kokosiński +Filipe Laíns +Florian Briand +Florian Rathgeber +Francesco +Francesco Montesano +Frost Ming +Gabriel Curio +Gabriel de Perthuis +Garry Polley +gdanielson +Geoffrey Sneddon +George Song +Georgi Valkov +ghost +Giftlin Rajaiah +gizmoguy1 +gkdoc +Gopinath M +GOTO Hayato +gpiks +Greg Ward +Guilherme Espada +gutsytechster +Guy Rozendorn +gzpan123 +Hanjun Kim +Hari Charan +Harsh Vardhan +Herbert Pfennig +Hsiaoming Yang +Hugo +Hugo Lopes Tavares +Hugo van Kemenade +hugovk +Hynek Schlawack +Ian Bicking +Ian Cordasco +Ian Lee +Ian Stapleton Cordasco +Ian Wienand +Igor Kuzmitshov +Igor Sobreira +Ilan Schnell +Ilya Baryshev +INADA Naoki +Ionel Cristian Mărieș +Ionel Maries Cristian +Ivan Pozdeev +Jacob Kim +jakirkham +Jakub Stasiak +Jakub Vysoky +Jakub Wilk +James Cleveland +James Firth +James Polley +Jan Pokorný +Jannis Leidel +jarondl +Jason R. Coombs +Jay Graves +Jean-Christophe Fillion-Robin +Jeff Barber +Jeff Dairiki +Jelmer Vernooij +jenix21 +Jeremy Stanley +Jeremy Zafran +Jiashuo Li +Jim Garrison +Jivan Amara +John Paton +John T. Wodder II +John-Scott Atlakson +johnthagen +Jon Banafato +Jon Dufresne +Jon Parise +Jonas Nockert +Jonathan Herbert +Joost Molenaar +Jorge Niedbalski +Joseph Long +Josh Bronson +Josh Hansen +Josh Schneier +Juanjo Bazán +Julian Berman +Julian Gethmann +Julien Demoor +Jussi Kukkonen +jwg4 +Jyrki Pulliainen +Kai Chen +Kamal Bin Mustafa +kaustav haldar +keanemind +Keith Maxwell +Kelsey Hightower +Kenneth Belitzky +Kenneth Reitz +Kevin Burke +Kevin Carter +Kevin Frommelt +Kevin R Patterson +Kexuan Sun +Kit Randel +KOLANICH +kpinc +Krishna Oza +Kumar McMillan +Kyle Persohn +lakshmanaram +Laszlo Kiss-Kollar +Laurent Bristiel +Laurie O +Laurie Opperman +Leon Sasson +Lev Givon +Lincoln de Sousa +Lipis +Loren Carvalho +Lucas Cimon +Ludovic Gasc +Luke Macken +Luo Jiebin +luojiebin +luz.paz +László Kiss Kollár +Marc Abramowitz +Marc Tamlyn +Marcus Smith +Mariatta +Mark Kohler +Mark Williams +Markus Hametner +Masaki +Masklinn +Matej Stuchlik +Mathew Jennings +Mathieu Bridon +Matt Good +Matt Maker +Matt Robenolt +matthew +Matthew Einhorn +Matthew Gilliard +Matthew Iversen +Matthew Trumbell +Matthew Willson +Matthias Bussonnier +mattip +Maxim Kurnikov +Maxime Rouyrre +mayeut +mbaluna +mdebi +memoselyk +Michael +Michael Aquilina +Michael E. Karpeles +Michael Klich +Michael Williamson +michaelpacer +Mickaël Schoentgen +Miguel Araujo Perez +Mihir Singh +Mike +Mike Hendricks +Min RK +MinRK +Miro Hrončok +Monica Baluna +montefra +Monty Taylor +Nate Coraor +Nathaniel J. Smith +Nehal J Wani +Neil Botelho +Nguyễn Gia Phong +Nick Coghlan +Nick Stenning +Nick Timkovich +Nicolas Bock +Nicole Harris +Nikhil Benesch +Nikolay Korolev +Nitesh Sharma +Noah +Noah Gorny +Nowell Strite +NtaleGrey +nvdv +Ofekmeister +ofrinevo +Oliver Jeeves +Oliver Mannion +Oliver Tonnhofer +Olivier Girardot +Olivier Grisel +Ollie Rutherfurd +OMOTO Kenji +Omry Yadan +onlinejudge95 +Oren Held +Oscar Benjamin +Oz N Tiram +Pachwenko +Patrick Dubroy +Patrick Jenkins +Patrick Lawson +patricktokeeffe +Patrik Kopkan +Paul Kehrer +Paul Moore +Paul Nasrat +Paul Oswald +Paul van der Linden +Paulus Schoutsen +Pavithra Eswaramoorthy +Pawel Jasinski +Pekka Klärck +Peter Lisák +Peter Waller +petr-tik +Phaneendra Chiruvella +Phil Elson +Phil Freo +Phil Pennock +Phil Whelan +Philip Jägenstedt +Philip Molloy +Philippe Ombredanne +Pi Delport +Pierre-Yves Rofes +pip +Prabakaran Kumaresshan +Prabhjyotsing Surjit Singh Sodhi +Prabhu Marappan +Pradyun Gedam +Prashant Sharma +Pratik Mallya +Preet Thakkar +Preston Holmes +Przemek Wrzos +Pulkit Goyal +Qiangning Hong +Quentin Pradet +R. David Murray +Rafael Caricio +Ralf Schmitt +Razzi Abuissa +rdb +Reece Dunham +Remi Rampin +Rene Dudfield +Riccardo Magliocchetti +Richard Jones +Ricky Ng-Adam +RobberPhex +Robert Collins +Robert McGibbon +Robert T. McGibbon +robin elisha robinson +Roey Berman +Rohan Jain +Roman Bogorodskiy +Romuald Brunet +Ronny Pfannschmidt +Rory McCann +Ross Brattain +Roy Wellington Ⅳ +Ruairidh MacLeod +Ryan Wooden +ryneeverett +Sachi King +Salvatore Rinchiera +Savio Jomton +schlamar +Scott Kitterman +Sean +seanj +Sebastian Jordan +Sebastian Schaetz +Segev Finer +SeongSoo Cho +Sergey Vasilyev +Seth Woodworth +shireenrao +Shlomi Fish +Shovan Maity +Simeon Visser +Simon Cross +Simon Pichugin +sinoroc +sinscary +socketubs +Sorin Sbarnea +Srinivas Nyayapati +Stavros Korokithakis +Stefan Scherfke +Stefano Rivera +Stephan Erb +stepshal +Steve (Gadget) Barnes +Steve Barnes +Steve Dower +Steve Kowalik +Steven Myint +stonebig +Stéphane Bidoul +Stéphane Bidoul (ACSONE) +Stéphane Klein +Sumana Harihareswara +Surbhi Sharma +Sviatoslav Sydorenko +Swat009 +Takayuki SHIMIZUKAWA +tbeswick +Thijs Triemstra +Thomas Fenzl +Thomas Grainger +Thomas Guettler +Thomas Johansson +Thomas Kluyver +Thomas Smith +Tim D. Smith +Tim Gates +Tim Harder +Tim Heap +tim smith +tinruufu +Tom Forbes +Tom Freudenheim +Tom V +Tomas Hrnciar +Tomas Orsava +Tomer Chachamu +Tony Beswick +Tony Zhaocheng Tan +TonyBeswick +toonarmycaptain +Toshio Kuratomi +toxinu +Travis Swicegood +Tzu-ping Chung +Valentin Haenel +Victor Stinner +victorvpaulo +Vikram - Google +Viktor Szépe +Ville Skyttä +Vinay Sajip +Vincent Philippon +Vinicyus Macedo +Vipul Kumar +Vitaly Babiy +Vladimir Rutsky +W. Trevor King +Wil Tan +Wilfred Hughes +William ML Leslie +William T Olson +Wilson Mo +wim glenn +Wolfgang Maier +Xavier Fernandez +xoviat +xtreak +YAMAMOTO Takashi +Yen Chi Hsuan +Yeray Diaz Diaz +Yoval P +Yu Jian +Yuan Jing Vincent Yan +Zearin +Zhiping Deng +Zvezdan Petkovic +Łukasz Langa +Семён Марьясин diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/INSTALLER b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/INSTALLER new file mode 100644 index 0000000000..a1b589e38a --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/LICENSE.txt b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/LICENSE.txt new file mode 100644 index 0000000000..75eb0fd80b --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/LICENSE.txt @@ -0,0 +1,20 @@ +Copyright (c) 2008-2020 The pip developers (see AUTHORS.txt file) + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/METADATA b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/METADATA new file mode 100644 index 0000000000..a1a8d09397 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/METADATA @@ -0,0 +1,82 @@ +Metadata-Version: 2.1 +Name: setuptools +Version: 44.1.1 +Summary: Easily download, build, install, upgrade, and uninstall Python packages +Home-page: https://github.com/pypa/setuptools +Author: Python Packaging Authority +Author-email: distutils-sig@python.org +License: UNKNOWN +Project-URL: Documentation, https://setuptools.readthedocs.io/ +Keywords: CPAN PyPI distutils eggs package management +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: System :: Archiving :: Packaging +Classifier: Topic :: System :: Systems Administration +Classifier: Topic :: Utilities +Requires-Python: !=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7 +Description-Content-Type: text/x-rst; charset=UTF-8 + +.. image:: https://img.shields.io/pypi/v/setuptools.svg + :target: https://pypi.org/project/setuptools + +.. image:: https://img.shields.io/readthedocs/setuptools/latest.svg + :target: https://setuptools.readthedocs.io + +.. image:: https://img.shields.io/travis/pypa/setuptools/master.svg?label=Linux%20CI&logo=travis&logoColor=white + :target: https://travis-ci.org/pypa/setuptools + +.. image:: https://img.shields.io/appveyor/ci/pypa/setuptools/master.svg?label=Windows%20CI&logo=appveyor&logoColor=white + :target: https://ci.appveyor.com/project/pypa/setuptools/branch/master + +.. image:: https://img.shields.io/codecov/c/github/pypa/setuptools/master.svg?logo=codecov&logoColor=white + :target: https://codecov.io/gh/pypa/setuptools + +.. image:: https://tidelift.com/badges/github/pypa/setuptools?style=flat + :target: https://tidelift.com/subscription/pkg/pypi-setuptools?utm_source=pypi-setuptools&utm_medium=readme + +.. image:: https://img.shields.io/pypi/pyversions/setuptools.svg + +See the `Installation Instructions +`_ in the Python Packaging +User's Guide for instructions on installing, upgrading, and uninstalling +Setuptools. + +Questions and comments should be directed to the `distutils-sig +mailing list `_. +Bug reports and especially tested patches may be +submitted directly to the `bug tracker +`_. + +To report a security vulnerability, please use the +`Tidelift security contact `_. +Tidelift will coordinate the fix and disclosure. + + +For Enterprise +============== + +Available as part of the Tidelift Subscription. + +Setuptools and the maintainers of thousands of other packages are working with Tidelift to deliver one enterprise subscription that covers all of the open source you use. + +`Learn more `_. + +Code of Conduct +=============== + +Everyone interacting in the setuptools project's codebases, issue trackers, +chat rooms, and mailing lists is expected to follow the +`PyPA Code of Conduct `_. + + diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/RECORD b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/RECORD new file mode 100644 index 0000000000..9a8d9a7a74 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/RECORD @@ -0,0 +1,164 @@ +../../../bin/easy_install,sha256=bZfWRfeggNsB4BnPtl9BmadK05YJilGICyn70Pba5k4,296 +../../../bin/easy_install-3.9,sha256=bZfWRfeggNsB4BnPtl9BmadK05YJilGICyn70Pba5k4,296 +__pycache__/easy_install.cpython-39.pyc,, +easy_install.py,sha256=MDC9vt5AxDsXX5qcKlBz2TnW6Tpuv_AobnfhCJ9X3PM,126 +setuptools-44.1.1.dist-info/AUTHORS.txt,sha256=ilkpJ4nuW3rRgU3fX4EufclaM4Y7RsZu5uOu0oizmNM,8036 +setuptools-44.1.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +setuptools-44.1.1.dist-info/LICENSE.txt,sha256=gdAS_gPyTUkBTvvgoNNlG9Mv1KFDTig6W1JdeMD2Efg,1090 +setuptools-44.1.1.dist-info/METADATA,sha256=2AxMds4jCrvXAyK2UO21ULfMbAvTyonMZlYm-L9cx7c,3523 +setuptools-44.1.1.dist-info/RECORD,, +setuptools-44.1.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +setuptools-44.1.1.dist-info/WHEEL,sha256=kGT74LWyRUZrL4VgLh6_g12IeVl_9u9ZVhadrgXZUEY,110 +setuptools-44.1.1.dist-info/dependency_links.txt,sha256=HlkCFkoK5TbZ5EMLbLKYhLcY_E31kBWD8TqW2EgmatQ,239 +setuptools-44.1.1.dist-info/entry_points.txt,sha256=ZmIqlp-SBdsBS2cuetmU2NdSOs4DG0kxctUR9UJ8Xk0,3150 +setuptools-44.1.1.dist-info/top_level.txt,sha256=2HUXVVwA4Pff1xgTFr3GsTXXKaPaO6vlG6oNJ_4u4Tg,38 +setuptools-44.1.1.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1 +setuptools/__init__.py,sha256=0SDEgF1acybGfdxJvyjgeAPKC-3-hoYrLKRw8y4YmLQ,7795 +setuptools/__pycache__/__init__.cpython-39.pyc,, +setuptools/__pycache__/_deprecation_warning.cpython-39.pyc,, +setuptools/__pycache__/_imp.cpython-39.pyc,, +setuptools/__pycache__/archive_util.cpython-39.pyc,, +setuptools/__pycache__/build_meta.cpython-39.pyc,, +setuptools/__pycache__/config.cpython-39.pyc,, +setuptools/__pycache__/dep_util.cpython-39.pyc,, +setuptools/__pycache__/depends.cpython-39.pyc,, +setuptools/__pycache__/dist.cpython-39.pyc,, +setuptools/__pycache__/errors.cpython-39.pyc,, +setuptools/__pycache__/extension.cpython-39.pyc,, +setuptools/__pycache__/glob.cpython-39.pyc,, +setuptools/__pycache__/installer.cpython-39.pyc,, +setuptools/__pycache__/launch.cpython-39.pyc,, +setuptools/__pycache__/lib2to3_ex.cpython-39.pyc,, +setuptools/__pycache__/monkey.cpython-39.pyc,, +setuptools/__pycache__/msvc.cpython-39.pyc,, +setuptools/__pycache__/namespaces.cpython-39.pyc,, +setuptools/__pycache__/package_index.cpython-39.pyc,, +setuptools/__pycache__/py27compat.cpython-39.pyc,, +setuptools/__pycache__/py31compat.cpython-39.pyc,, +setuptools/__pycache__/py33compat.cpython-39.pyc,, +setuptools/__pycache__/py34compat.cpython-39.pyc,, +setuptools/__pycache__/sandbox.cpython-39.pyc,, +setuptools/__pycache__/site-patch.cpython-39.pyc,, +setuptools/__pycache__/ssl_support.cpython-39.pyc,, +setuptools/__pycache__/unicode_utils.cpython-39.pyc,, +setuptools/__pycache__/version.cpython-39.pyc,, +setuptools/__pycache__/wheel.cpython-39.pyc,, +setuptools/__pycache__/windows_support.cpython-39.pyc,, +setuptools/_deprecation_warning.py,sha256=jU9-dtfv6cKmtQJOXN8nP1mm7gONw5kKEtiPtbwnZyI,218 +setuptools/_imp.py,sha256=jloslOkxrTKbobgemfP94YII0nhqiJzE1bRmCTZ1a5I,2223 +setuptools/_vendor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +setuptools/_vendor/__pycache__/__init__.cpython-39.pyc,, +setuptools/_vendor/__pycache__/ordered_set.cpython-39.pyc,, +setuptools/_vendor/__pycache__/pyparsing.cpython-39.pyc,, +setuptools/_vendor/__pycache__/six.cpython-39.pyc,, +setuptools/_vendor/ordered_set.py,sha256=dbaCcs27dyN9gnMWGF5nA_BrVn6Q-NrjKYJpV9_fgBs,15130 +setuptools/_vendor/packaging/__about__.py,sha256=CpuMSyh1V7adw8QMjWKkY3LtdqRUkRX4MgJ6nF4stM0,744 +setuptools/_vendor/packaging/__init__.py,sha256=6enbp5XgRfjBjsI9-bn00HjHf5TH21PDMOKkJW8xw-w,562 +setuptools/_vendor/packaging/__pycache__/__about__.cpython-39.pyc,, +setuptools/_vendor/packaging/__pycache__/__init__.cpython-39.pyc,, +setuptools/_vendor/packaging/__pycache__/_compat.cpython-39.pyc,, +setuptools/_vendor/packaging/__pycache__/_structures.cpython-39.pyc,, +setuptools/_vendor/packaging/__pycache__/markers.cpython-39.pyc,, +setuptools/_vendor/packaging/__pycache__/requirements.cpython-39.pyc,, +setuptools/_vendor/packaging/__pycache__/specifiers.cpython-39.pyc,, +setuptools/_vendor/packaging/__pycache__/tags.cpython-39.pyc,, +setuptools/_vendor/packaging/__pycache__/utils.cpython-39.pyc,, +setuptools/_vendor/packaging/__pycache__/version.cpython-39.pyc,, +setuptools/_vendor/packaging/_compat.py,sha256=Ugdm-qcneSchW25JrtMIKgUxfEEBcCAz6WrEeXeqz9o,865 +setuptools/_vendor/packaging/_structures.py,sha256=pVd90XcXRGwpZRB_qdFuVEibhCHpX_bL5zYr9-N0mc8,1416 +setuptools/_vendor/packaging/markers.py,sha256=-meFl9Fr9V8rF5Rduzgett5EHK9wBYRUqssAV2pj0lw,8268 +setuptools/_vendor/packaging/requirements.py,sha256=3dwIJekt8RRGCUbgxX8reeAbgmZYjb0wcCRtmH63kxI,4742 +setuptools/_vendor/packaging/specifiers.py,sha256=0ZzQpcUnvrQ6LjR-mQRLzMr8G6hdRv-mY0VSf_amFtI,27778 +setuptools/_vendor/packaging/tags.py,sha256=EPLXhO6GTD7_oiWEO1U0l0PkfR8R_xivpMDHXnsTlts,12933 +setuptools/_vendor/packaging/utils.py,sha256=VaTC0Ei7zO2xl9ARiWmz2YFLFt89PuuhLbAlXMyAGms,1520 +setuptools/_vendor/packaging/version.py,sha256=Npdwnb8OHedj_2L86yiUqscujb7w_i5gmSK1PhOAFzg,11978 +setuptools/_vendor/pyparsing.py,sha256=tmrp-lu-qO1i75ZzIN5A12nKRRD1Cm4Vpk-5LR9rims,232055 +setuptools/_vendor/six.py,sha256=A6hdJZVjI3t_geebZ9BzUvwRrIXo0lfwzQlM2LcKyas,30098 +setuptools/archive_util.py,sha256=kw8Ib_lKjCcnPKNbS7h8HztRVK0d5RacU3r_KRdVnmM,6592 +setuptools/build_meta.py,sha256=MQWILThG6texTPLol6icwM83h8V8TLbg0QCacFRt33k,9887 +setuptools/cli-32.exe,sha256=dfEuovMNnA2HLa3jRfMPVi5tk4R7alCbpTvuxtCyw0Y,65536 +setuptools/cli-64.exe,sha256=KLABu5pyrnokJCv6skjXZ6GsXeyYHGcqOUT3oHI3Xpo,74752 +setuptools/cli.exe,sha256=dfEuovMNnA2HLa3jRfMPVi5tk4R7alCbpTvuxtCyw0Y,65536 +setuptools/command/__init__.py,sha256=QCAuA9whnq8Bnoc0bBaS6Lw_KAUO0DiHYZQXEMNn5hg,568 +setuptools/command/__pycache__/__init__.cpython-39.pyc,, +setuptools/command/__pycache__/alias.cpython-39.pyc,, +setuptools/command/__pycache__/bdist_egg.cpython-39.pyc,, +setuptools/command/__pycache__/bdist_rpm.cpython-39.pyc,, +setuptools/command/__pycache__/bdist_wininst.cpython-39.pyc,, +setuptools/command/__pycache__/build_clib.cpython-39.pyc,, +setuptools/command/__pycache__/build_ext.cpython-39.pyc,, +setuptools/command/__pycache__/build_py.cpython-39.pyc,, +setuptools/command/__pycache__/develop.cpython-39.pyc,, +setuptools/command/__pycache__/dist_info.cpython-39.pyc,, +setuptools/command/__pycache__/easy_install.cpython-39.pyc,, +setuptools/command/__pycache__/egg_info.cpython-39.pyc,, +setuptools/command/__pycache__/install.cpython-39.pyc,, +setuptools/command/__pycache__/install_egg_info.cpython-39.pyc,, +setuptools/command/__pycache__/install_lib.cpython-39.pyc,, +setuptools/command/__pycache__/install_scripts.cpython-39.pyc,, +setuptools/command/__pycache__/py36compat.cpython-39.pyc,, +setuptools/command/__pycache__/register.cpython-39.pyc,, +setuptools/command/__pycache__/rotate.cpython-39.pyc,, +setuptools/command/__pycache__/saveopts.cpython-39.pyc,, +setuptools/command/__pycache__/sdist.cpython-39.pyc,, +setuptools/command/__pycache__/setopt.cpython-39.pyc,, +setuptools/command/__pycache__/test.cpython-39.pyc,, +setuptools/command/__pycache__/upload.cpython-39.pyc,, +setuptools/command/__pycache__/upload_docs.cpython-39.pyc,, +setuptools/command/alias.py,sha256=KjpE0sz_SDIHv3fpZcIQK-sCkJz-SrC6Gmug6b9Nkc8,2426 +setuptools/command/bdist_egg.py,sha256=nnfV8Ah8IRC_Ifv5Loa9FdxL66MVbyDXwy-foP810zM,18185 +setuptools/command/bdist_rpm.py,sha256=B7l0TnzCGb-0nLlm6rS00jWLkojASwVmdhW2w5Qz_Ak,1508 +setuptools/command/bdist_wininst.py,sha256=_6dz3lpB1tY200LxKPLM7qgwTCceOMgaWFF-jW2-pm0,637 +setuptools/command/build_clib.py,sha256=bQ9aBr-5ZSO-9fGsGsDLz0mnnFteHUZnftVLkhvHDq0,4484 +setuptools/command/build_ext.py,sha256=8k4kJcOp_ZMxZ1sZYmle5_OAtNYLXGjrbWOj-IPjvwY,13023 +setuptools/command/build_py.py,sha256=yWyYaaS9F3o9JbIczn064A5g1C5_UiKRDxGaTqYbtLE,9596 +setuptools/command/develop.py,sha256=B3-ImHP30Bxnqx-s_9Dp-fxtHhE245ACE1W5hmKY9xE,8188 +setuptools/command/dist_info.py,sha256=5t6kOfrdgALT-P3ogss6PF9k-Leyesueycuk3dUyZnI,960 +setuptools/command/easy_install.py,sha256=QU5oxbZC1COqfiBk3m39yB__bG4YwDtWoNEC8GKFyHo,89907 +setuptools/command/egg_info.py,sha256=dnFO_LBQFIZHCfX9_ke_Mtc6s78CrKIpqeiqlFASB0w,25582 +setuptools/command/install.py,sha256=8doMxeQEDoK4Eco0mO2WlXXzzp9QnsGJQ7Z7yWkZPG8,4705 +setuptools/command/install_egg_info.py,sha256=4zq_Ad3jE-EffParuyDEnvxU6efB-Xhrzdr8aB6Ln_8,3195 +setuptools/command/install_lib.py,sha256=9zdc-H5h6RPxjySRhOwi30E_WfcVva7gpfhZ5ata60w,5023 +setuptools/command/install_scripts.py,sha256=UD0rEZ6861mTYhIdzcsqKnUl8PozocXWl9VBQ1VTWnc,2439 +setuptools/command/launcher manifest.xml,sha256=xlLbjWrB01tKC0-hlVkOKkiSPbzMml2eOPtJ_ucCnbE,628 +setuptools/command/py36compat.py,sha256=SzjZcOxF7zdFUT47Zv2n7AM3H8koDys_0OpS-n9gIfc,4986 +setuptools/command/register.py,sha256=kk3DxXCb5lXTvqnhfwx2g6q7iwbUmgTyXUCaBooBOUk,468 +setuptools/command/rotate.py,sha256=co5C1EkI7P0GGT6Tqz-T2SIj2LBJTZXYELpmao6d4KQ,2164 +setuptools/command/saveopts.py,sha256=za7QCBcQimKKriWcoCcbhxPjUz30gSB74zuTL47xpP4,658 +setuptools/command/sdist.py,sha256=14kBw_QOZ9L_RQDqgf9DAlEuoj0zC30X5mfDWeiyZwU,8092 +setuptools/command/setopt.py,sha256=NTWDyx-gjDF-txf4dO577s7LOzHVoKR0Mq33rFxaRr8,5085 +setuptools/command/test.py,sha256=MahF7jRBGYwgM-fdPBLyBpfCqR5ZXSGexPVR-afV3Vc,9610 +setuptools/command/upload.py,sha256=XT3YFVfYPAmA5qhGg0euluU98ftxRUW-PzKcODMLxUs,462 +setuptools/command/upload_docs.py,sha256=O137bN9dt_sELevf4vwuwWRkogHf4bPXc-jNRxVQaiw,7315 +setuptools/config.py,sha256=6SB2OY3qcooOJmG_rsK_s0pKBsorBlDpfMJUyzjQIGk,20575 +setuptools/dep_util.py,sha256=fgixvC1R7sH3r13ktyf7N0FALoqEXL1cBarmNpSEoWg,935 +setuptools/depends.py,sha256=qt2RWllArRvhnm8lxsyRpcthEZYp4GHQgREl1q0LkFw,5517 +setuptools/dist.py,sha256=WiypLfe-bt358W15a53L-KoO_35xK55EXPbmuTtgBYo,49885 +setuptools/errors.py,sha256=MVOcv381HNSajDgEUWzOQ4J6B5BHCBMSjHfaWcEwA1o,524 +setuptools/extension.py,sha256=uc6nHI-MxwmNCNPbUiBnybSyqhpJqjbhvOQ-emdvt_E,1729 +setuptools/extern/__init__.py,sha256=4q9gtShB1XFP6CisltsyPqtcfTO6ZM9Lu1QBl3l-qmo,2514 +setuptools/extern/__pycache__/__init__.cpython-39.pyc,, +setuptools/glob.py,sha256=o75cHrOxYsvn854thSxE0x9k8JrKDuhP_rRXlVB00Q4,5084 +setuptools/gui-32.exe,sha256=XBr0bHMA6Hpz2s9s9Bzjl-PwXfa9nH4ie0rFn4V2kWA,65536 +setuptools/gui-64.exe,sha256=aYKMhX1IJLn4ULHgWX0sE0yREUt6B3TEHf_jOw6yNyE,75264 +setuptools/gui.exe,sha256=XBr0bHMA6Hpz2s9s9Bzjl-PwXfa9nH4ie0rFn4V2kWA,65536 +setuptools/installer.py,sha256=TCFRonRo01I79zo-ucf3Ymhj8TenPlmhMijN916aaJs,5337 +setuptools/launch.py,sha256=sd7ejwhBocCDx_wG9rIs0OaZ8HtmmFU8ZC6IR_S0Lvg,787 +setuptools/lib2to3_ex.py,sha256=t5e12hbR2pi9V4ezWDTB4JM-AISUnGOkmcnYHek3xjg,2013 +setuptools/monkey.py,sha256=FGc9fffh7gAxMLFmJs2DW_OYWpBjkdbNS2n14UAK4NA,5264 +setuptools/msvc.py,sha256=8baJ6aYgCA4TRdWQQi185qB9dnU8FaP4wgpbmd7VODs,46751 +setuptools/namespaces.py,sha256=F0Nrbv8KCT2OrO7rwa03om4N4GZKAlnce-rr-cgDQa8,3199 +setuptools/package_index.py,sha256=6pb-B1POtHyLycAbkDETk4fO-Qv8_sY-rjTXhUOoh6k,40605 +setuptools/py27compat.py,sha256=tvmer0Tn-wk_JummCkoM22UIjpjL-AQ8uUiOaqTs8sI,1496 +setuptools/py31compat.py,sha256=h2rtZghOfwoGYd8sQ0-auaKiF3TcL3qX0bX3VessqcE,838 +setuptools/py33compat.py,sha256=SMF9Z8wnGicTOkU1uRNwZ_kz5Z_bj29PUBbqdqeeNsc,1330 +setuptools/py34compat.py,sha256=KYOd6ybRxjBW8NJmYD8t_UyyVmysppFXqHpFLdslGXU,245 +setuptools/sandbox.py,sha256=9UbwfEL5QY436oMI1LtFWohhoZ-UzwHvGyZjUH_qhkw,14276 +setuptools/script (dev).tmpl,sha256=RUzQzCQUaXtwdLtYHWYbIQmOaES5Brqq1FvUA_tu-5I,218 +setuptools/script.tmpl,sha256=WGTt5piezO27c-Dbx6l5Q4T3Ff20A5z7872hv3aAhYY,138 +setuptools/site-patch.py,sha256=OumkIHMuoSenRSW1382kKWI1VAwxNE86E5W8iDd34FY,2302 +setuptools/ssl_support.py,sha256=nLjPUBBw7RTTx6O4RJZ5eAMGgjJG8beiDbkFXDZpLuM,8493 +setuptools/unicode_utils.py,sha256=NOiZ_5hD72A6w-4wVj8awHFM3n51Kmw1Ic_vx15XFqw,996 +setuptools/version.py,sha256=og_cuZQb0QI6ukKZFfZWPlr1HgJBPPn2vO2m_bI9ZTE,144 +setuptools/wheel.py,sha256=zct-SEj5_LoHg6XELt2cVRdulsUENenCdS1ekM7TlZA,8455 +setuptools/windows_support.py,sha256=5GrfqSP2-dLGJoZTq2g6dCKkyQxxa2n5IQiXlJCoYEE,714 diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/REQUESTED b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/REQUESTED new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/WHEEL b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/WHEEL new file mode 100644 index 0000000000..ef99c6cf32 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.34.2) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/dependency_links.txt b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/dependency_links.txt new file mode 100644 index 0000000000..e87d02103e --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/dependency_links.txt @@ -0,0 +1,2 @@ +https://files.pythonhosted.org/packages/source/c/certifi/certifi-2016.9.26.tar.gz#md5=baa81e951a29958563689d868ef1064d +https://files.pythonhosted.org/packages/source/w/wincertstore/wincertstore-0.2.zip#md5=ae728f2f007185648d0c7a8679b361e2 diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/entry_points.txt b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/entry_points.txt new file mode 100644 index 0000000000..0fed3f1d83 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/entry_points.txt @@ -0,0 +1,68 @@ +[console_scripts] +easy_install = setuptools.command.easy_install:main + +[distutils.commands] +alias = setuptools.command.alias:alias +bdist_egg = setuptools.command.bdist_egg:bdist_egg +bdist_rpm = setuptools.command.bdist_rpm:bdist_rpm +bdist_wininst = setuptools.command.bdist_wininst:bdist_wininst +build_clib = setuptools.command.build_clib:build_clib +build_ext = setuptools.command.build_ext:build_ext +build_py = setuptools.command.build_py:build_py +develop = setuptools.command.develop:develop +dist_info = setuptools.command.dist_info:dist_info +easy_install = setuptools.command.easy_install:easy_install +egg_info = setuptools.command.egg_info:egg_info +install = setuptools.command.install:install +install_egg_info = setuptools.command.install_egg_info:install_egg_info +install_lib = setuptools.command.install_lib:install_lib +install_scripts = setuptools.command.install_scripts:install_scripts +rotate = setuptools.command.rotate:rotate +saveopts = setuptools.command.saveopts:saveopts +sdist = setuptools.command.sdist:sdist +setopt = setuptools.command.setopt:setopt +test = setuptools.command.test:test +upload_docs = setuptools.command.upload_docs:upload_docs + +[distutils.setup_keywords] +convert_2to3_doctests = setuptools.dist:assert_string_list +dependency_links = setuptools.dist:assert_string_list +eager_resources = setuptools.dist:assert_string_list +entry_points = setuptools.dist:check_entry_points +exclude_package_data = setuptools.dist:check_package_data +extras_require = setuptools.dist:check_extras +include_package_data = setuptools.dist:assert_bool +install_requires = setuptools.dist:check_requirements +namespace_packages = setuptools.dist:check_nsp +package_data = setuptools.dist:check_package_data +packages = setuptools.dist:check_packages +python_requires = setuptools.dist:check_specifier +setup_requires = setuptools.dist:check_requirements +test_loader = setuptools.dist:check_importable +test_runner = setuptools.dist:check_importable +test_suite = setuptools.dist:check_test_suite +tests_require = setuptools.dist:check_requirements +use_2to3 = setuptools.dist:assert_bool +use_2to3_exclude_fixers = setuptools.dist:assert_string_list +use_2to3_fixers = setuptools.dist:assert_string_list +zip_safe = setuptools.dist:assert_bool + +[egg_info.writers] +PKG-INFO = setuptools.command.egg_info:write_pkg_info +dependency_links.txt = setuptools.command.egg_info:overwrite_arg +depends.txt = setuptools.command.egg_info:warn_depends_obsolete +eager_resources.txt = setuptools.command.egg_info:overwrite_arg +entry_points.txt = setuptools.command.egg_info:write_entries +namespace_packages.txt = setuptools.command.egg_info:overwrite_arg +requires.txt = setuptools.command.egg_info:write_requirements +top_level.txt = setuptools.command.egg_info:write_toplevel_names + +[setuptools.finalize_distribution_options] +2to3_doctests = setuptools.dist:Distribution._finalize_2to3_doctests +features = setuptools.dist:Distribution._finalize_feature_opts +keywords = setuptools.dist:Distribution._finalize_setup_keywords +parent_finalize = setuptools.dist:_Distribution.finalize_options + +[setuptools.installation] +eggsecutable = setuptools.command.easy_install:bootstrap + diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/top_level.txt b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/top_level.txt new file mode 100644 index 0000000000..4577c6a795 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/top_level.txt @@ -0,0 +1,3 @@ +easy_install +pkg_resources +setuptools diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/zip-safe b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/zip-safe new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/zip-safe @@ -0,0 +1 @@ + diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/__init__.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/__init__.py new file mode 100644 index 0000000000..9d8ae1ed5f --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/__init__.py @@ -0,0 +1,245 @@ +"""Extensions to the 'distutils' for large or complex distributions""" + +import os +import sys +import functools +import distutils.core +import distutils.filelist +import re +from distutils.errors import DistutilsOptionError +from distutils.util import convert_path +from fnmatch import fnmatchcase + +from ._deprecation_warning import SetuptoolsDeprecationWarning + +from setuptools.extern.six import PY3, string_types +from setuptools.extern.six.moves import filter, map + +import setuptools.version +from setuptools.extension import Extension +from setuptools.dist import Distribution, Feature +from setuptools.depends import Require +from . import monkey + +__metaclass__ = type + + +__all__ = [ + 'setup', 'Distribution', 'Feature', 'Command', 'Extension', 'Require', + 'SetuptoolsDeprecationWarning', + 'find_packages' +] + +if PY3: + __all__.append('find_namespace_packages') + +__version__ = setuptools.version.__version__ + +bootstrap_install_from = None + +# If we run 2to3 on .py files, should we also convert docstrings? +# Default: yes; assume that we can detect doctests reliably +run_2to3_on_doctests = True +# Standard package names for fixer packages +lib2to3_fixer_packages = ['lib2to3.fixes'] + + +class PackageFinder: + """ + Generate a list of all Python packages found within a directory + """ + + @classmethod + def find(cls, where='.', exclude=(), include=('*',)): + """Return a list all Python packages found within directory 'where' + + 'where' is the root directory which will be searched for packages. It + should be supplied as a "cross-platform" (i.e. URL-style) path; it will + be converted to the appropriate local path syntax. + + 'exclude' is a sequence of package names to exclude; '*' can be used + as a wildcard in the names, such that 'foo.*' will exclude all + subpackages of 'foo' (but not 'foo' itself). + + 'include' is a sequence of package names to include. If it's + specified, only the named packages will be included. If it's not + specified, all found packages will be included. 'include' can contain + shell style wildcard patterns just like 'exclude'. + """ + + return list(cls._find_packages_iter( + convert_path(where), + cls._build_filter('ez_setup', '*__pycache__', *exclude), + cls._build_filter(*include))) + + @classmethod + def _find_packages_iter(cls, where, exclude, include): + """ + All the packages found in 'where' that pass the 'include' filter, but + not the 'exclude' filter. + """ + for root, dirs, files in os.walk(where, followlinks=True): + # Copy dirs to iterate over it, then empty dirs. + all_dirs = dirs[:] + dirs[:] = [] + + for dir in all_dirs: + full_path = os.path.join(root, dir) + rel_path = os.path.relpath(full_path, where) + package = rel_path.replace(os.path.sep, '.') + + # Skip directory trees that are not valid packages + if ('.' in dir or not cls._looks_like_package(full_path)): + continue + + # Should this package be included? + if include(package) and not exclude(package): + yield package + + # Keep searching subdirectories, as there may be more packages + # down there, even if the parent was excluded. + dirs.append(dir) + + @staticmethod + def _looks_like_package(path): + """Does a directory look like a package?""" + return os.path.isfile(os.path.join(path, '__init__.py')) + + @staticmethod + def _build_filter(*patterns): + """ + Given a list of patterns, return a callable that will be true only if + the input matches at least one of the patterns. + """ + return lambda name: any(fnmatchcase(name, pat=pat) for pat in patterns) + + +class PEP420PackageFinder(PackageFinder): + @staticmethod + def _looks_like_package(path): + return True + + +find_packages = PackageFinder.find + +if PY3: + find_namespace_packages = PEP420PackageFinder.find + + +def _install_setup_requires(attrs): + # Note: do not use `setuptools.Distribution` directly, as + # our PEP 517 backend patch `distutils.core.Distribution`. + class MinimalDistribution(distutils.core.Distribution): + """ + A minimal version of a distribution for supporting the + fetch_build_eggs interface. + """ + def __init__(self, attrs): + _incl = 'dependency_links', 'setup_requires' + filtered = { + k: attrs[k] + for k in set(_incl) & set(attrs) + } + distutils.core.Distribution.__init__(self, filtered) + + def finalize_options(self): + """ + Disable finalize_options to avoid building the working set. + Ref #2158. + """ + + dist = MinimalDistribution(attrs) + + # Honor setup.cfg's options. + dist.parse_config_files(ignore_option_errors=True) + if dist.setup_requires: + dist.fetch_build_eggs(dist.setup_requires) + + +def setup(**attrs): + # Make sure we have any requirements needed to interpret 'attrs'. + _install_setup_requires(attrs) + return distutils.core.setup(**attrs) + +setup.__doc__ = distutils.core.setup.__doc__ + + +_Command = monkey.get_unpatched(distutils.core.Command) + + +class Command(_Command): + __doc__ = _Command.__doc__ + + command_consumes_arguments = False + + def __init__(self, dist, **kw): + """ + Construct the command for dist, updating + vars(self) with any keyword parameters. + """ + _Command.__init__(self, dist) + vars(self).update(kw) + + def _ensure_stringlike(self, option, what, default=None): + val = getattr(self, option) + if val is None: + setattr(self, option, default) + return default + elif not isinstance(val, string_types): + raise DistutilsOptionError("'%s' must be a %s (got `%s`)" + % (option, what, val)) + return val + + def ensure_string_list(self, option): + r"""Ensure that 'option' is a list of strings. If 'option' is + currently a string, we split it either on /,\s*/ or /\s+/, so + "foo bar baz", "foo,bar,baz", and "foo, bar baz" all become + ["foo", "bar", "baz"]. + """ + val = getattr(self, option) + if val is None: + return + elif isinstance(val, string_types): + setattr(self, option, re.split(r',\s*|\s+', val)) + else: + if isinstance(val, list): + ok = all(isinstance(v, string_types) for v in val) + else: + ok = False + if not ok: + raise DistutilsOptionError( + "'%s' must be a list of strings (got %r)" + % (option, val)) + + def reinitialize_command(self, command, reinit_subcommands=0, **kw): + cmd = _Command.reinitialize_command(self, command, reinit_subcommands) + vars(cmd).update(kw) + return cmd + + +def _find_all_simple(path): + """ + Find all files under 'path' + """ + results = ( + os.path.join(base, file) + for base, dirs, files in os.walk(path, followlinks=True) + for file in files + ) + return filter(os.path.isfile, results) + + +def findall(dir=os.curdir): + """ + Find all files under 'dir' and return the list of full filenames. + Unless dir is '.', return full filenames with dir prepended. + """ + files = _find_all_simple(dir) + if dir == os.curdir: + make_rel = functools.partial(os.path.relpath, start=dir) + files = map(make_rel, files) + return list(files) + + +# Apply monkey patches +monkey.patch_all() diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_deprecation_warning.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_deprecation_warning.py new file mode 100644 index 0000000000..086b64dd38 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_deprecation_warning.py @@ -0,0 +1,7 @@ +class SetuptoolsDeprecationWarning(Warning): + """ + Base class for warning deprecations in ``setuptools`` + + This class is not derived from ``DeprecationWarning``, and as such is + visible by default. + """ diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_imp.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_imp.py new file mode 100644 index 0000000000..a3cce9b284 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_imp.py @@ -0,0 +1,73 @@ +""" +Re-implementation of find_module and get_frozen_object +from the deprecated imp module. +""" + +import os +import importlib.util +import importlib.machinery + +from .py34compat import module_from_spec + + +PY_SOURCE = 1 +PY_COMPILED = 2 +C_EXTENSION = 3 +C_BUILTIN = 6 +PY_FROZEN = 7 + + +def find_module(module, paths=None): + """Just like 'imp.find_module()', but with package support""" + spec = importlib.util.find_spec(module, paths) + if spec is None: + raise ImportError("Can't find %s" % module) + if not spec.has_location and hasattr(spec, 'submodule_search_locations'): + spec = importlib.util.spec_from_loader('__init__.py', spec.loader) + + kind = -1 + file = None + static = isinstance(spec.loader, type) + if spec.origin == 'frozen' or static and issubclass( + spec.loader, importlib.machinery.FrozenImporter): + kind = PY_FROZEN + path = None # imp compabilty + suffix = mode = '' # imp compability + elif spec.origin == 'built-in' or static and issubclass( + spec.loader, importlib.machinery.BuiltinImporter): + kind = C_BUILTIN + path = None # imp compabilty + suffix = mode = '' # imp compability + elif spec.has_location: + path = spec.origin + suffix = os.path.splitext(path)[1] + mode = 'r' if suffix in importlib.machinery.SOURCE_SUFFIXES else 'rb' + + if suffix in importlib.machinery.SOURCE_SUFFIXES: + kind = PY_SOURCE + elif suffix in importlib.machinery.BYTECODE_SUFFIXES: + kind = PY_COMPILED + elif suffix in importlib.machinery.EXTENSION_SUFFIXES: + kind = C_EXTENSION + + if kind in {PY_SOURCE, PY_COMPILED}: + file = open(path, mode) + else: + path = None + suffix = mode = '' + + return file, path, (suffix, mode, kind) + + +def get_frozen_object(module, paths=None): + spec = importlib.util.find_spec(module, paths) + if not spec: + raise ImportError("Can't find %s" % module) + return spec.loader.get_code(module) + + +def get_module(module, paths, info): + spec = importlib.util.find_spec(module, paths) + if not spec: + raise ImportError("Can't find %s" % module) + return module_from_spec(spec) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/__init__.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/ordered_set.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/ordered_set.py new file mode 100644 index 0000000000..14876000de --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/ordered_set.py @@ -0,0 +1,488 @@ +""" +An OrderedSet is a custom MutableSet that remembers its order, so that every +entry has an index that can be looked up. + +Based on a recipe originally posted to ActiveState Recipes by Raymond Hettiger, +and released under the MIT license. +""" +import itertools as it +from collections import deque + +try: + # Python 3 + from collections.abc import MutableSet, Sequence +except ImportError: + # Python 2.7 + from collections import MutableSet, Sequence + +SLICE_ALL = slice(None) +__version__ = "3.1" + + +def is_iterable(obj): + """ + Are we being asked to look up a list of things, instead of a single thing? + We check for the `__iter__` attribute so that this can cover types that + don't have to be known by this module, such as NumPy arrays. + + Strings, however, should be considered as atomic values to look up, not + iterables. The same goes for tuples, since they are immutable and therefore + valid entries. + + We don't need to check for the Python 2 `unicode` type, because it doesn't + have an `__iter__` attribute anyway. + """ + return ( + hasattr(obj, "__iter__") + and not isinstance(obj, str) + and not isinstance(obj, tuple) + ) + + +class OrderedSet(MutableSet, Sequence): + """ + An OrderedSet is a custom MutableSet that remembers its order, so that + every entry has an index that can be looked up. + + Example: + >>> OrderedSet([1, 1, 2, 3, 2]) + OrderedSet([1, 2, 3]) + """ + + def __init__(self, iterable=None): + self.items = [] + self.map = {} + if iterable is not None: + self |= iterable + + def __len__(self): + """ + Returns the number of unique elements in the ordered set + + Example: + >>> len(OrderedSet([])) + 0 + >>> len(OrderedSet([1, 2])) + 2 + """ + return len(self.items) + + def __getitem__(self, index): + """ + Get the item at a given index. + + If `index` is a slice, you will get back that slice of items, as a + new OrderedSet. + + If `index` is a list or a similar iterable, you'll get a list of + items corresponding to those indices. This is similar to NumPy's + "fancy indexing". The result is not an OrderedSet because you may ask + for duplicate indices, and the number of elements returned should be + the number of elements asked for. + + Example: + >>> oset = OrderedSet([1, 2, 3]) + >>> oset[1] + 2 + """ + if isinstance(index, slice) and index == SLICE_ALL: + return self.copy() + elif is_iterable(index): + return [self.items[i] for i in index] + elif hasattr(index, "__index__") or isinstance(index, slice): + result = self.items[index] + if isinstance(result, list): + return self.__class__(result) + else: + return result + else: + raise TypeError("Don't know how to index an OrderedSet by %r" % index) + + def copy(self): + """ + Return a shallow copy of this object. + + Example: + >>> this = OrderedSet([1, 2, 3]) + >>> other = this.copy() + >>> this == other + True + >>> this is other + False + """ + return self.__class__(self) + + def __getstate__(self): + if len(self) == 0: + # The state can't be an empty list. + # We need to return a truthy value, or else __setstate__ won't be run. + # + # This could have been done more gracefully by always putting the state + # in a tuple, but this way is backwards- and forwards- compatible with + # previous versions of OrderedSet. + return (None,) + else: + return list(self) + + def __setstate__(self, state): + if state == (None,): + self.__init__([]) + else: + self.__init__(state) + + def __contains__(self, key): + """ + Test if the item is in this ordered set + + Example: + >>> 1 in OrderedSet([1, 3, 2]) + True + >>> 5 in OrderedSet([1, 3, 2]) + False + """ + return key in self.map + + def add(self, key): + """ + Add `key` as an item to this OrderedSet, then return its index. + + If `key` is already in the OrderedSet, return the index it already + had. + + Example: + >>> oset = OrderedSet() + >>> oset.append(3) + 0 + >>> print(oset) + OrderedSet([3]) + """ + if key not in self.map: + self.map[key] = len(self.items) + self.items.append(key) + return self.map[key] + + append = add + + def update(self, sequence): + """ + Update the set with the given iterable sequence, then return the index + of the last element inserted. + + Example: + >>> oset = OrderedSet([1, 2, 3]) + >>> oset.update([3, 1, 5, 1, 4]) + 4 + >>> print(oset) + OrderedSet([1, 2, 3, 5, 4]) + """ + item_index = None + try: + for item in sequence: + item_index = self.add(item) + except TypeError: + raise ValueError( + "Argument needs to be an iterable, got %s" % type(sequence) + ) + return item_index + + def index(self, key): + """ + Get the index of a given entry, raising an IndexError if it's not + present. + + `key` can be an iterable of entries that is not a string, in which case + this returns a list of indices. + + Example: + >>> oset = OrderedSet([1, 2, 3]) + >>> oset.index(2) + 1 + """ + if is_iterable(key): + return [self.index(subkey) for subkey in key] + return self.map[key] + + # Provide some compatibility with pd.Index + get_loc = index + get_indexer = index + + def pop(self): + """ + Remove and return the last element from the set. + + Raises KeyError if the set is empty. + + Example: + >>> oset = OrderedSet([1, 2, 3]) + >>> oset.pop() + 3 + """ + if not self.items: + raise KeyError("Set is empty") + + elem = self.items[-1] + del self.items[-1] + del self.map[elem] + return elem + + def discard(self, key): + """ + Remove an element. Do not raise an exception if absent. + + The MutableSet mixin uses this to implement the .remove() method, which + *does* raise an error when asked to remove a non-existent item. + + Example: + >>> oset = OrderedSet([1, 2, 3]) + >>> oset.discard(2) + >>> print(oset) + OrderedSet([1, 3]) + >>> oset.discard(2) + >>> print(oset) + OrderedSet([1, 3]) + """ + if key in self: + i = self.map[key] + del self.items[i] + del self.map[key] + for k, v in self.map.items(): + if v >= i: + self.map[k] = v - 1 + + def clear(self): + """ + Remove all items from this OrderedSet. + """ + del self.items[:] + self.map.clear() + + def __iter__(self): + """ + Example: + >>> list(iter(OrderedSet([1, 2, 3]))) + [1, 2, 3] + """ + return iter(self.items) + + def __reversed__(self): + """ + Example: + >>> list(reversed(OrderedSet([1, 2, 3]))) + [3, 2, 1] + """ + return reversed(self.items) + + def __repr__(self): + if not self: + return "%s()" % (self.__class__.__name__,) + return "%s(%r)" % (self.__class__.__name__, list(self)) + + def __eq__(self, other): + """ + Returns true if the containers have the same items. If `other` is a + Sequence, then order is checked, otherwise it is ignored. + + Example: + >>> oset = OrderedSet([1, 3, 2]) + >>> oset == [1, 3, 2] + True + >>> oset == [1, 2, 3] + False + >>> oset == [2, 3] + False + >>> oset == OrderedSet([3, 2, 1]) + False + """ + # In Python 2 deque is not a Sequence, so treat it as one for + # consistent behavior with Python 3. + if isinstance(other, (Sequence, deque)): + # Check that this OrderedSet contains the same elements, in the + # same order, as the other object. + return list(self) == list(other) + try: + other_as_set = set(other) + except TypeError: + # If `other` can't be converted into a set, it's not equal. + return False + else: + return set(self) == other_as_set + + def union(self, *sets): + """ + Combines all unique items. + Each items order is defined by its first appearance. + + Example: + >>> oset = OrderedSet.union(OrderedSet([3, 1, 4, 1, 5]), [1, 3], [2, 0]) + >>> print(oset) + OrderedSet([3, 1, 4, 5, 2, 0]) + >>> oset.union([8, 9]) + OrderedSet([3, 1, 4, 5, 2, 0, 8, 9]) + >>> oset | {10} + OrderedSet([3, 1, 4, 5, 2, 0, 10]) + """ + cls = self.__class__ if isinstance(self, OrderedSet) else OrderedSet + containers = map(list, it.chain([self], sets)) + items = it.chain.from_iterable(containers) + return cls(items) + + def __and__(self, other): + # the parent implementation of this is backwards + return self.intersection(other) + + def intersection(self, *sets): + """ + Returns elements in common between all sets. Order is defined only + by the first set. + + Example: + >>> oset = OrderedSet.intersection(OrderedSet([0, 1, 2, 3]), [1, 2, 3]) + >>> print(oset) + OrderedSet([1, 2, 3]) + >>> oset.intersection([2, 4, 5], [1, 2, 3, 4]) + OrderedSet([2]) + >>> oset.intersection() + OrderedSet([1, 2, 3]) + """ + cls = self.__class__ if isinstance(self, OrderedSet) else OrderedSet + if sets: + common = set.intersection(*map(set, sets)) + items = (item for item in self if item in common) + else: + items = self + return cls(items) + + def difference(self, *sets): + """ + Returns all elements that are in this set but not the others. + + Example: + >>> OrderedSet([1, 2, 3]).difference(OrderedSet([2])) + OrderedSet([1, 3]) + >>> OrderedSet([1, 2, 3]).difference(OrderedSet([2]), OrderedSet([3])) + OrderedSet([1]) + >>> OrderedSet([1, 2, 3]) - OrderedSet([2]) + OrderedSet([1, 3]) + >>> OrderedSet([1, 2, 3]).difference() + OrderedSet([1, 2, 3]) + """ + cls = self.__class__ + if sets: + other = set.union(*map(set, sets)) + items = (item for item in self if item not in other) + else: + items = self + return cls(items) + + def issubset(self, other): + """ + Report whether another set contains this set. + + Example: + >>> OrderedSet([1, 2, 3]).issubset({1, 2}) + False + >>> OrderedSet([1, 2, 3]).issubset({1, 2, 3, 4}) + True + >>> OrderedSet([1, 2, 3]).issubset({1, 4, 3, 5}) + False + """ + if len(self) > len(other): # Fast check for obvious cases + return False + return all(item in other for item in self) + + def issuperset(self, other): + """ + Report whether this set contains another set. + + Example: + >>> OrderedSet([1, 2]).issuperset([1, 2, 3]) + False + >>> OrderedSet([1, 2, 3, 4]).issuperset({1, 2, 3}) + True + >>> OrderedSet([1, 4, 3, 5]).issuperset({1, 2, 3}) + False + """ + if len(self) < len(other): # Fast check for obvious cases + return False + return all(item in self for item in other) + + def symmetric_difference(self, other): + """ + Return the symmetric difference of two OrderedSets as a new set. + That is, the new set will contain all elements that are in exactly + one of the sets. + + Their order will be preserved, with elements from `self` preceding + elements from `other`. + + Example: + >>> this = OrderedSet([1, 4, 3, 5, 7]) + >>> other = OrderedSet([9, 7, 1, 3, 2]) + >>> this.symmetric_difference(other) + OrderedSet([4, 5, 9, 2]) + """ + cls = self.__class__ if isinstance(self, OrderedSet) else OrderedSet + diff1 = cls(self).difference(other) + diff2 = cls(other).difference(self) + return diff1.union(diff2) + + def _update_items(self, items): + """ + Replace the 'items' list of this OrderedSet with a new one, updating + self.map accordingly. + """ + self.items = items + self.map = {item: idx for (idx, item) in enumerate(items)} + + def difference_update(self, *sets): + """ + Update this OrderedSet to remove items from one or more other sets. + + Example: + >>> this = OrderedSet([1, 2, 3]) + >>> this.difference_update(OrderedSet([2, 4])) + >>> print(this) + OrderedSet([1, 3]) + + >>> this = OrderedSet([1, 2, 3, 4, 5]) + >>> this.difference_update(OrderedSet([2, 4]), OrderedSet([1, 4, 6])) + >>> print(this) + OrderedSet([3, 5]) + """ + items_to_remove = set() + for other in sets: + items_to_remove |= set(other) + self._update_items([item for item in self.items if item not in items_to_remove]) + + def intersection_update(self, other): + """ + Update this OrderedSet to keep only items in another set, preserving + their order in this set. + + Example: + >>> this = OrderedSet([1, 4, 3, 5, 7]) + >>> other = OrderedSet([9, 7, 1, 3, 2]) + >>> this.intersection_update(other) + >>> print(this) + OrderedSet([1, 3, 7]) + """ + other = set(other) + self._update_items([item for item in self.items if item in other]) + + def symmetric_difference_update(self, other): + """ + Update this OrderedSet to remove items from another set, then + add items from the other set that were not present in this set. + + Example: + >>> this = OrderedSet([1, 4, 3, 5, 7]) + >>> other = OrderedSet([9, 7, 1, 3, 2]) + >>> this.symmetric_difference_update(other) + >>> print(this) + OrderedSet([4, 5, 9, 2]) + """ + items_to_add = [item for item in other if item not in self] + items_to_remove = set(other) + self._update_items( + [item for item in self.items if item not in items_to_remove] + items_to_add + ) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/packaging/__about__.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/packaging/__about__.py new file mode 100644 index 0000000000..dc95138d04 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/packaging/__about__.py @@ -0,0 +1,27 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +__all__ = [ + "__title__", + "__summary__", + "__uri__", + "__version__", + "__author__", + "__email__", + "__license__", + "__copyright__", +] + +__title__ = "packaging" +__summary__ = "Core utilities for Python packages" +__uri__ = "https://github.com/pypa/packaging" + +__version__ = "19.2" + +__author__ = "Donald Stufft and individual contributors" +__email__ = "donald@stufft.io" + +__license__ = "BSD or Apache License, Version 2.0" +__copyright__ = "Copyright 2014-2019 %s" % __author__ diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/packaging/__init__.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/packaging/__init__.py new file mode 100644 index 0000000000..a0cf67df52 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/packaging/__init__.py @@ -0,0 +1,26 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +from .__about__ import ( + __author__, + __copyright__, + __email__, + __license__, + __summary__, + __title__, + __uri__, + __version__, +) + +__all__ = [ + "__title__", + "__summary__", + "__uri__", + "__version__", + "__author__", + "__email__", + "__license__", + "__copyright__", +] diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/packaging/_compat.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/packaging/_compat.py new file mode 100644 index 0000000000..25da473c19 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/packaging/_compat.py @@ -0,0 +1,31 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +import sys + + +PY2 = sys.version_info[0] == 2 +PY3 = sys.version_info[0] == 3 + +# flake8: noqa + +if PY3: + string_types = (str,) +else: + string_types = (basestring,) + + +def with_metaclass(meta, *bases): + """ + Create a base class with a metaclass. + """ + # This requires a bit of explanation: the basic idea is to make a dummy + # metaclass for one level of class instantiation that replaces itself with + # the actual metaclass. + class metaclass(meta): + def __new__(cls, name, this_bases, d): + return meta(name, bases, d) + + return type.__new__(metaclass, "temporary_class", (), {}) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/packaging/_structures.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/packaging/_structures.py new file mode 100644 index 0000000000..68dcca634d --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/packaging/_structures.py @@ -0,0 +1,68 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + + +class Infinity(object): + def __repr__(self): + return "Infinity" + + def __hash__(self): + return hash(repr(self)) + + def __lt__(self, other): + return False + + def __le__(self, other): + return False + + def __eq__(self, other): + return isinstance(other, self.__class__) + + def __ne__(self, other): + return not isinstance(other, self.__class__) + + def __gt__(self, other): + return True + + def __ge__(self, other): + return True + + def __neg__(self): + return NegativeInfinity + + +Infinity = Infinity() + + +class NegativeInfinity(object): + def __repr__(self): + return "-Infinity" + + def __hash__(self): + return hash(repr(self)) + + def __lt__(self, other): + return True + + def __le__(self, other): + return True + + def __eq__(self, other): + return isinstance(other, self.__class__) + + def __ne__(self, other): + return not isinstance(other, self.__class__) + + def __gt__(self, other): + return False + + def __ge__(self, other): + return False + + def __neg__(self): + return Infinity + + +NegativeInfinity = NegativeInfinity() diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/packaging/markers.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/packaging/markers.py new file mode 100644 index 0000000000..4bdfdb24f2 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/packaging/markers.py @@ -0,0 +1,296 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +import operator +import os +import platform +import sys + +from setuptools.extern.pyparsing import ParseException, ParseResults, stringStart, stringEnd +from setuptools.extern.pyparsing import ZeroOrMore, Group, Forward, QuotedString +from setuptools.extern.pyparsing import Literal as L # noqa + +from ._compat import string_types +from .specifiers import Specifier, InvalidSpecifier + + +__all__ = [ + "InvalidMarker", + "UndefinedComparison", + "UndefinedEnvironmentName", + "Marker", + "default_environment", +] + + +class InvalidMarker(ValueError): + """ + An invalid marker was found, users should refer to PEP 508. + """ + + +class UndefinedComparison(ValueError): + """ + An invalid operation was attempted on a value that doesn't support it. + """ + + +class UndefinedEnvironmentName(ValueError): + """ + A name was attempted to be used that does not exist inside of the + environment. + """ + + +class Node(object): + def __init__(self, value): + self.value = value + + def __str__(self): + return str(self.value) + + def __repr__(self): + return "<{0}({1!r})>".format(self.__class__.__name__, str(self)) + + def serialize(self): + raise NotImplementedError + + +class Variable(Node): + def serialize(self): + return str(self) + + +class Value(Node): + def serialize(self): + return '"{0}"'.format(self) + + +class Op(Node): + def serialize(self): + return str(self) + + +VARIABLE = ( + L("implementation_version") + | L("platform_python_implementation") + | L("implementation_name") + | L("python_full_version") + | L("platform_release") + | L("platform_version") + | L("platform_machine") + | L("platform_system") + | L("python_version") + | L("sys_platform") + | L("os_name") + | L("os.name") + | L("sys.platform") # PEP-345 + | L("platform.version") # PEP-345 + | L("platform.machine") # PEP-345 + | L("platform.python_implementation") # PEP-345 + | L("python_implementation") # PEP-345 + | L("extra") # undocumented setuptools legacy +) +ALIASES = { + "os.name": "os_name", + "sys.platform": "sys_platform", + "platform.version": "platform_version", + "platform.machine": "platform_machine", + "platform.python_implementation": "platform_python_implementation", + "python_implementation": "platform_python_implementation", +} +VARIABLE.setParseAction(lambda s, l, t: Variable(ALIASES.get(t[0], t[0]))) + +VERSION_CMP = ( + L("===") | L("==") | L(">=") | L("<=") | L("!=") | L("~=") | L(">") | L("<") +) + +MARKER_OP = VERSION_CMP | L("not in") | L("in") +MARKER_OP.setParseAction(lambda s, l, t: Op(t[0])) + +MARKER_VALUE = QuotedString("'") | QuotedString('"') +MARKER_VALUE.setParseAction(lambda s, l, t: Value(t[0])) + +BOOLOP = L("and") | L("or") + +MARKER_VAR = VARIABLE | MARKER_VALUE + +MARKER_ITEM = Group(MARKER_VAR + MARKER_OP + MARKER_VAR) +MARKER_ITEM.setParseAction(lambda s, l, t: tuple(t[0])) + +LPAREN = L("(").suppress() +RPAREN = L(")").suppress() + +MARKER_EXPR = Forward() +MARKER_ATOM = MARKER_ITEM | Group(LPAREN + MARKER_EXPR + RPAREN) +MARKER_EXPR << MARKER_ATOM + ZeroOrMore(BOOLOP + MARKER_EXPR) + +MARKER = stringStart + MARKER_EXPR + stringEnd + + +def _coerce_parse_result(results): + if isinstance(results, ParseResults): + return [_coerce_parse_result(i) for i in results] + else: + return results + + +def _format_marker(marker, first=True): + assert isinstance(marker, (list, tuple, string_types)) + + # Sometimes we have a structure like [[...]] which is a single item list + # where the single item is itself it's own list. In that case we want skip + # the rest of this function so that we don't get extraneous () on the + # outside. + if ( + isinstance(marker, list) + and len(marker) == 1 + and isinstance(marker[0], (list, tuple)) + ): + return _format_marker(marker[0]) + + if isinstance(marker, list): + inner = (_format_marker(m, first=False) for m in marker) + if first: + return " ".join(inner) + else: + return "(" + " ".join(inner) + ")" + elif isinstance(marker, tuple): + return " ".join([m.serialize() for m in marker]) + else: + return marker + + +_operators = { + "in": lambda lhs, rhs: lhs in rhs, + "not in": lambda lhs, rhs: lhs not in rhs, + "<": operator.lt, + "<=": operator.le, + "==": operator.eq, + "!=": operator.ne, + ">=": operator.ge, + ">": operator.gt, +} + + +def _eval_op(lhs, op, rhs): + try: + spec = Specifier("".join([op.serialize(), rhs])) + except InvalidSpecifier: + pass + else: + return spec.contains(lhs) + + oper = _operators.get(op.serialize()) + if oper is None: + raise UndefinedComparison( + "Undefined {0!r} on {1!r} and {2!r}.".format(op, lhs, rhs) + ) + + return oper(lhs, rhs) + + +_undefined = object() + + +def _get_env(environment, name): + value = environment.get(name, _undefined) + + if value is _undefined: + raise UndefinedEnvironmentName( + "{0!r} does not exist in evaluation environment.".format(name) + ) + + return value + + +def _evaluate_markers(markers, environment): + groups = [[]] + + for marker in markers: + assert isinstance(marker, (list, tuple, string_types)) + + if isinstance(marker, list): + groups[-1].append(_evaluate_markers(marker, environment)) + elif isinstance(marker, tuple): + lhs, op, rhs = marker + + if isinstance(lhs, Variable): + lhs_value = _get_env(environment, lhs.value) + rhs_value = rhs.value + else: + lhs_value = lhs.value + rhs_value = _get_env(environment, rhs.value) + + groups[-1].append(_eval_op(lhs_value, op, rhs_value)) + else: + assert marker in ["and", "or"] + if marker == "or": + groups.append([]) + + return any(all(item) for item in groups) + + +def format_full_version(info): + version = "{0.major}.{0.minor}.{0.micro}".format(info) + kind = info.releaselevel + if kind != "final": + version += kind[0] + str(info.serial) + return version + + +def default_environment(): + if hasattr(sys, "implementation"): + iver = format_full_version(sys.implementation.version) + implementation_name = sys.implementation.name + else: + iver = "0" + implementation_name = "" + + return { + "implementation_name": implementation_name, + "implementation_version": iver, + "os_name": os.name, + "platform_machine": platform.machine(), + "platform_release": platform.release(), + "platform_system": platform.system(), + "platform_version": platform.version(), + "python_full_version": platform.python_version(), + "platform_python_implementation": platform.python_implementation(), + "python_version": ".".join(platform.python_version_tuple()[:2]), + "sys_platform": sys.platform, + } + + +class Marker(object): + def __init__(self, marker): + try: + self._markers = _coerce_parse_result(MARKER.parseString(marker)) + except ParseException as e: + err_str = "Invalid marker: {0!r}, parse error at {1!r}".format( + marker, marker[e.loc : e.loc + 8] + ) + raise InvalidMarker(err_str) + + def __str__(self): + return _format_marker(self._markers) + + def __repr__(self): + return "".format(str(self)) + + def evaluate(self, environment=None): + """Evaluate a marker. + + Return the boolean from evaluating the given marker against the + environment. environment is an optional argument to override all or + part of the determined environment. + + The environment is determined from the current Python process. + """ + current_environment = default_environment() + if environment is not None: + current_environment.update(environment) + + return _evaluate_markers(self._markers, current_environment) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/packaging/requirements.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/packaging/requirements.py new file mode 100644 index 0000000000..8a0c2cb9be --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/packaging/requirements.py @@ -0,0 +1,138 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +import string +import re + +from setuptools.extern.pyparsing import stringStart, stringEnd, originalTextFor, ParseException +from setuptools.extern.pyparsing import ZeroOrMore, Word, Optional, Regex, Combine +from setuptools.extern.pyparsing import Literal as L # noqa +from setuptools.extern.six.moves.urllib import parse as urlparse + +from .markers import MARKER_EXPR, Marker +from .specifiers import LegacySpecifier, Specifier, SpecifierSet + + +class InvalidRequirement(ValueError): + """ + An invalid requirement was found, users should refer to PEP 508. + """ + + +ALPHANUM = Word(string.ascii_letters + string.digits) + +LBRACKET = L("[").suppress() +RBRACKET = L("]").suppress() +LPAREN = L("(").suppress() +RPAREN = L(")").suppress() +COMMA = L(",").suppress() +SEMICOLON = L(";").suppress() +AT = L("@").suppress() + +PUNCTUATION = Word("-_.") +IDENTIFIER_END = ALPHANUM | (ZeroOrMore(PUNCTUATION) + ALPHANUM) +IDENTIFIER = Combine(ALPHANUM + ZeroOrMore(IDENTIFIER_END)) + +NAME = IDENTIFIER("name") +EXTRA = IDENTIFIER + +URI = Regex(r"[^ ]+")("url") +URL = AT + URI + +EXTRAS_LIST = EXTRA + ZeroOrMore(COMMA + EXTRA) +EXTRAS = (LBRACKET + Optional(EXTRAS_LIST) + RBRACKET)("extras") + +VERSION_PEP440 = Regex(Specifier._regex_str, re.VERBOSE | re.IGNORECASE) +VERSION_LEGACY = Regex(LegacySpecifier._regex_str, re.VERBOSE | re.IGNORECASE) + +VERSION_ONE = VERSION_PEP440 ^ VERSION_LEGACY +VERSION_MANY = Combine( + VERSION_ONE + ZeroOrMore(COMMA + VERSION_ONE), joinString=",", adjacent=False +)("_raw_spec") +_VERSION_SPEC = Optional(((LPAREN + VERSION_MANY + RPAREN) | VERSION_MANY)) +_VERSION_SPEC.setParseAction(lambda s, l, t: t._raw_spec or "") + +VERSION_SPEC = originalTextFor(_VERSION_SPEC)("specifier") +VERSION_SPEC.setParseAction(lambda s, l, t: t[1]) + +MARKER_EXPR = originalTextFor(MARKER_EXPR())("marker") +MARKER_EXPR.setParseAction( + lambda s, l, t: Marker(s[t._original_start : t._original_end]) +) +MARKER_SEPARATOR = SEMICOLON +MARKER = MARKER_SEPARATOR + MARKER_EXPR + +VERSION_AND_MARKER = VERSION_SPEC + Optional(MARKER) +URL_AND_MARKER = URL + Optional(MARKER) + +NAMED_REQUIREMENT = NAME + Optional(EXTRAS) + (URL_AND_MARKER | VERSION_AND_MARKER) + +REQUIREMENT = stringStart + NAMED_REQUIREMENT + stringEnd +# setuptools.extern.pyparsing isn't thread safe during initialization, so we do it eagerly, see +# issue #104 +REQUIREMENT.parseString("x[]") + + +class Requirement(object): + """Parse a requirement. + + Parse a given requirement string into its parts, such as name, specifier, + URL, and extras. Raises InvalidRequirement on a badly-formed requirement + string. + """ + + # TODO: Can we test whether something is contained within a requirement? + # If so how do we do that? Do we need to test against the _name_ of + # the thing as well as the version? What about the markers? + # TODO: Can we normalize the name and extra name? + + def __init__(self, requirement_string): + try: + req = REQUIREMENT.parseString(requirement_string) + except ParseException as e: + raise InvalidRequirement( + 'Parse error at "{0!r}": {1}'.format( + requirement_string[e.loc : e.loc + 8], e.msg + ) + ) + + self.name = req.name + if req.url: + parsed_url = urlparse.urlparse(req.url) + if parsed_url.scheme == "file": + if urlparse.urlunparse(parsed_url) != req.url: + raise InvalidRequirement("Invalid URL given") + elif not (parsed_url.scheme and parsed_url.netloc) or ( + not parsed_url.scheme and not parsed_url.netloc + ): + raise InvalidRequirement("Invalid URL: {0}".format(req.url)) + self.url = req.url + else: + self.url = None + self.extras = set(req.extras.asList() if req.extras else []) + self.specifier = SpecifierSet(req.specifier) + self.marker = req.marker if req.marker else None + + def __str__(self): + parts = [self.name] + + if self.extras: + parts.append("[{0}]".format(",".join(sorted(self.extras)))) + + if self.specifier: + parts.append(str(self.specifier)) + + if self.url: + parts.append("@ {0}".format(self.url)) + if self.marker: + parts.append(" ") + + if self.marker: + parts.append("; {0}".format(self.marker)) + + return "".join(parts) + + def __repr__(self): + return "".format(str(self)) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/packaging/specifiers.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/packaging/specifiers.py new file mode 100644 index 0000000000..743576a080 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/packaging/specifiers.py @@ -0,0 +1,749 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +import abc +import functools +import itertools +import re + +from ._compat import string_types, with_metaclass +from .version import Version, LegacyVersion, parse + + +class InvalidSpecifier(ValueError): + """ + An invalid specifier was found, users should refer to PEP 440. + """ + + +class BaseSpecifier(with_metaclass(abc.ABCMeta, object)): + @abc.abstractmethod + def __str__(self): + """ + Returns the str representation of this Specifier like object. This + should be representative of the Specifier itself. + """ + + @abc.abstractmethod + def __hash__(self): + """ + Returns a hash value for this Specifier like object. + """ + + @abc.abstractmethod + def __eq__(self, other): + """ + Returns a boolean representing whether or not the two Specifier like + objects are equal. + """ + + @abc.abstractmethod + def __ne__(self, other): + """ + Returns a boolean representing whether or not the two Specifier like + objects are not equal. + """ + + @abc.abstractproperty + def prereleases(self): + """ + Returns whether or not pre-releases as a whole are allowed by this + specifier. + """ + + @prereleases.setter + def prereleases(self, value): + """ + Sets whether or not pre-releases as a whole are allowed by this + specifier. + """ + + @abc.abstractmethod + def contains(self, item, prereleases=None): + """ + Determines if the given item is contained within this specifier. + """ + + @abc.abstractmethod + def filter(self, iterable, prereleases=None): + """ + Takes an iterable of items and filters them so that only items which + are contained within this specifier are allowed in it. + """ + + +class _IndividualSpecifier(BaseSpecifier): + + _operators = {} + + def __init__(self, spec="", prereleases=None): + match = self._regex.search(spec) + if not match: + raise InvalidSpecifier("Invalid specifier: '{0}'".format(spec)) + + self._spec = (match.group("operator").strip(), match.group("version").strip()) + + # Store whether or not this Specifier should accept prereleases + self._prereleases = prereleases + + def __repr__(self): + pre = ( + ", prereleases={0!r}".format(self.prereleases) + if self._prereleases is not None + else "" + ) + + return "<{0}({1!r}{2})>".format(self.__class__.__name__, str(self), pre) + + def __str__(self): + return "{0}{1}".format(*self._spec) + + def __hash__(self): + return hash(self._spec) + + def __eq__(self, other): + if isinstance(other, string_types): + try: + other = self.__class__(other) + except InvalidSpecifier: + return NotImplemented + elif not isinstance(other, self.__class__): + return NotImplemented + + return self._spec == other._spec + + def __ne__(self, other): + if isinstance(other, string_types): + try: + other = self.__class__(other) + except InvalidSpecifier: + return NotImplemented + elif not isinstance(other, self.__class__): + return NotImplemented + + return self._spec != other._spec + + def _get_operator(self, op): + return getattr(self, "_compare_{0}".format(self._operators[op])) + + def _coerce_version(self, version): + if not isinstance(version, (LegacyVersion, Version)): + version = parse(version) + return version + + @property + def operator(self): + return self._spec[0] + + @property + def version(self): + return self._spec[1] + + @property + def prereleases(self): + return self._prereleases + + @prereleases.setter + def prereleases(self, value): + self._prereleases = value + + def __contains__(self, item): + return self.contains(item) + + def contains(self, item, prereleases=None): + # Determine if prereleases are to be allowed or not. + if prereleases is None: + prereleases = self.prereleases + + # Normalize item to a Version or LegacyVersion, this allows us to have + # a shortcut for ``"2.0" in Specifier(">=2") + item = self._coerce_version(item) + + # Determine if we should be supporting prereleases in this specifier + # or not, if we do not support prereleases than we can short circuit + # logic if this version is a prereleases. + if item.is_prerelease and not prereleases: + return False + + # Actually do the comparison to determine if this item is contained + # within this Specifier or not. + return self._get_operator(self.operator)(item, self.version) + + def filter(self, iterable, prereleases=None): + yielded = False + found_prereleases = [] + + kw = {"prereleases": prereleases if prereleases is not None else True} + + # Attempt to iterate over all the values in the iterable and if any of + # them match, yield them. + for version in iterable: + parsed_version = self._coerce_version(version) + + if self.contains(parsed_version, **kw): + # If our version is a prerelease, and we were not set to allow + # prereleases, then we'll store it for later incase nothing + # else matches this specifier. + if parsed_version.is_prerelease and not ( + prereleases or self.prereleases + ): + found_prereleases.append(version) + # Either this is not a prerelease, or we should have been + # accepting prereleases from the beginning. + else: + yielded = True + yield version + + # Now that we've iterated over everything, determine if we've yielded + # any values, and if we have not and we have any prereleases stored up + # then we will go ahead and yield the prereleases. + if not yielded and found_prereleases: + for version in found_prereleases: + yield version + + +class LegacySpecifier(_IndividualSpecifier): + + _regex_str = r""" + (?P(==|!=|<=|>=|<|>)) + \s* + (?P + [^,;\s)]* # Since this is a "legacy" specifier, and the version + # string can be just about anything, we match everything + # except for whitespace, a semi-colon for marker support, + # a closing paren since versions can be enclosed in + # them, and a comma since it's a version separator. + ) + """ + + _regex = re.compile(r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE) + + _operators = { + "==": "equal", + "!=": "not_equal", + "<=": "less_than_equal", + ">=": "greater_than_equal", + "<": "less_than", + ">": "greater_than", + } + + def _coerce_version(self, version): + if not isinstance(version, LegacyVersion): + version = LegacyVersion(str(version)) + return version + + def _compare_equal(self, prospective, spec): + return prospective == self._coerce_version(spec) + + def _compare_not_equal(self, prospective, spec): + return prospective != self._coerce_version(spec) + + def _compare_less_than_equal(self, prospective, spec): + return prospective <= self._coerce_version(spec) + + def _compare_greater_than_equal(self, prospective, spec): + return prospective >= self._coerce_version(spec) + + def _compare_less_than(self, prospective, spec): + return prospective < self._coerce_version(spec) + + def _compare_greater_than(self, prospective, spec): + return prospective > self._coerce_version(spec) + + +def _require_version_compare(fn): + @functools.wraps(fn) + def wrapped(self, prospective, spec): + if not isinstance(prospective, Version): + return False + return fn(self, prospective, spec) + + return wrapped + + +class Specifier(_IndividualSpecifier): + + _regex_str = r""" + (?P(~=|==|!=|<=|>=|<|>|===)) + (?P + (?: + # The identity operators allow for an escape hatch that will + # do an exact string match of the version you wish to install. + # This will not be parsed by PEP 440 and we cannot determine + # any semantic meaning from it. This operator is discouraged + # but included entirely as an escape hatch. + (?<====) # Only match for the identity operator + \s* + [^\s]* # We just match everything, except for whitespace + # since we are only testing for strict identity. + ) + | + (?: + # The (non)equality operators allow for wild card and local + # versions to be specified so we have to define these two + # operators separately to enable that. + (?<===|!=) # Only match for equals and not equals + + \s* + v? + (?:[0-9]+!)? # epoch + [0-9]+(?:\.[0-9]+)* # release + (?: # pre release + [-_\.]? + (a|b|c|rc|alpha|beta|pre|preview) + [-_\.]? + [0-9]* + )? + (?: # post release + (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) + )? + + # You cannot use a wild card and a dev or local version + # together so group them with a | and make them optional. + (?: + (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release + (?:\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*)? # local + | + \.\* # Wild card syntax of .* + )? + ) + | + (?: + # The compatible operator requires at least two digits in the + # release segment. + (?<=~=) # Only match for the compatible operator + + \s* + v? + (?:[0-9]+!)? # epoch + [0-9]+(?:\.[0-9]+)+ # release (We have a + instead of a *) + (?: # pre release + [-_\.]? + (a|b|c|rc|alpha|beta|pre|preview) + [-_\.]? + [0-9]* + )? + (?: # post release + (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) + )? + (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release + ) + | + (?: + # All other operators only allow a sub set of what the + # (non)equality operators do. Specifically they do not allow + # local versions to be specified nor do they allow the prefix + # matching wild cards. + (?=": "greater_than_equal", + "<": "less_than", + ">": "greater_than", + "===": "arbitrary", + } + + @_require_version_compare + def _compare_compatible(self, prospective, spec): + # Compatible releases have an equivalent combination of >= and ==. That + # is that ~=2.2 is equivalent to >=2.2,==2.*. This allows us to + # implement this in terms of the other specifiers instead of + # implementing it ourselves. The only thing we need to do is construct + # the other specifiers. + + # We want everything but the last item in the version, but we want to + # ignore post and dev releases and we want to treat the pre-release as + # it's own separate segment. + prefix = ".".join( + list( + itertools.takewhile( + lambda x: (not x.startswith("post") and not x.startswith("dev")), + _version_split(spec), + ) + )[:-1] + ) + + # Add the prefix notation to the end of our string + prefix += ".*" + + return self._get_operator(">=")(prospective, spec) and self._get_operator("==")( + prospective, prefix + ) + + @_require_version_compare + def _compare_equal(self, prospective, spec): + # We need special logic to handle prefix matching + if spec.endswith(".*"): + # In the case of prefix matching we want to ignore local segment. + prospective = Version(prospective.public) + # Split the spec out by dots, and pretend that there is an implicit + # dot in between a release segment and a pre-release segment. + spec = _version_split(spec[:-2]) # Remove the trailing .* + + # Split the prospective version out by dots, and pretend that there + # is an implicit dot in between a release segment and a pre-release + # segment. + prospective = _version_split(str(prospective)) + + # Shorten the prospective version to be the same length as the spec + # so that we can determine if the specifier is a prefix of the + # prospective version or not. + prospective = prospective[: len(spec)] + + # Pad out our two sides with zeros so that they both equal the same + # length. + spec, prospective = _pad_version(spec, prospective) + else: + # Convert our spec string into a Version + spec = Version(spec) + + # If the specifier does not have a local segment, then we want to + # act as if the prospective version also does not have a local + # segment. + if not spec.local: + prospective = Version(prospective.public) + + return prospective == spec + + @_require_version_compare + def _compare_not_equal(self, prospective, spec): + return not self._compare_equal(prospective, spec) + + @_require_version_compare + def _compare_less_than_equal(self, prospective, spec): + return prospective <= Version(spec) + + @_require_version_compare + def _compare_greater_than_equal(self, prospective, spec): + return prospective >= Version(spec) + + @_require_version_compare + def _compare_less_than(self, prospective, spec): + # Convert our spec to a Version instance, since we'll want to work with + # it as a version. + spec = Version(spec) + + # Check to see if the prospective version is less than the spec + # version. If it's not we can short circuit and just return False now + # instead of doing extra unneeded work. + if not prospective < spec: + return False + + # This special case is here so that, unless the specifier itself + # includes is a pre-release version, that we do not accept pre-release + # versions for the version mentioned in the specifier (e.g. <3.1 should + # not match 3.1.dev0, but should match 3.0.dev0). + if not spec.is_prerelease and prospective.is_prerelease: + if Version(prospective.base_version) == Version(spec.base_version): + return False + + # If we've gotten to here, it means that prospective version is both + # less than the spec version *and* it's not a pre-release of the same + # version in the spec. + return True + + @_require_version_compare + def _compare_greater_than(self, prospective, spec): + # Convert our spec to a Version instance, since we'll want to work with + # it as a version. + spec = Version(spec) + + # Check to see if the prospective version is greater than the spec + # version. If it's not we can short circuit and just return False now + # instead of doing extra unneeded work. + if not prospective > spec: + return False + + # This special case is here so that, unless the specifier itself + # includes is a post-release version, that we do not accept + # post-release versions for the version mentioned in the specifier + # (e.g. >3.1 should not match 3.0.post0, but should match 3.2.post0). + if not spec.is_postrelease and prospective.is_postrelease: + if Version(prospective.base_version) == Version(spec.base_version): + return False + + # Ensure that we do not allow a local version of the version mentioned + # in the specifier, which is technically greater than, to match. + if prospective.local is not None: + if Version(prospective.base_version) == Version(spec.base_version): + return False + + # If we've gotten to here, it means that prospective version is both + # greater than the spec version *and* it's not a pre-release of the + # same version in the spec. + return True + + def _compare_arbitrary(self, prospective, spec): + return str(prospective).lower() == str(spec).lower() + + @property + def prereleases(self): + # If there is an explicit prereleases set for this, then we'll just + # blindly use that. + if self._prereleases is not None: + return self._prereleases + + # Look at all of our specifiers and determine if they are inclusive + # operators, and if they are if they are including an explicit + # prerelease. + operator, version = self._spec + if operator in ["==", ">=", "<=", "~=", "==="]: + # The == specifier can include a trailing .*, if it does we + # want to remove before parsing. + if operator == "==" and version.endswith(".*"): + version = version[:-2] + + # Parse the version, and if it is a pre-release than this + # specifier allows pre-releases. + if parse(version).is_prerelease: + return True + + return False + + @prereleases.setter + def prereleases(self, value): + self._prereleases = value + + +_prefix_regex = re.compile(r"^([0-9]+)((?:a|b|c|rc)[0-9]+)$") + + +def _version_split(version): + result = [] + for item in version.split("."): + match = _prefix_regex.search(item) + if match: + result.extend(match.groups()) + else: + result.append(item) + return result + + +def _pad_version(left, right): + left_split, right_split = [], [] + + # Get the release segment of our versions + left_split.append(list(itertools.takewhile(lambda x: x.isdigit(), left))) + right_split.append(list(itertools.takewhile(lambda x: x.isdigit(), right))) + + # Get the rest of our versions + left_split.append(left[len(left_split[0]) :]) + right_split.append(right[len(right_split[0]) :]) + + # Insert our padding + left_split.insert(1, ["0"] * max(0, len(right_split[0]) - len(left_split[0]))) + right_split.insert(1, ["0"] * max(0, len(left_split[0]) - len(right_split[0]))) + + return (list(itertools.chain(*left_split)), list(itertools.chain(*right_split))) + + +class SpecifierSet(BaseSpecifier): + def __init__(self, specifiers="", prereleases=None): + # Split on , to break each indidivual specifier into it's own item, and + # strip each item to remove leading/trailing whitespace. + specifiers = [s.strip() for s in specifiers.split(",") if s.strip()] + + # Parsed each individual specifier, attempting first to make it a + # Specifier and falling back to a LegacySpecifier. + parsed = set() + for specifier in specifiers: + try: + parsed.add(Specifier(specifier)) + except InvalidSpecifier: + parsed.add(LegacySpecifier(specifier)) + + # Turn our parsed specifiers into a frozen set and save them for later. + self._specs = frozenset(parsed) + + # Store our prereleases value so we can use it later to determine if + # we accept prereleases or not. + self._prereleases = prereleases + + def __repr__(self): + pre = ( + ", prereleases={0!r}".format(self.prereleases) + if self._prereleases is not None + else "" + ) + + return "".format(str(self), pre) + + def __str__(self): + return ",".join(sorted(str(s) for s in self._specs)) + + def __hash__(self): + return hash(self._specs) + + def __and__(self, other): + if isinstance(other, string_types): + other = SpecifierSet(other) + elif not isinstance(other, SpecifierSet): + return NotImplemented + + specifier = SpecifierSet() + specifier._specs = frozenset(self._specs | other._specs) + + if self._prereleases is None and other._prereleases is not None: + specifier._prereleases = other._prereleases + elif self._prereleases is not None and other._prereleases is None: + specifier._prereleases = self._prereleases + elif self._prereleases == other._prereleases: + specifier._prereleases = self._prereleases + else: + raise ValueError( + "Cannot combine SpecifierSets with True and False prerelease " + "overrides." + ) + + return specifier + + def __eq__(self, other): + if isinstance(other, string_types): + other = SpecifierSet(other) + elif isinstance(other, _IndividualSpecifier): + other = SpecifierSet(str(other)) + elif not isinstance(other, SpecifierSet): + return NotImplemented + + return self._specs == other._specs + + def __ne__(self, other): + if isinstance(other, string_types): + other = SpecifierSet(other) + elif isinstance(other, _IndividualSpecifier): + other = SpecifierSet(str(other)) + elif not isinstance(other, SpecifierSet): + return NotImplemented + + return self._specs != other._specs + + def __len__(self): + return len(self._specs) + + def __iter__(self): + return iter(self._specs) + + @property + def prereleases(self): + # If we have been given an explicit prerelease modifier, then we'll + # pass that through here. + if self._prereleases is not None: + return self._prereleases + + # If we don't have any specifiers, and we don't have a forced value, + # then we'll just return None since we don't know if this should have + # pre-releases or not. + if not self._specs: + return None + + # Otherwise we'll see if any of the given specifiers accept + # prereleases, if any of them do we'll return True, otherwise False. + return any(s.prereleases for s in self._specs) + + @prereleases.setter + def prereleases(self, value): + self._prereleases = value + + def __contains__(self, item): + return self.contains(item) + + def contains(self, item, prereleases=None): + # Ensure that our item is a Version or LegacyVersion instance. + if not isinstance(item, (LegacyVersion, Version)): + item = parse(item) + + # Determine if we're forcing a prerelease or not, if we're not forcing + # one for this particular filter call, then we'll use whatever the + # SpecifierSet thinks for whether or not we should support prereleases. + if prereleases is None: + prereleases = self.prereleases + + # We can determine if we're going to allow pre-releases by looking to + # see if any of the underlying items supports them. If none of them do + # and this item is a pre-release then we do not allow it and we can + # short circuit that here. + # Note: This means that 1.0.dev1 would not be contained in something + # like >=1.0.devabc however it would be in >=1.0.debabc,>0.0.dev0 + if not prereleases and item.is_prerelease: + return False + + # We simply dispatch to the underlying specs here to make sure that the + # given version is contained within all of them. + # Note: This use of all() here means that an empty set of specifiers + # will always return True, this is an explicit design decision. + return all(s.contains(item, prereleases=prereleases) for s in self._specs) + + def filter(self, iterable, prereleases=None): + # Determine if we're forcing a prerelease or not, if we're not forcing + # one for this particular filter call, then we'll use whatever the + # SpecifierSet thinks for whether or not we should support prereleases. + if prereleases is None: + prereleases = self.prereleases + + # If we have any specifiers, then we want to wrap our iterable in the + # filter method for each one, this will act as a logical AND amongst + # each specifier. + if self._specs: + for spec in self._specs: + iterable = spec.filter(iterable, prereleases=bool(prereleases)) + return iterable + # If we do not have any specifiers, then we need to have a rough filter + # which will filter out any pre-releases, unless there are no final + # releases, and which will filter out LegacyVersion in general. + else: + filtered = [] + found_prereleases = [] + + for item in iterable: + # Ensure that we some kind of Version class for this item. + if not isinstance(item, (LegacyVersion, Version)): + parsed_version = parse(item) + else: + parsed_version = item + + # Filter out any item which is parsed as a LegacyVersion + if isinstance(parsed_version, LegacyVersion): + continue + + # Store any item which is a pre-release for later unless we've + # already found a final version or we are accepting prereleases + if parsed_version.is_prerelease and not prereleases: + if not filtered: + found_prereleases.append(item) + else: + filtered.append(item) + + # If we've found no items except for pre-releases, then we'll go + # ahead and use the pre-releases + if not filtered and found_prereleases and prereleases is None: + return found_prereleases + + return filtered diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/packaging/tags.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/packaging/tags.py new file mode 100644 index 0000000000..ec9942f0f6 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/packaging/tags.py @@ -0,0 +1,404 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import absolute_import + +import distutils.util + +try: + from importlib.machinery import EXTENSION_SUFFIXES +except ImportError: # pragma: no cover + import imp + + EXTENSION_SUFFIXES = [x[0] for x in imp.get_suffixes()] + del imp +import platform +import re +import sys +import sysconfig +import warnings + + +INTERPRETER_SHORT_NAMES = { + "python": "py", # Generic. + "cpython": "cp", + "pypy": "pp", + "ironpython": "ip", + "jython": "jy", +} + + +_32_BIT_INTERPRETER = sys.maxsize <= 2 ** 32 + + +class Tag(object): + + __slots__ = ["_interpreter", "_abi", "_platform"] + + def __init__(self, interpreter, abi, platform): + self._interpreter = interpreter.lower() + self._abi = abi.lower() + self._platform = platform.lower() + + @property + def interpreter(self): + return self._interpreter + + @property + def abi(self): + return self._abi + + @property + def platform(self): + return self._platform + + def __eq__(self, other): + return ( + (self.platform == other.platform) + and (self.abi == other.abi) + and (self.interpreter == other.interpreter) + ) + + def __hash__(self): + return hash((self._interpreter, self._abi, self._platform)) + + def __str__(self): + return "{}-{}-{}".format(self._interpreter, self._abi, self._platform) + + def __repr__(self): + return "<{self} @ {self_id}>".format(self=self, self_id=id(self)) + + +def parse_tag(tag): + tags = set() + interpreters, abis, platforms = tag.split("-") + for interpreter in interpreters.split("."): + for abi in abis.split("."): + for platform_ in platforms.split("."): + tags.add(Tag(interpreter, abi, platform_)) + return frozenset(tags) + + +def _normalize_string(string): + return string.replace(".", "_").replace("-", "_") + + +def _cpython_interpreter(py_version): + # TODO: Is using py_version_nodot for interpreter version critical? + return "cp{major}{minor}".format(major=py_version[0], minor=py_version[1]) + + +def _cpython_abis(py_version): + abis = [] + version = "{}{}".format(*py_version[:2]) + debug = pymalloc = ucs4 = "" + with_debug = sysconfig.get_config_var("Py_DEBUG") + has_refcount = hasattr(sys, "gettotalrefcount") + # Windows doesn't set Py_DEBUG, so checking for support of debug-compiled + # extension modules is the best option. + # https://github.com/pypa/pip/issues/3383#issuecomment-173267692 + has_ext = "_d.pyd" in EXTENSION_SUFFIXES + if with_debug or (with_debug is None and (has_refcount or has_ext)): + debug = "d" + if py_version < (3, 8): + with_pymalloc = sysconfig.get_config_var("WITH_PYMALLOC") + if with_pymalloc or with_pymalloc is None: + pymalloc = "m" + if py_version < (3, 3): + unicode_size = sysconfig.get_config_var("Py_UNICODE_SIZE") + if unicode_size == 4 or ( + unicode_size is None and sys.maxunicode == 0x10FFFF + ): + ucs4 = "u" + elif debug: + # Debug builds can also load "normal" extension modules. + # We can also assume no UCS-4 or pymalloc requirement. + abis.append("cp{version}".format(version=version)) + abis.insert( + 0, + "cp{version}{debug}{pymalloc}{ucs4}".format( + version=version, debug=debug, pymalloc=pymalloc, ucs4=ucs4 + ), + ) + return abis + + +def _cpython_tags(py_version, interpreter, abis, platforms): + for abi in abis: + for platform_ in platforms: + yield Tag(interpreter, abi, platform_) + for tag in (Tag(interpreter, "abi3", platform_) for platform_ in platforms): + yield tag + for tag in (Tag(interpreter, "none", platform_) for platform_ in platforms): + yield tag + # PEP 384 was first implemented in Python 3.2. + for minor_version in range(py_version[1] - 1, 1, -1): + for platform_ in platforms: + interpreter = "cp{major}{minor}".format( + major=py_version[0], minor=minor_version + ) + yield Tag(interpreter, "abi3", platform_) + + +def _pypy_interpreter(): + return "pp{py_major}{pypy_major}{pypy_minor}".format( + py_major=sys.version_info[0], + pypy_major=sys.pypy_version_info.major, + pypy_minor=sys.pypy_version_info.minor, + ) + + +def _generic_abi(): + abi = sysconfig.get_config_var("SOABI") + if abi: + return _normalize_string(abi) + else: + return "none" + + +def _pypy_tags(py_version, interpreter, abi, platforms): + for tag in (Tag(interpreter, abi, platform) for platform in platforms): + yield tag + for tag in (Tag(interpreter, "none", platform) for platform in platforms): + yield tag + + +def _generic_tags(interpreter, py_version, abi, platforms): + for tag in (Tag(interpreter, abi, platform) for platform in platforms): + yield tag + if abi != "none": + tags = (Tag(interpreter, "none", platform_) for platform_ in platforms) + for tag in tags: + yield tag + + +def _py_interpreter_range(py_version): + """ + Yield Python versions in descending order. + + After the latest version, the major-only version will be yielded, and then + all following versions up to 'end'. + """ + yield "py{major}{minor}".format(major=py_version[0], minor=py_version[1]) + yield "py{major}".format(major=py_version[0]) + for minor in range(py_version[1] - 1, -1, -1): + yield "py{major}{minor}".format(major=py_version[0], minor=minor) + + +def _independent_tags(interpreter, py_version, platforms): + """ + Return the sequence of tags that are consistent across implementations. + + The tags consist of: + - py*-none- + - -none-any + - py*-none-any + """ + for version in _py_interpreter_range(py_version): + for platform_ in platforms: + yield Tag(version, "none", platform_) + yield Tag(interpreter, "none", "any") + for version in _py_interpreter_range(py_version): + yield Tag(version, "none", "any") + + +def _mac_arch(arch, is_32bit=_32_BIT_INTERPRETER): + if not is_32bit: + return arch + + if arch.startswith("ppc"): + return "ppc" + + return "i386" + + +def _mac_binary_formats(version, cpu_arch): + formats = [cpu_arch] + if cpu_arch == "x86_64": + if version < (10, 4): + return [] + formats.extend(["intel", "fat64", "fat32"]) + + elif cpu_arch == "i386": + if version < (10, 4): + return [] + formats.extend(["intel", "fat32", "fat"]) + + elif cpu_arch == "ppc64": + # TODO: Need to care about 32-bit PPC for ppc64 through 10.2? + if version > (10, 5) or version < (10, 4): + return [] + formats.append("fat64") + + elif cpu_arch == "ppc": + if version > (10, 6): + return [] + formats.extend(["fat32", "fat"]) + + formats.append("universal") + return formats + + +def _mac_platforms(version=None, arch=None): + version_str, _, cpu_arch = platform.mac_ver() + if version is None: + version = tuple(map(int, version_str.split(".")[:2])) + if arch is None: + arch = _mac_arch(cpu_arch) + platforms = [] + for minor_version in range(version[1], -1, -1): + compat_version = version[0], minor_version + binary_formats = _mac_binary_formats(compat_version, arch) + for binary_format in binary_formats: + platforms.append( + "macosx_{major}_{minor}_{binary_format}".format( + major=compat_version[0], + minor=compat_version[1], + binary_format=binary_format, + ) + ) + return platforms + + +# From PEP 513. +def _is_manylinux_compatible(name, glibc_version): + # Check for presence of _manylinux module. + try: + import _manylinux + + return bool(getattr(_manylinux, name + "_compatible")) + except (ImportError, AttributeError): + # Fall through to heuristic check below. + pass + + return _have_compatible_glibc(*glibc_version) + + +def _glibc_version_string(): + # Returns glibc version string, or None if not using glibc. + import ctypes + + # ctypes.CDLL(None) internally calls dlopen(NULL), and as the dlopen + # manpage says, "If filename is NULL, then the returned handle is for the + # main program". This way we can let the linker do the work to figure out + # which libc our process is actually using. + process_namespace = ctypes.CDLL(None) + try: + gnu_get_libc_version = process_namespace.gnu_get_libc_version + except AttributeError: + # Symbol doesn't exist -> therefore, we are not linked to + # glibc. + return None + + # Call gnu_get_libc_version, which returns a string like "2.5" + gnu_get_libc_version.restype = ctypes.c_char_p + version_str = gnu_get_libc_version() + # py2 / py3 compatibility: + if not isinstance(version_str, str): + version_str = version_str.decode("ascii") + + return version_str + + +# Separated out from have_compatible_glibc for easier unit testing. +def _check_glibc_version(version_str, required_major, minimum_minor): + # Parse string and check against requested version. + # + # We use a regexp instead of str.split because we want to discard any + # random junk that might come after the minor version -- this might happen + # in patched/forked versions of glibc (e.g. Linaro's version of glibc + # uses version strings like "2.20-2014.11"). See gh-3588. + m = re.match(r"(?P[0-9]+)\.(?P[0-9]+)", version_str) + if not m: + warnings.warn( + "Expected glibc version with 2 components major.minor," + " got: %s" % version_str, + RuntimeWarning, + ) + return False + return ( + int(m.group("major")) == required_major + and int(m.group("minor")) >= minimum_minor + ) + + +def _have_compatible_glibc(required_major, minimum_minor): + version_str = _glibc_version_string() + if version_str is None: + return False + return _check_glibc_version(version_str, required_major, minimum_minor) + + +def _linux_platforms(is_32bit=_32_BIT_INTERPRETER): + linux = _normalize_string(distutils.util.get_platform()) + if linux == "linux_x86_64" and is_32bit: + linux = "linux_i686" + manylinux_support = ( + ("manylinux2014", (2, 17)), # CentOS 7 w/ glibc 2.17 (PEP 599) + ("manylinux2010", (2, 12)), # CentOS 6 w/ glibc 2.12 (PEP 571) + ("manylinux1", (2, 5)), # CentOS 5 w/ glibc 2.5 (PEP 513) + ) + manylinux_support_iter = iter(manylinux_support) + for name, glibc_version in manylinux_support_iter: + if _is_manylinux_compatible(name, glibc_version): + platforms = [linux.replace("linux", name)] + break + else: + platforms = [] + # Support for a later manylinux implies support for an earlier version. + platforms += [linux.replace("linux", name) for name, _ in manylinux_support_iter] + platforms.append(linux) + return platforms + + +def _generic_platforms(): + platform = _normalize_string(distutils.util.get_platform()) + return [platform] + + +def _interpreter_name(): + name = platform.python_implementation().lower() + return INTERPRETER_SHORT_NAMES.get(name) or name + + +def _generic_interpreter(name, py_version): + version = sysconfig.get_config_var("py_version_nodot") + if not version: + version = "".join(map(str, py_version[:2])) + return "{name}{version}".format(name=name, version=version) + + +def sys_tags(): + """ + Returns the sequence of tag triples for the running interpreter. + + The order of the sequence corresponds to priority order for the + interpreter, from most to least important. + """ + py_version = sys.version_info[:2] + interpreter_name = _interpreter_name() + if platform.system() == "Darwin": + platforms = _mac_platforms() + elif platform.system() == "Linux": + platforms = _linux_platforms() + else: + platforms = _generic_platforms() + + if interpreter_name == "cp": + interpreter = _cpython_interpreter(py_version) + abis = _cpython_abis(py_version) + for tag in _cpython_tags(py_version, interpreter, abis, platforms): + yield tag + elif interpreter_name == "pp": + interpreter = _pypy_interpreter() + abi = _generic_abi() + for tag in _pypy_tags(py_version, interpreter, abi, platforms): + yield tag + else: + interpreter = _generic_interpreter(interpreter_name, py_version) + abi = _generic_abi() + for tag in _generic_tags(interpreter, py_version, abi, platforms): + yield tag + for tag in _independent_tags(interpreter, py_version, platforms): + yield tag diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/packaging/utils.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/packaging/utils.py new file mode 100644 index 0000000000..8841878693 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/packaging/utils.py @@ -0,0 +1,57 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +import re + +from .version import InvalidVersion, Version + + +_canonicalize_regex = re.compile(r"[-_.]+") + + +def canonicalize_name(name): + # This is taken from PEP 503. + return _canonicalize_regex.sub("-", name).lower() + + +def canonicalize_version(version): + """ + This is very similar to Version.__str__, but has one subtle differences + with the way it handles the release segment. + """ + + try: + version = Version(version) + except InvalidVersion: + # Legacy versions cannot be normalized + return version + + parts = [] + + # Epoch + if version.epoch != 0: + parts.append("{0}!".format(version.epoch)) + + # Release segment + # NB: This strips trailing '.0's to normalize + parts.append(re.sub(r"(\.0)+$", "", ".".join(str(x) for x in version.release))) + + # Pre-release + if version.pre is not None: + parts.append("".join(str(x) for x in version.pre)) + + # Post-release + if version.post is not None: + parts.append(".post{0}".format(version.post)) + + # Development release + if version.dev is not None: + parts.append(".dev{0}".format(version.dev)) + + # Local version segment + if version.local is not None: + parts.append("+{0}".format(version.local)) + + return "".join(parts) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/packaging/version.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/packaging/version.py new file mode 100644 index 0000000000..95157a1f78 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/packaging/version.py @@ -0,0 +1,420 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import absolute_import, division, print_function + +import collections +import itertools +import re + +from ._structures import Infinity + + +__all__ = ["parse", "Version", "LegacyVersion", "InvalidVersion", "VERSION_PATTERN"] + + +_Version = collections.namedtuple( + "_Version", ["epoch", "release", "dev", "pre", "post", "local"] +) + + +def parse(version): + """ + Parse the given version string and return either a :class:`Version` object + or a :class:`LegacyVersion` object depending on if the given version is + a valid PEP 440 version or a legacy version. + """ + try: + return Version(version) + except InvalidVersion: + return LegacyVersion(version) + + +class InvalidVersion(ValueError): + """ + An invalid version was found, users should refer to PEP 440. + """ + + +class _BaseVersion(object): + def __hash__(self): + return hash(self._key) + + def __lt__(self, other): + return self._compare(other, lambda s, o: s < o) + + def __le__(self, other): + return self._compare(other, lambda s, o: s <= o) + + def __eq__(self, other): + return self._compare(other, lambda s, o: s == o) + + def __ge__(self, other): + return self._compare(other, lambda s, o: s >= o) + + def __gt__(self, other): + return self._compare(other, lambda s, o: s > o) + + def __ne__(self, other): + return self._compare(other, lambda s, o: s != o) + + def _compare(self, other, method): + if not isinstance(other, _BaseVersion): + return NotImplemented + + return method(self._key, other._key) + + +class LegacyVersion(_BaseVersion): + def __init__(self, version): + self._version = str(version) + self._key = _legacy_cmpkey(self._version) + + def __str__(self): + return self._version + + def __repr__(self): + return "".format(repr(str(self))) + + @property + def public(self): + return self._version + + @property + def base_version(self): + return self._version + + @property + def epoch(self): + return -1 + + @property + def release(self): + return None + + @property + def pre(self): + return None + + @property + def post(self): + return None + + @property + def dev(self): + return None + + @property + def local(self): + return None + + @property + def is_prerelease(self): + return False + + @property + def is_postrelease(self): + return False + + @property + def is_devrelease(self): + return False + + +_legacy_version_component_re = re.compile(r"(\d+ | [a-z]+ | \.| -)", re.VERBOSE) + +_legacy_version_replacement_map = { + "pre": "c", + "preview": "c", + "-": "final-", + "rc": "c", + "dev": "@", +} + + +def _parse_version_parts(s): + for part in _legacy_version_component_re.split(s): + part = _legacy_version_replacement_map.get(part, part) + + if not part or part == ".": + continue + + if part[:1] in "0123456789": + # pad for numeric comparison + yield part.zfill(8) + else: + yield "*" + part + + # ensure that alpha/beta/candidate are before final + yield "*final" + + +def _legacy_cmpkey(version): + # We hardcode an epoch of -1 here. A PEP 440 version can only have a epoch + # greater than or equal to 0. This will effectively put the LegacyVersion, + # which uses the defacto standard originally implemented by setuptools, + # as before all PEP 440 versions. + epoch = -1 + + # This scheme is taken from pkg_resources.parse_version setuptools prior to + # it's adoption of the packaging library. + parts = [] + for part in _parse_version_parts(version.lower()): + if part.startswith("*"): + # remove "-" before a prerelease tag + if part < "*final": + while parts and parts[-1] == "*final-": + parts.pop() + + # remove trailing zeros from each series of numeric parts + while parts and parts[-1] == "00000000": + parts.pop() + + parts.append(part) + parts = tuple(parts) + + return epoch, parts + + +# Deliberately not anchored to the start and end of the string, to make it +# easier for 3rd party code to reuse +VERSION_PATTERN = r""" + v? + (?: + (?:(?P[0-9]+)!)? # epoch + (?P[0-9]+(?:\.[0-9]+)*) # release segment + (?P
                                          # pre-release
+            [-_\.]?
+            (?P(a|b|c|rc|alpha|beta|pre|preview))
+            [-_\.]?
+            (?P[0-9]+)?
+        )?
+        (?P                                         # post release
+            (?:-(?P[0-9]+))
+            |
+            (?:
+                [-_\.]?
+                (?Ppost|rev|r)
+                [-_\.]?
+                (?P[0-9]+)?
+            )
+        )?
+        (?P                                          # dev release
+            [-_\.]?
+            (?Pdev)
+            [-_\.]?
+            (?P[0-9]+)?
+        )?
+    )
+    (?:\+(?P[a-z0-9]+(?:[-_\.][a-z0-9]+)*))?       # local version
+"""
+
+
+class Version(_BaseVersion):
+
+    _regex = re.compile(r"^\s*" + VERSION_PATTERN + r"\s*$", re.VERBOSE | re.IGNORECASE)
+
+    def __init__(self, version):
+        # Validate the version and parse it into pieces
+        match = self._regex.search(version)
+        if not match:
+            raise InvalidVersion("Invalid version: '{0}'".format(version))
+
+        # Store the parsed out pieces of the version
+        self._version = _Version(
+            epoch=int(match.group("epoch")) if match.group("epoch") else 0,
+            release=tuple(int(i) for i in match.group("release").split(".")),
+            pre=_parse_letter_version(match.group("pre_l"), match.group("pre_n")),
+            post=_parse_letter_version(
+                match.group("post_l"), match.group("post_n1") or match.group("post_n2")
+            ),
+            dev=_parse_letter_version(match.group("dev_l"), match.group("dev_n")),
+            local=_parse_local_version(match.group("local")),
+        )
+
+        # Generate a key which will be used for sorting
+        self._key = _cmpkey(
+            self._version.epoch,
+            self._version.release,
+            self._version.pre,
+            self._version.post,
+            self._version.dev,
+            self._version.local,
+        )
+
+    def __repr__(self):
+        return "".format(repr(str(self)))
+
+    def __str__(self):
+        parts = []
+
+        # Epoch
+        if self.epoch != 0:
+            parts.append("{0}!".format(self.epoch))
+
+        # Release segment
+        parts.append(".".join(str(x) for x in self.release))
+
+        # Pre-release
+        if self.pre is not None:
+            parts.append("".join(str(x) for x in self.pre))
+
+        # Post-release
+        if self.post is not None:
+            parts.append(".post{0}".format(self.post))
+
+        # Development release
+        if self.dev is not None:
+            parts.append(".dev{0}".format(self.dev))
+
+        # Local version segment
+        if self.local is not None:
+            parts.append("+{0}".format(self.local))
+
+        return "".join(parts)
+
+    @property
+    def epoch(self):
+        return self._version.epoch
+
+    @property
+    def release(self):
+        return self._version.release
+
+    @property
+    def pre(self):
+        return self._version.pre
+
+    @property
+    def post(self):
+        return self._version.post[1] if self._version.post else None
+
+    @property
+    def dev(self):
+        return self._version.dev[1] if self._version.dev else None
+
+    @property
+    def local(self):
+        if self._version.local:
+            return ".".join(str(x) for x in self._version.local)
+        else:
+            return None
+
+    @property
+    def public(self):
+        return str(self).split("+", 1)[0]
+
+    @property
+    def base_version(self):
+        parts = []
+
+        # Epoch
+        if self.epoch != 0:
+            parts.append("{0}!".format(self.epoch))
+
+        # Release segment
+        parts.append(".".join(str(x) for x in self.release))
+
+        return "".join(parts)
+
+    @property
+    def is_prerelease(self):
+        return self.dev is not None or self.pre is not None
+
+    @property
+    def is_postrelease(self):
+        return self.post is not None
+
+    @property
+    def is_devrelease(self):
+        return self.dev is not None
+
+
+def _parse_letter_version(letter, number):
+    if letter:
+        # We consider there to be an implicit 0 in a pre-release if there is
+        # not a numeral associated with it.
+        if number is None:
+            number = 0
+
+        # We normalize any letters to their lower case form
+        letter = letter.lower()
+
+        # We consider some words to be alternate spellings of other words and
+        # in those cases we want to normalize the spellings to our preferred
+        # spelling.
+        if letter == "alpha":
+            letter = "a"
+        elif letter == "beta":
+            letter = "b"
+        elif letter in ["c", "pre", "preview"]:
+            letter = "rc"
+        elif letter in ["rev", "r"]:
+            letter = "post"
+
+        return letter, int(number)
+    if not letter and number:
+        # We assume if we are given a number, but we are not given a letter
+        # then this is using the implicit post release syntax (e.g. 1.0-1)
+        letter = "post"
+
+        return letter, int(number)
+
+
+_local_version_separators = re.compile(r"[\._-]")
+
+
+def _parse_local_version(local):
+    """
+    Takes a string like abc.1.twelve and turns it into ("abc", 1, "twelve").
+    """
+    if local is not None:
+        return tuple(
+            part.lower() if not part.isdigit() else int(part)
+            for part in _local_version_separators.split(local)
+        )
+
+
+def _cmpkey(epoch, release, pre, post, dev, local):
+    # When we compare a release version, we want to compare it with all of the
+    # trailing zeros removed. So we'll use a reverse the list, drop all the now
+    # leading zeros until we come to something non zero, then take the rest
+    # re-reverse it back into the correct order and make it a tuple and use
+    # that for our sorting key.
+    release = tuple(
+        reversed(list(itertools.dropwhile(lambda x: x == 0, reversed(release))))
+    )
+
+    # We need to "trick" the sorting algorithm to put 1.0.dev0 before 1.0a0.
+    # We'll do this by abusing the pre segment, but we _only_ want to do this
+    # if there is not a pre or a post segment. If we have one of those then
+    # the normal sorting rules will handle this case correctly.
+    if pre is None and post is None and dev is not None:
+        pre = -Infinity
+    # Versions without a pre-release (except as noted above) should sort after
+    # those with one.
+    elif pre is None:
+        pre = Infinity
+
+    # Versions without a post segment should sort before those with one.
+    if post is None:
+        post = -Infinity
+
+    # Versions without a development segment should sort after those with one.
+    if dev is None:
+        dev = Infinity
+
+    if local is None:
+        # Versions without a local segment should sort before those with one.
+        local = -Infinity
+    else:
+        # Versions with a local segment need that segment parsed to implement
+        # the sorting rules in PEP440.
+        # - Alpha numeric segments sort before numeric segments
+        # - Alpha numeric segments sort lexicographically
+        # - Numeric segments sort numerically
+        # - Shorter versions sort before longer versions when the prefixes
+        #   match exactly
+        local = tuple((i, "") if isinstance(i, int) else (-Infinity, i) for i in local)
+
+    return epoch, release, pre, post, dev, local
diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/pyparsing.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/pyparsing.py
new file mode 100644
index 0000000000..cf75e1e5fc
--- /dev/null
+++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/pyparsing.py
@@ -0,0 +1,5742 @@
+# module pyparsing.py
+#
+# Copyright (c) 2003-2018  Paul T. McGuire
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__doc__ = \
+"""
+pyparsing module - Classes and methods to define and execute parsing grammars
+=============================================================================
+
+The pyparsing module is an alternative approach to creating and executing simple grammars,
+vs. the traditional lex/yacc approach, or the use of regular expressions.  With pyparsing, you
+don't need to learn a new syntax for defining grammars or matching expressions - the parsing module
+provides a library of classes that you use to construct the grammar directly in Python.
+
+Here is a program to parse "Hello, World!" (or any greeting of the form 
+C{", !"}), built up using L{Word}, L{Literal}, and L{And} elements 
+(L{'+'} operator gives L{And} expressions, strings are auto-converted to
+L{Literal} expressions)::
+
+    from pyparsing import Word, alphas
+
+    # define grammar of a greeting
+    greet = Word(alphas) + "," + Word(alphas) + "!"
+
+    hello = "Hello, World!"
+    print (hello, "->", greet.parseString(hello))
+
+The program outputs the following::
+
+    Hello, World! -> ['Hello', ',', 'World', '!']
+
+The Python representation of the grammar is quite readable, owing to the self-explanatory
+class names, and the use of '+', '|' and '^' operators.
+
+The L{ParseResults} object returned from L{ParserElement.parseString} can be accessed as a nested list, a dictionary, or an
+object with named attributes.
+
+The pyparsing module handles some of the problems that are typically vexing when writing text parsers:
+ - extra or missing whitespace (the above program will also handle "Hello,World!", "Hello  ,  World  !", etc.)
+ - quoted strings
+ - embedded comments
+
+
+Getting Started -
+-----------------
+Visit the classes L{ParserElement} and L{ParseResults} to see the base classes that most other pyparsing
+classes inherit from. Use the docstrings for examples of how to:
+ - construct literal match expressions from L{Literal} and L{CaselessLiteral} classes
+ - construct character word-group expressions using the L{Word} class
+ - see how to create repetitive expressions using L{ZeroOrMore} and L{OneOrMore} classes
+ - use L{'+'}, L{'|'}, L{'^'}, and L{'&'} operators to combine simple expressions into more complex ones
+ - associate names with your parsed results using L{ParserElement.setResultsName}
+ - find some helpful expression short-cuts like L{delimitedList} and L{oneOf}
+ - find more useful common expressions in the L{pyparsing_common} namespace class
+"""
+
+__version__ = "2.2.1"
+__versionTime__ = "18 Sep 2018 00:49 UTC"
+__author__ = "Paul McGuire "
+
+import string
+from weakref import ref as wkref
+import copy
+import sys
+import warnings
+import re
+import sre_constants
+import collections
+import pprint
+import traceback
+import types
+from datetime import datetime
+
+try:
+    from _thread import RLock
+except ImportError:
+    from threading import RLock
+
+try:
+    # Python 3
+    from collections.abc import Iterable
+    from collections.abc import MutableMapping
+except ImportError:
+    # Python 2.7
+    from collections import Iterable
+    from collections import MutableMapping
+
+try:
+    from collections import OrderedDict as _OrderedDict
+except ImportError:
+    try:
+        from ordereddict import OrderedDict as _OrderedDict
+    except ImportError:
+        _OrderedDict = None
+
+#~ sys.stderr.write( "testing pyparsing module, version %s, %s\n" % (__version__,__versionTime__ ) )
+
+__all__ = [
+'And', 'CaselessKeyword', 'CaselessLiteral', 'CharsNotIn', 'Combine', 'Dict', 'Each', 'Empty',
+'FollowedBy', 'Forward', 'GoToColumn', 'Group', 'Keyword', 'LineEnd', 'LineStart', 'Literal',
+'MatchFirst', 'NoMatch', 'NotAny', 'OneOrMore', 'OnlyOnce', 'Optional', 'Or',
+'ParseBaseException', 'ParseElementEnhance', 'ParseException', 'ParseExpression', 'ParseFatalException',
+'ParseResults', 'ParseSyntaxException', 'ParserElement', 'QuotedString', 'RecursiveGrammarException',
+'Regex', 'SkipTo', 'StringEnd', 'StringStart', 'Suppress', 'Token', 'TokenConverter', 
+'White', 'Word', 'WordEnd', 'WordStart', 'ZeroOrMore',
+'alphanums', 'alphas', 'alphas8bit', 'anyCloseTag', 'anyOpenTag', 'cStyleComment', 'col',
+'commaSeparatedList', 'commonHTMLEntity', 'countedArray', 'cppStyleComment', 'dblQuotedString',
+'dblSlashComment', 'delimitedList', 'dictOf', 'downcaseTokens', 'empty', 'hexnums',
+'htmlComment', 'javaStyleComment', 'line', 'lineEnd', 'lineStart', 'lineno',
+'makeHTMLTags', 'makeXMLTags', 'matchOnlyAtCol', 'matchPreviousExpr', 'matchPreviousLiteral',
+'nestedExpr', 'nullDebugAction', 'nums', 'oneOf', 'opAssoc', 'operatorPrecedence', 'printables',
+'punc8bit', 'pythonStyleComment', 'quotedString', 'removeQuotes', 'replaceHTMLEntity', 
+'replaceWith', 'restOfLine', 'sglQuotedString', 'srange', 'stringEnd',
+'stringStart', 'traceParseAction', 'unicodeString', 'upcaseTokens', 'withAttribute',
+'indentedBlock', 'originalTextFor', 'ungroup', 'infixNotation','locatedExpr', 'withClass',
+'CloseMatch', 'tokenMap', 'pyparsing_common',
+]
+
+system_version = tuple(sys.version_info)[:3]
+PY_3 = system_version[0] == 3
+if PY_3:
+    _MAX_INT = sys.maxsize
+    basestring = str
+    unichr = chr
+    _ustr = str
+
+    # build list of single arg builtins, that can be used as parse actions
+    singleArgBuiltins = [sum, len, sorted, reversed, list, tuple, set, any, all, min, max]
+
+else:
+    _MAX_INT = sys.maxint
+    range = xrange
+
+    def _ustr(obj):
+        """Drop-in replacement for str(obj) that tries to be Unicode friendly. It first tries
+           str(obj). If that fails with a UnicodeEncodeError, then it tries unicode(obj). It
+           then < returns the unicode object | encodes it with the default encoding | ... >.
+        """
+        if isinstance(obj,unicode):
+            return obj
+
+        try:
+            # If this works, then _ustr(obj) has the same behaviour as str(obj), so
+            # it won't break any existing code.
+            return str(obj)
+
+        except UnicodeEncodeError:
+            # Else encode it
+            ret = unicode(obj).encode(sys.getdefaultencoding(), 'xmlcharrefreplace')
+            xmlcharref = Regex(r'&#\d+;')
+            xmlcharref.setParseAction(lambda t: '\\u' + hex(int(t[0][2:-1]))[2:])
+            return xmlcharref.transformString(ret)
+
+    # build list of single arg builtins, tolerant of Python version, that can be used as parse actions
+    singleArgBuiltins = []
+    import __builtin__
+    for fname in "sum len sorted reversed list tuple set any all min max".split():
+        try:
+            singleArgBuiltins.append(getattr(__builtin__,fname))
+        except AttributeError:
+            continue
+            
+_generatorType = type((y for y in range(1)))
+ 
+def _xml_escape(data):
+    """Escape &, <, >, ", ', etc. in a string of data."""
+
+    # ampersand must be replaced first
+    from_symbols = '&><"\''
+    to_symbols = ('&'+s+';' for s in "amp gt lt quot apos".split())
+    for from_,to_ in zip(from_symbols, to_symbols):
+        data = data.replace(from_, to_)
+    return data
+
+class _Constants(object):
+    pass
+
+alphas     = string.ascii_uppercase + string.ascii_lowercase
+nums       = "0123456789"
+hexnums    = nums + "ABCDEFabcdef"
+alphanums  = alphas + nums
+_bslash    = chr(92)
+printables = "".join(c for c in string.printable if c not in string.whitespace)
+
+class ParseBaseException(Exception):
+    """base exception class for all parsing runtime exceptions"""
+    # Performance tuning: we construct a *lot* of these, so keep this
+    # constructor as small and fast as possible
+    def __init__( self, pstr, loc=0, msg=None, elem=None ):
+        self.loc = loc
+        if msg is None:
+            self.msg = pstr
+            self.pstr = ""
+        else:
+            self.msg = msg
+            self.pstr = pstr
+        self.parserElement = elem
+        self.args = (pstr, loc, msg)
+
+    @classmethod
+    def _from_exception(cls, pe):
+        """
+        internal factory method to simplify creating one type of ParseException 
+        from another - avoids having __init__ signature conflicts among subclasses
+        """
+        return cls(pe.pstr, pe.loc, pe.msg, pe.parserElement)
+
+    def __getattr__( self, aname ):
+        """supported attributes by name are:
+            - lineno - returns the line number of the exception text
+            - col - returns the column number of the exception text
+            - line - returns the line containing the exception text
+        """
+        if( aname == "lineno" ):
+            return lineno( self.loc, self.pstr )
+        elif( aname in ("col", "column") ):
+            return col( self.loc, self.pstr )
+        elif( aname == "line" ):
+            return line( self.loc, self.pstr )
+        else:
+            raise AttributeError(aname)
+
+    def __str__( self ):
+        return "%s (at char %d), (line:%d, col:%d)" % \
+                ( self.msg, self.loc, self.lineno, self.column )
+    def __repr__( self ):
+        return _ustr(self)
+    def markInputline( self, markerString = ">!<" ):
+        """Extracts the exception line from the input string, and marks
+           the location of the exception with a special symbol.
+        """
+        line_str = self.line
+        line_column = self.column - 1
+        if markerString:
+            line_str = "".join((line_str[:line_column],
+                                markerString, line_str[line_column:]))
+        return line_str.strip()
+    def __dir__(self):
+        return "lineno col line".split() + dir(type(self))
+
+class ParseException(ParseBaseException):
+    """
+    Exception thrown when parse expressions don't match class;
+    supported attributes by name are:
+     - lineno - returns the line number of the exception text
+     - col - returns the column number of the exception text
+     - line - returns the line containing the exception text
+        
+    Example::
+        try:
+            Word(nums).setName("integer").parseString("ABC")
+        except ParseException as pe:
+            print(pe)
+            print("column: {}".format(pe.col))
+            
+    prints::
+       Expected integer (at char 0), (line:1, col:1)
+        column: 1
+    """
+    pass
+
+class ParseFatalException(ParseBaseException):
+    """user-throwable exception thrown when inconsistent parse content
+       is found; stops all parsing immediately"""
+    pass
+
+class ParseSyntaxException(ParseFatalException):
+    """just like L{ParseFatalException}, but thrown internally when an
+       L{ErrorStop} ('-' operator) indicates that parsing is to stop 
+       immediately because an unbacktrackable syntax error has been found"""
+    pass
+
+#~ class ReparseException(ParseBaseException):
+    #~ """Experimental class - parse actions can raise this exception to cause
+       #~ pyparsing to reparse the input string:
+        #~ - with a modified input string, and/or
+        #~ - with a modified start location
+       #~ Set the values of the ReparseException in the constructor, and raise the
+       #~ exception in a parse action to cause pyparsing to use the new string/location.
+       #~ Setting the values as None causes no change to be made.
+       #~ """
+    #~ def __init_( self, newstring, restartLoc ):
+        #~ self.newParseText = newstring
+        #~ self.reparseLoc = restartLoc
+
+class RecursiveGrammarException(Exception):
+    """exception thrown by L{ParserElement.validate} if the grammar could be improperly recursive"""
+    def __init__( self, parseElementList ):
+        self.parseElementTrace = parseElementList
+
+    def __str__( self ):
+        return "RecursiveGrammarException: %s" % self.parseElementTrace
+
+class _ParseResultsWithOffset(object):
+    def __init__(self,p1,p2):
+        self.tup = (p1,p2)
+    def __getitem__(self,i):
+        return self.tup[i]
+    def __repr__(self):
+        return repr(self.tup[0])
+    def setOffset(self,i):
+        self.tup = (self.tup[0],i)
+
+class ParseResults(object):
+    """
+    Structured parse results, to provide multiple means of access to the parsed data:
+       - as a list (C{len(results)})
+       - by list index (C{results[0], results[1]}, etc.)
+       - by attribute (C{results.} - see L{ParserElement.setResultsName})
+
+    Example::
+        integer = Word(nums)
+        date_str = (integer.setResultsName("year") + '/' 
+                        + integer.setResultsName("month") + '/' 
+                        + integer.setResultsName("day"))
+        # equivalent form:
+        # date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
+
+        # parseString returns a ParseResults object
+        result = date_str.parseString("1999/12/31")
+
+        def test(s, fn=repr):
+            print("%s -> %s" % (s, fn(eval(s))))
+        test("list(result)")
+        test("result[0]")
+        test("result['month']")
+        test("result.day")
+        test("'month' in result")
+        test("'minutes' in result")
+        test("result.dump()", str)
+    prints::
+        list(result) -> ['1999', '/', '12', '/', '31']
+        result[0] -> '1999'
+        result['month'] -> '12'
+        result.day -> '31'
+        'month' in result -> True
+        'minutes' in result -> False
+        result.dump() -> ['1999', '/', '12', '/', '31']
+        - day: 31
+        - month: 12
+        - year: 1999
+    """
+    def __new__(cls, toklist=None, name=None, asList=True, modal=True ):
+        if isinstance(toklist, cls):
+            return toklist
+        retobj = object.__new__(cls)
+        retobj.__doinit = True
+        return retobj
+
+    # Performance tuning: we construct a *lot* of these, so keep this
+    # constructor as small and fast as possible
+    def __init__( self, toklist=None, name=None, asList=True, modal=True, isinstance=isinstance ):
+        if self.__doinit:
+            self.__doinit = False
+            self.__name = None
+            self.__parent = None
+            self.__accumNames = {}
+            self.__asList = asList
+            self.__modal = modal
+            if toklist is None:
+                toklist = []
+            if isinstance(toklist, list):
+                self.__toklist = toklist[:]
+            elif isinstance(toklist, _generatorType):
+                self.__toklist = list(toklist)
+            else:
+                self.__toklist = [toklist]
+            self.__tokdict = dict()
+
+        if name is not None and name:
+            if not modal:
+                self.__accumNames[name] = 0
+            if isinstance(name,int):
+                name = _ustr(name) # will always return a str, but use _ustr for consistency
+            self.__name = name
+            if not (isinstance(toklist, (type(None), basestring, list)) and toklist in (None,'',[])):
+                if isinstance(toklist,basestring):
+                    toklist = [ toklist ]
+                if asList:
+                    if isinstance(toklist,ParseResults):
+                        self[name] = _ParseResultsWithOffset(toklist.copy(),0)
+                    else:
+                        self[name] = _ParseResultsWithOffset(ParseResults(toklist[0]),0)
+                    self[name].__name = name
+                else:
+                    try:
+                        self[name] = toklist[0]
+                    except (KeyError,TypeError,IndexError):
+                        self[name] = toklist
+
+    def __getitem__( self, i ):
+        if isinstance( i, (int,slice) ):
+            return self.__toklist[i]
+        else:
+            if i not in self.__accumNames:
+                return self.__tokdict[i][-1][0]
+            else:
+                return ParseResults([ v[0] for v in self.__tokdict[i] ])
+
+    def __setitem__( self, k, v, isinstance=isinstance ):
+        if isinstance(v,_ParseResultsWithOffset):
+            self.__tokdict[k] = self.__tokdict.get(k,list()) + [v]
+            sub = v[0]
+        elif isinstance(k,(int,slice)):
+            self.__toklist[k] = v
+            sub = v
+        else:
+            self.__tokdict[k] = self.__tokdict.get(k,list()) + [_ParseResultsWithOffset(v,0)]
+            sub = v
+        if isinstance(sub,ParseResults):
+            sub.__parent = wkref(self)
+
+    def __delitem__( self, i ):
+        if isinstance(i,(int,slice)):
+            mylen = len( self.__toklist )
+            del self.__toklist[i]
+
+            # convert int to slice
+            if isinstance(i, int):
+                if i < 0:
+                    i += mylen
+                i = slice(i, i+1)
+            # get removed indices
+            removed = list(range(*i.indices(mylen)))
+            removed.reverse()
+            # fixup indices in token dictionary
+            for name,occurrences in self.__tokdict.items():
+                for j in removed:
+                    for k, (value, position) in enumerate(occurrences):
+                        occurrences[k] = _ParseResultsWithOffset(value, position - (position > j))
+        else:
+            del self.__tokdict[i]
+
+    def __contains__( self, k ):
+        return k in self.__tokdict
+
+    def __len__( self ): return len( self.__toklist )
+    def __bool__(self): return ( not not self.__toklist )
+    __nonzero__ = __bool__
+    def __iter__( self ): return iter( self.__toklist )
+    def __reversed__( self ): return iter( self.__toklist[::-1] )
+    def _iterkeys( self ):
+        if hasattr(self.__tokdict, "iterkeys"):
+            return self.__tokdict.iterkeys()
+        else:
+            return iter(self.__tokdict)
+
+    def _itervalues( self ):
+        return (self[k] for k in self._iterkeys())
+            
+    def _iteritems( self ):
+        return ((k, self[k]) for k in self._iterkeys())
+
+    if PY_3:
+        keys = _iterkeys       
+        """Returns an iterator of all named result keys (Python 3.x only)."""
+
+        values = _itervalues
+        """Returns an iterator of all named result values (Python 3.x only)."""
+
+        items = _iteritems
+        """Returns an iterator of all named result key-value tuples (Python 3.x only)."""
+
+    else:
+        iterkeys = _iterkeys
+        """Returns an iterator of all named result keys (Python 2.x only)."""
+
+        itervalues = _itervalues
+        """Returns an iterator of all named result values (Python 2.x only)."""
+
+        iteritems = _iteritems
+        """Returns an iterator of all named result key-value tuples (Python 2.x only)."""
+
+        def keys( self ):
+            """Returns all named result keys (as a list in Python 2.x, as an iterator in Python 3.x)."""
+            return list(self.iterkeys())
+
+        def values( self ):
+            """Returns all named result values (as a list in Python 2.x, as an iterator in Python 3.x)."""
+            return list(self.itervalues())
+                
+        def items( self ):
+            """Returns all named result key-values (as a list of tuples in Python 2.x, as an iterator in Python 3.x)."""
+            return list(self.iteritems())
+
+    def haskeys( self ):
+        """Since keys() returns an iterator, this method is helpful in bypassing
+           code that looks for the existence of any defined results names."""
+        return bool(self.__tokdict)
+        
+    def pop( self, *args, **kwargs):
+        """
+        Removes and returns item at specified index (default=C{last}).
+        Supports both C{list} and C{dict} semantics for C{pop()}. If passed no
+        argument or an integer argument, it will use C{list} semantics
+        and pop tokens from the list of parsed tokens. If passed a 
+        non-integer argument (most likely a string), it will use C{dict}
+        semantics and pop the corresponding value from any defined 
+        results names. A second default return value argument is 
+        supported, just as in C{dict.pop()}.
+
+        Example::
+            def remove_first(tokens):
+                tokens.pop(0)
+            print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321']
+            print(OneOrMore(Word(nums)).addParseAction(remove_first).parseString("0 123 321")) # -> ['123', '321']
+
+            label = Word(alphas)
+            patt = label("LABEL") + OneOrMore(Word(nums))
+            print(patt.parseString("AAB 123 321").dump())
+
+            # Use pop() in a parse action to remove named result (note that corresponding value is not
+            # removed from list form of results)
+            def remove_LABEL(tokens):
+                tokens.pop("LABEL")
+                return tokens
+            patt.addParseAction(remove_LABEL)
+            print(patt.parseString("AAB 123 321").dump())
+        prints::
+            ['AAB', '123', '321']
+            - LABEL: AAB
+
+            ['AAB', '123', '321']
+        """
+        if not args:
+            args = [-1]
+        for k,v in kwargs.items():
+            if k == 'default':
+                args = (args[0], v)
+            else:
+                raise TypeError("pop() got an unexpected keyword argument '%s'" % k)
+        if (isinstance(args[0], int) or 
+                        len(args) == 1 or 
+                        args[0] in self):
+            index = args[0]
+            ret = self[index]
+            del self[index]
+            return ret
+        else:
+            defaultvalue = args[1]
+            return defaultvalue
+
+    def get(self, key, defaultValue=None):
+        """
+        Returns named result matching the given key, or if there is no
+        such name, then returns the given C{defaultValue} or C{None} if no
+        C{defaultValue} is specified.
+
+        Similar to C{dict.get()}.
+        
+        Example::
+            integer = Word(nums)
+            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")           
+
+            result = date_str.parseString("1999/12/31")
+            print(result.get("year")) # -> '1999'
+            print(result.get("hour", "not specified")) # -> 'not specified'
+            print(result.get("hour")) # -> None
+        """
+        if key in self:
+            return self[key]
+        else:
+            return defaultValue
+
+    def insert( self, index, insStr ):
+        """
+        Inserts new element at location index in the list of parsed tokens.
+        
+        Similar to C{list.insert()}.
+
+        Example::
+            print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321']
+
+            # use a parse action to insert the parse location in the front of the parsed results
+            def insert_locn(locn, tokens):
+                tokens.insert(0, locn)
+            print(OneOrMore(Word(nums)).addParseAction(insert_locn).parseString("0 123 321")) # -> [0, '0', '123', '321']
+        """
+        self.__toklist.insert(index, insStr)
+        # fixup indices in token dictionary
+        for name,occurrences in self.__tokdict.items():
+            for k, (value, position) in enumerate(occurrences):
+                occurrences[k] = _ParseResultsWithOffset(value, position + (position > index))
+
+    def append( self, item ):
+        """
+        Add single element to end of ParseResults list of elements.
+
+        Example::
+            print(OneOrMore(Word(nums)).parseString("0 123 321")) # -> ['0', '123', '321']
+            
+            # use a parse action to compute the sum of the parsed integers, and add it to the end
+            def append_sum(tokens):
+                tokens.append(sum(map(int, tokens)))
+            print(OneOrMore(Word(nums)).addParseAction(append_sum).parseString("0 123 321")) # -> ['0', '123', '321', 444]
+        """
+        self.__toklist.append(item)
+
+    def extend( self, itemseq ):
+        """
+        Add sequence of elements to end of ParseResults list of elements.
+
+        Example::
+            patt = OneOrMore(Word(alphas))
+            
+            # use a parse action to append the reverse of the matched strings, to make a palindrome
+            def make_palindrome(tokens):
+                tokens.extend(reversed([t[::-1] for t in tokens]))
+                return ''.join(tokens)
+            print(patt.addParseAction(make_palindrome).parseString("lskdj sdlkjf lksd")) # -> 'lskdjsdlkjflksddsklfjkldsjdksl'
+        """
+        if isinstance(itemseq, ParseResults):
+            self += itemseq
+        else:
+            self.__toklist.extend(itemseq)
+
+    def clear( self ):
+        """
+        Clear all elements and results names.
+        """
+        del self.__toklist[:]
+        self.__tokdict.clear()
+
+    def __getattr__( self, name ):
+        try:
+            return self[name]
+        except KeyError:
+            return ""
+            
+        if name in self.__tokdict:
+            if name not in self.__accumNames:
+                return self.__tokdict[name][-1][0]
+            else:
+                return ParseResults([ v[0] for v in self.__tokdict[name] ])
+        else:
+            return ""
+
+    def __add__( self, other ):
+        ret = self.copy()
+        ret += other
+        return ret
+
+    def __iadd__( self, other ):
+        if other.__tokdict:
+            offset = len(self.__toklist)
+            addoffset = lambda a: offset if a<0 else a+offset
+            otheritems = other.__tokdict.items()
+            otherdictitems = [(k, _ParseResultsWithOffset(v[0],addoffset(v[1])) )
+                                for (k,vlist) in otheritems for v in vlist]
+            for k,v in otherdictitems:
+                self[k] = v
+                if isinstance(v[0],ParseResults):
+                    v[0].__parent = wkref(self)
+            
+        self.__toklist += other.__toklist
+        self.__accumNames.update( other.__accumNames )
+        return self
+
+    def __radd__(self, other):
+        if isinstance(other,int) and other == 0:
+            # useful for merging many ParseResults using sum() builtin
+            return self.copy()
+        else:
+            # this may raise a TypeError - so be it
+            return other + self
+        
+    def __repr__( self ):
+        return "(%s, %s)" % ( repr( self.__toklist ), repr( self.__tokdict ) )
+
+    def __str__( self ):
+        return '[' + ', '.join(_ustr(i) if isinstance(i, ParseResults) else repr(i) for i in self.__toklist) + ']'
+
+    def _asStringList( self, sep='' ):
+        out = []
+        for item in self.__toklist:
+            if out and sep:
+                out.append(sep)
+            if isinstance( item, ParseResults ):
+                out += item._asStringList()
+            else:
+                out.append( _ustr(item) )
+        return out
+
+    def asList( self ):
+        """
+        Returns the parse results as a nested list of matching tokens, all converted to strings.
+
+        Example::
+            patt = OneOrMore(Word(alphas))
+            result = patt.parseString("sldkj lsdkj sldkj")
+            # even though the result prints in string-like form, it is actually a pyparsing ParseResults
+            print(type(result), result) # ->  ['sldkj', 'lsdkj', 'sldkj']
+            
+            # Use asList() to create an actual list
+            result_list = result.asList()
+            print(type(result_list), result_list) # ->  ['sldkj', 'lsdkj', 'sldkj']
+        """
+        return [res.asList() if isinstance(res,ParseResults) else res for res in self.__toklist]
+
+    def asDict( self ):
+        """
+        Returns the named parse results as a nested dictionary.
+
+        Example::
+            integer = Word(nums)
+            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
+            
+            result = date_str.parseString('12/31/1999')
+            print(type(result), repr(result)) # ->  (['12', '/', '31', '/', '1999'], {'day': [('1999', 4)], 'year': [('12', 0)], 'month': [('31', 2)]})
+            
+            result_dict = result.asDict()
+            print(type(result_dict), repr(result_dict)) # ->  {'day': '1999', 'year': '12', 'month': '31'}
+
+            # even though a ParseResults supports dict-like access, sometime you just need to have a dict
+            import json
+            print(json.dumps(result)) # -> Exception: TypeError: ... is not JSON serializable
+            print(json.dumps(result.asDict())) # -> {"month": "31", "day": "1999", "year": "12"}
+        """
+        if PY_3:
+            item_fn = self.items
+        else:
+            item_fn = self.iteritems
+            
+        def toItem(obj):
+            if isinstance(obj, ParseResults):
+                if obj.haskeys():
+                    return obj.asDict()
+                else:
+                    return [toItem(v) for v in obj]
+            else:
+                return obj
+                
+        return dict((k,toItem(v)) for k,v in item_fn())
+
+    def copy( self ):
+        """
+        Returns a new copy of a C{ParseResults} object.
+        """
+        ret = ParseResults( self.__toklist )
+        ret.__tokdict = self.__tokdict.copy()
+        ret.__parent = self.__parent
+        ret.__accumNames.update( self.__accumNames )
+        ret.__name = self.__name
+        return ret
+
+    def asXML( self, doctag=None, namedItemsOnly=False, indent="", formatted=True ):
+        """
+        (Deprecated) Returns the parse results as XML. Tags are created for tokens and lists that have defined results names.
+        """
+        nl = "\n"
+        out = []
+        namedItems = dict((v[1],k) for (k,vlist) in self.__tokdict.items()
+                                                            for v in vlist)
+        nextLevelIndent = indent + "  "
+
+        # collapse out indents if formatting is not desired
+        if not formatted:
+            indent = ""
+            nextLevelIndent = ""
+            nl = ""
+
+        selfTag = None
+        if doctag is not None:
+            selfTag = doctag
+        else:
+            if self.__name:
+                selfTag = self.__name
+
+        if not selfTag:
+            if namedItemsOnly:
+                return ""
+            else:
+                selfTag = "ITEM"
+
+        out += [ nl, indent, "<", selfTag, ">" ]
+
+        for i,res in enumerate(self.__toklist):
+            if isinstance(res,ParseResults):
+                if i in namedItems:
+                    out += [ res.asXML(namedItems[i],
+                                        namedItemsOnly and doctag is None,
+                                        nextLevelIndent,
+                                        formatted)]
+                else:
+                    out += [ res.asXML(None,
+                                        namedItemsOnly and doctag is None,
+                                        nextLevelIndent,
+                                        formatted)]
+            else:
+                # individual token, see if there is a name for it
+                resTag = None
+                if i in namedItems:
+                    resTag = namedItems[i]
+                if not resTag:
+                    if namedItemsOnly:
+                        continue
+                    else:
+                        resTag = "ITEM"
+                xmlBodyText = _xml_escape(_ustr(res))
+                out += [ nl, nextLevelIndent, "<", resTag, ">",
+                                                xmlBodyText,
+                                                "" ]
+
+        out += [ nl, indent, "" ]
+        return "".join(out)
+
+    def __lookup(self,sub):
+        for k,vlist in self.__tokdict.items():
+            for v,loc in vlist:
+                if sub is v:
+                    return k
+        return None
+
+    def getName(self):
+        r"""
+        Returns the results name for this token expression. Useful when several 
+        different expressions might match at a particular location.
+
+        Example::
+            integer = Word(nums)
+            ssn_expr = Regex(r"\d\d\d-\d\d-\d\d\d\d")
+            house_number_expr = Suppress('#') + Word(nums, alphanums)
+            user_data = (Group(house_number_expr)("house_number") 
+                        | Group(ssn_expr)("ssn")
+                        | Group(integer)("age"))
+            user_info = OneOrMore(user_data)
+            
+            result = user_info.parseString("22 111-22-3333 #221B")
+            for item in result:
+                print(item.getName(), ':', item[0])
+        prints::
+            age : 22
+            ssn : 111-22-3333
+            house_number : 221B
+        """
+        if self.__name:
+            return self.__name
+        elif self.__parent:
+            par = self.__parent()
+            if par:
+                return par.__lookup(self)
+            else:
+                return None
+        elif (len(self) == 1 and
+               len(self.__tokdict) == 1 and
+               next(iter(self.__tokdict.values()))[0][1] in (0,-1)):
+            return next(iter(self.__tokdict.keys()))
+        else:
+            return None
+
+    def dump(self, indent='', depth=0, full=True):
+        """
+        Diagnostic method for listing out the contents of a C{ParseResults}.
+        Accepts an optional C{indent} argument so that this string can be embedded
+        in a nested display of other data.
+
+        Example::
+            integer = Word(nums)
+            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
+            
+            result = date_str.parseString('12/31/1999')
+            print(result.dump())
+        prints::
+            ['12', '/', '31', '/', '1999']
+            - day: 1999
+            - month: 31
+            - year: 12
+        """
+        out = []
+        NL = '\n'
+        out.append( indent+_ustr(self.asList()) )
+        if full:
+            if self.haskeys():
+                items = sorted((str(k), v) for k,v in self.items())
+                for k,v in items:
+                    if out:
+                        out.append(NL)
+                    out.append( "%s%s- %s: " % (indent,('  '*depth), k) )
+                    if isinstance(v,ParseResults):
+                        if v:
+                            out.append( v.dump(indent,depth+1) )
+                        else:
+                            out.append(_ustr(v))
+                    else:
+                        out.append(repr(v))
+            elif any(isinstance(vv,ParseResults) for vv in self):
+                v = self
+                for i,vv in enumerate(v):
+                    if isinstance(vv,ParseResults):
+                        out.append("\n%s%s[%d]:\n%s%s%s" % (indent,('  '*(depth)),i,indent,('  '*(depth+1)),vv.dump(indent,depth+1) ))
+                    else:
+                        out.append("\n%s%s[%d]:\n%s%s%s" % (indent,('  '*(depth)),i,indent,('  '*(depth+1)),_ustr(vv)))
+            
+        return "".join(out)
+
+    def pprint(self, *args, **kwargs):
+        """
+        Pretty-printer for parsed results as a list, using the C{pprint} module.
+        Accepts additional positional or keyword args as defined for the 
+        C{pprint.pprint} method. (U{http://docs.python.org/3/library/pprint.html#pprint.pprint})
+
+        Example::
+            ident = Word(alphas, alphanums)
+            num = Word(nums)
+            func = Forward()
+            term = ident | num | Group('(' + func + ')')
+            func <<= ident + Group(Optional(delimitedList(term)))
+            result = func.parseString("fna a,b,(fnb c,d,200),100")
+            result.pprint(width=40)
+        prints::
+            ['fna',
+             ['a',
+              'b',
+              ['(', 'fnb', ['c', 'd', '200'], ')'],
+              '100']]
+        """
+        pprint.pprint(self.asList(), *args, **kwargs)
+
+    # add support for pickle protocol
+    def __getstate__(self):
+        return ( self.__toklist,
+                 ( self.__tokdict.copy(),
+                   self.__parent is not None and self.__parent() or None,
+                   self.__accumNames,
+                   self.__name ) )
+
+    def __setstate__(self,state):
+        self.__toklist = state[0]
+        (self.__tokdict,
+         par,
+         inAccumNames,
+         self.__name) = state[1]
+        self.__accumNames = {}
+        self.__accumNames.update(inAccumNames)
+        if par is not None:
+            self.__parent = wkref(par)
+        else:
+            self.__parent = None
+
+    def __getnewargs__(self):
+        return self.__toklist, self.__name, self.__asList, self.__modal
+
+    def __dir__(self):
+        return (dir(type(self)) + list(self.keys()))
+
+MutableMapping.register(ParseResults)
+
+def col (loc,strg):
+    """Returns current column within a string, counting newlines as line separators.
+   The first column is number 1.
+
+   Note: the default parsing behavior is to expand tabs in the input string
+   before starting the parsing process.  See L{I{ParserElement.parseString}} for more information
+   on parsing strings containing C{}s, and suggested methods to maintain a
+   consistent view of the parsed string, the parse location, and line and column
+   positions within the parsed string.
+   """
+    s = strg
+    return 1 if 0} for more information
+   on parsing strings containing C{}s, and suggested methods to maintain a
+   consistent view of the parsed string, the parse location, and line and column
+   positions within the parsed string.
+   """
+    return strg.count("\n",0,loc) + 1
+
+def line( loc, strg ):
+    """Returns the line of text containing loc within a string, counting newlines as line separators.
+       """
+    lastCR = strg.rfind("\n", 0, loc)
+    nextCR = strg.find("\n", loc)
+    if nextCR >= 0:
+        return strg[lastCR+1:nextCR]
+    else:
+        return strg[lastCR+1:]
+
+def _defaultStartDebugAction( instring, loc, expr ):
+    print (("Match " + _ustr(expr) + " at loc " + _ustr(loc) + "(%d,%d)" % ( lineno(loc,instring), col(loc,instring) )))
+
+def _defaultSuccessDebugAction( instring, startloc, endloc, expr, toks ):
+    print ("Matched " + _ustr(expr) + " -> " + str(toks.asList()))
+
+def _defaultExceptionDebugAction( instring, loc, expr, exc ):
+    print ("Exception raised:" + _ustr(exc))
+
+def nullDebugAction(*args):
+    """'Do-nothing' debug action, to suppress debugging output during parsing."""
+    pass
+
+# Only works on Python 3.x - nonlocal is toxic to Python 2 installs
+#~ 'decorator to trim function calls to match the arity of the target'
+#~ def _trim_arity(func, maxargs=3):
+    #~ if func in singleArgBuiltins:
+        #~ return lambda s,l,t: func(t)
+    #~ limit = 0
+    #~ foundArity = False
+    #~ def wrapper(*args):
+        #~ nonlocal limit,foundArity
+        #~ while 1:
+            #~ try:
+                #~ ret = func(*args[limit:])
+                #~ foundArity = True
+                #~ return ret
+            #~ except TypeError:
+                #~ if limit == maxargs or foundArity:
+                    #~ raise
+                #~ limit += 1
+                #~ continue
+    #~ return wrapper
+
+# this version is Python 2.x-3.x cross-compatible
+'decorator to trim function calls to match the arity of the target'
+def _trim_arity(func, maxargs=2):
+    if func in singleArgBuiltins:
+        return lambda s,l,t: func(t)
+    limit = [0]
+    foundArity = [False]
+    
+    # traceback return data structure changed in Py3.5 - normalize back to plain tuples
+    if system_version[:2] >= (3,5):
+        def extract_stack(limit=0):
+            # special handling for Python 3.5.0 - extra deep call stack by 1
+            offset = -3 if system_version == (3,5,0) else -2
+            frame_summary = traceback.extract_stack(limit=-offset+limit-1)[offset]
+            return [frame_summary[:2]]
+        def extract_tb(tb, limit=0):
+            frames = traceback.extract_tb(tb, limit=limit)
+            frame_summary = frames[-1]
+            return [frame_summary[:2]]
+    else:
+        extract_stack = traceback.extract_stack
+        extract_tb = traceback.extract_tb
+    
+    # synthesize what would be returned by traceback.extract_stack at the call to 
+    # user's parse action 'func', so that we don't incur call penalty at parse time
+    
+    LINE_DIFF = 6
+    # IF ANY CODE CHANGES, EVEN JUST COMMENTS OR BLANK LINES, BETWEEN THE NEXT LINE AND 
+    # THE CALL TO FUNC INSIDE WRAPPER, LINE_DIFF MUST BE MODIFIED!!!!
+    this_line = extract_stack(limit=2)[-1]
+    pa_call_line_synth = (this_line[0], this_line[1]+LINE_DIFF)
+
+    def wrapper(*args):
+        while 1:
+            try:
+                ret = func(*args[limit[0]:])
+                foundArity[0] = True
+                return ret
+            except TypeError:
+                # re-raise TypeErrors if they did not come from our arity testing
+                if foundArity[0]:
+                    raise
+                else:
+                    try:
+                        tb = sys.exc_info()[-1]
+                        if not extract_tb(tb, limit=2)[-1][:2] == pa_call_line_synth:
+                            raise
+                    finally:
+                        del tb
+
+                if limit[0] <= maxargs:
+                    limit[0] += 1
+                    continue
+                raise
+
+    # copy func name to wrapper for sensible debug output
+    func_name = ""
+    try:
+        func_name = getattr(func, '__name__', 
+                            getattr(func, '__class__').__name__)
+    except Exception:
+        func_name = str(func)
+    wrapper.__name__ = func_name
+
+    return wrapper
+
+class ParserElement(object):
+    """Abstract base level parser element class."""
+    DEFAULT_WHITE_CHARS = " \n\t\r"
+    verbose_stacktrace = False
+
+    @staticmethod
+    def setDefaultWhitespaceChars( chars ):
+        r"""
+        Overrides the default whitespace chars
+
+        Example::
+            # default whitespace chars are space,  and newline
+            OneOrMore(Word(alphas)).parseString("abc def\nghi jkl")  # -> ['abc', 'def', 'ghi', 'jkl']
+            
+            # change to just treat newline as significant
+            ParserElement.setDefaultWhitespaceChars(" \t")
+            OneOrMore(Word(alphas)).parseString("abc def\nghi jkl")  # -> ['abc', 'def']
+        """
+        ParserElement.DEFAULT_WHITE_CHARS = chars
+
+    @staticmethod
+    def inlineLiteralsUsing(cls):
+        """
+        Set class to be used for inclusion of string literals into a parser.
+        
+        Example::
+            # default literal class used is Literal
+            integer = Word(nums)
+            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")           
+
+            date_str.parseString("1999/12/31")  # -> ['1999', '/', '12', '/', '31']
+
+
+            # change to Suppress
+            ParserElement.inlineLiteralsUsing(Suppress)
+            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")           
+
+            date_str.parseString("1999/12/31")  # -> ['1999', '12', '31']
+        """
+        ParserElement._literalStringClass = cls
+
+    def __init__( self, savelist=False ):
+        self.parseAction = list()
+        self.failAction = None
+        #~ self.name = ""  # don't define self.name, let subclasses try/except upcall
+        self.strRepr = None
+        self.resultsName = None
+        self.saveAsList = savelist
+        self.skipWhitespace = True
+        self.whiteChars = ParserElement.DEFAULT_WHITE_CHARS
+        self.copyDefaultWhiteChars = True
+        self.mayReturnEmpty = False # used when checking for left-recursion
+        self.keepTabs = False
+        self.ignoreExprs = list()
+        self.debug = False
+        self.streamlined = False
+        self.mayIndexError = True # used to optimize exception handling for subclasses that don't advance parse index
+        self.errmsg = ""
+        self.modalResults = True # used to mark results names as modal (report only last) or cumulative (list all)
+        self.debugActions = ( None, None, None ) #custom debug actions
+        self.re = None
+        self.callPreparse = True # used to avoid redundant calls to preParse
+        self.callDuringTry = False
+
+    def copy( self ):
+        """
+        Make a copy of this C{ParserElement}.  Useful for defining different parse actions
+        for the same parsing pattern, using copies of the original parse element.
+        
+        Example::
+            integer = Word(nums).setParseAction(lambda toks: int(toks[0]))
+            integerK = integer.copy().addParseAction(lambda toks: toks[0]*1024) + Suppress("K")
+            integerM = integer.copy().addParseAction(lambda toks: toks[0]*1024*1024) + Suppress("M")
+            
+            print(OneOrMore(integerK | integerM | integer).parseString("5K 100 640K 256M"))
+        prints::
+            [5120, 100, 655360, 268435456]
+        Equivalent form of C{expr.copy()} is just C{expr()}::
+            integerM = integer().addParseAction(lambda toks: toks[0]*1024*1024) + Suppress("M")
+        """
+        cpy = copy.copy( self )
+        cpy.parseAction = self.parseAction[:]
+        cpy.ignoreExprs = self.ignoreExprs[:]
+        if self.copyDefaultWhiteChars:
+            cpy.whiteChars = ParserElement.DEFAULT_WHITE_CHARS
+        return cpy
+
+    def setName( self, name ):
+        """
+        Define name for this expression, makes debugging and exception messages clearer.
+        
+        Example::
+            Word(nums).parseString("ABC")  # -> Exception: Expected W:(0123...) (at char 0), (line:1, col:1)
+            Word(nums).setName("integer").parseString("ABC")  # -> Exception: Expected integer (at char 0), (line:1, col:1)
+        """
+        self.name = name
+        self.errmsg = "Expected " + self.name
+        if hasattr(self,"exception"):
+            self.exception.msg = self.errmsg
+        return self
+
+    def setResultsName( self, name, listAllMatches=False ):
+        """
+        Define name for referencing matching tokens as a nested attribute
+        of the returned parse results.
+        NOTE: this returns a *copy* of the original C{ParserElement} object;
+        this is so that the client can define a basic element, such as an
+        integer, and reference it in multiple places with different names.
+
+        You can also set results names using the abbreviated syntax,
+        C{expr("name")} in place of C{expr.setResultsName("name")} - 
+        see L{I{__call__}<__call__>}.
+
+        Example::
+            date_str = (integer.setResultsName("year") + '/' 
+                        + integer.setResultsName("month") + '/' 
+                        + integer.setResultsName("day"))
+
+            # equivalent form:
+            date_str = integer("year") + '/' + integer("month") + '/' + integer("day")
+        """
+        newself = self.copy()
+        if name.endswith("*"):
+            name = name[:-1]
+            listAllMatches=True
+        newself.resultsName = name
+        newself.modalResults = not listAllMatches
+        return newself
+
+    def setBreak(self,breakFlag = True):
+        """Method to invoke the Python pdb debugger when this element is
+           about to be parsed. Set C{breakFlag} to True to enable, False to
+           disable.
+        """
+        if breakFlag:
+            _parseMethod = self._parse
+            def breaker(instring, loc, doActions=True, callPreParse=True):
+                import pdb
+                pdb.set_trace()
+                return _parseMethod( instring, loc, doActions, callPreParse )
+            breaker._originalParseMethod = _parseMethod
+            self._parse = breaker
+        else:
+            if hasattr(self._parse,"_originalParseMethod"):
+                self._parse = self._parse._originalParseMethod
+        return self
+
+    def setParseAction( self, *fns, **kwargs ):
+        """
+        Define one or more actions to perform when successfully matching parse element definition.
+        Parse action fn is a callable method with 0-3 arguments, called as C{fn(s,loc,toks)},
+        C{fn(loc,toks)}, C{fn(toks)}, or just C{fn()}, where:
+         - s   = the original string being parsed (see note below)
+         - loc = the location of the matching substring
+         - toks = a list of the matched tokens, packaged as a C{L{ParseResults}} object
+        If the functions in fns modify the tokens, they can return them as the return
+        value from fn, and the modified list of tokens will replace the original.
+        Otherwise, fn does not need to return any value.
+
+        Optional keyword arguments:
+         - callDuringTry = (default=C{False}) indicate if parse action should be run during lookaheads and alternate testing
+
+        Note: the default parsing behavior is to expand tabs in the input string
+        before starting the parsing process.  See L{I{parseString}} for more information
+        on parsing strings containing C{}s, and suggested methods to maintain a
+        consistent view of the parsed string, the parse location, and line and column
+        positions within the parsed string.
+        
+        Example::
+            integer = Word(nums)
+            date_str = integer + '/' + integer + '/' + integer
+
+            date_str.parseString("1999/12/31")  # -> ['1999', '/', '12', '/', '31']
+
+            # use parse action to convert to ints at parse time
+            integer = Word(nums).setParseAction(lambda toks: int(toks[0]))
+            date_str = integer + '/' + integer + '/' + integer
+
+            # note that integer fields are now ints, not strings
+            date_str.parseString("1999/12/31")  # -> [1999, '/', 12, '/', 31]
+        """
+        self.parseAction = list(map(_trim_arity, list(fns)))
+        self.callDuringTry = kwargs.get("callDuringTry", False)
+        return self
+
+    def addParseAction( self, *fns, **kwargs ):
+        """
+        Add one or more parse actions to expression's list of parse actions. See L{I{setParseAction}}.
+        
+        See examples in L{I{copy}}.
+        """
+        self.parseAction += list(map(_trim_arity, list(fns)))
+        self.callDuringTry = self.callDuringTry or kwargs.get("callDuringTry", False)
+        return self
+
+    def addCondition(self, *fns, **kwargs):
+        """Add a boolean predicate function to expression's list of parse actions. See 
+        L{I{setParseAction}} for function call signatures. Unlike C{setParseAction}, 
+        functions passed to C{addCondition} need to return boolean success/fail of the condition.
+
+        Optional keyword arguments:
+         - message = define a custom message to be used in the raised exception
+         - fatal   = if True, will raise ParseFatalException to stop parsing immediately; otherwise will raise ParseException
+         
+        Example::
+            integer = Word(nums).setParseAction(lambda toks: int(toks[0]))
+            year_int = integer.copy()
+            year_int.addCondition(lambda toks: toks[0] >= 2000, message="Only support years 2000 and later")
+            date_str = year_int + '/' + integer + '/' + integer
+
+            result = date_str.parseString("1999/12/31")  # -> Exception: Only support years 2000 and later (at char 0), (line:1, col:1)
+        """
+        msg = kwargs.get("message", "failed user-defined condition")
+        exc_type = ParseFatalException if kwargs.get("fatal", False) else ParseException
+        for fn in fns:
+            def pa(s,l,t):
+                if not bool(_trim_arity(fn)(s,l,t)):
+                    raise exc_type(s,l,msg)
+            self.parseAction.append(pa)
+        self.callDuringTry = self.callDuringTry or kwargs.get("callDuringTry", False)
+        return self
+
+    def setFailAction( self, fn ):
+        """Define action to perform if parsing fails at this expression.
+           Fail acton fn is a callable function that takes the arguments
+           C{fn(s,loc,expr,err)} where:
+            - s = string being parsed
+            - loc = location where expression match was attempted and failed
+            - expr = the parse expression that failed
+            - err = the exception thrown
+           The function returns no value.  It may throw C{L{ParseFatalException}}
+           if it is desired to stop parsing immediately."""
+        self.failAction = fn
+        return self
+
+    def _skipIgnorables( self, instring, loc ):
+        exprsFound = True
+        while exprsFound:
+            exprsFound = False
+            for e in self.ignoreExprs:
+                try:
+                    while 1:
+                        loc,dummy = e._parse( instring, loc )
+                        exprsFound = True
+                except ParseException:
+                    pass
+        return loc
+
+    def preParse( self, instring, loc ):
+        if self.ignoreExprs:
+            loc = self._skipIgnorables( instring, loc )
+
+        if self.skipWhitespace:
+            wt = self.whiteChars
+            instrlen = len(instring)
+            while loc < instrlen and instring[loc] in wt:
+                loc += 1
+
+        return loc
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        return loc, []
+
+    def postParse( self, instring, loc, tokenlist ):
+        return tokenlist
+
+    #~ @profile
+    def _parseNoCache( self, instring, loc, doActions=True, callPreParse=True ):
+        debugging = ( self.debug ) #and doActions )
+
+        if debugging or self.failAction:
+            #~ print ("Match",self,"at loc",loc,"(%d,%d)" % ( lineno(loc,instring), col(loc,instring) ))
+            if (self.debugActions[0] ):
+                self.debugActions[0]( instring, loc, self )
+            if callPreParse and self.callPreparse:
+                preloc = self.preParse( instring, loc )
+            else:
+                preloc = loc
+            tokensStart = preloc
+            try:
+                try:
+                    loc,tokens = self.parseImpl( instring, preloc, doActions )
+                except IndexError:
+                    raise ParseException( instring, len(instring), self.errmsg, self )
+            except ParseBaseException as err:
+                #~ print ("Exception raised:", err)
+                if self.debugActions[2]:
+                    self.debugActions[2]( instring, tokensStart, self, err )
+                if self.failAction:
+                    self.failAction( instring, tokensStart, self, err )
+                raise
+        else:
+            if callPreParse and self.callPreparse:
+                preloc = self.preParse( instring, loc )
+            else:
+                preloc = loc
+            tokensStart = preloc
+            if self.mayIndexError or preloc >= len(instring):
+                try:
+                    loc,tokens = self.parseImpl( instring, preloc, doActions )
+                except IndexError:
+                    raise ParseException( instring, len(instring), self.errmsg, self )
+            else:
+                loc,tokens = self.parseImpl( instring, preloc, doActions )
+
+        tokens = self.postParse( instring, loc, tokens )
+
+        retTokens = ParseResults( tokens, self.resultsName, asList=self.saveAsList, modal=self.modalResults )
+        if self.parseAction and (doActions or self.callDuringTry):
+            if debugging:
+                try:
+                    for fn in self.parseAction:
+                        tokens = fn( instring, tokensStart, retTokens )
+                        if tokens is not None:
+                            retTokens = ParseResults( tokens,
+                                                      self.resultsName,
+                                                      asList=self.saveAsList and isinstance(tokens,(ParseResults,list)),
+                                                      modal=self.modalResults )
+                except ParseBaseException as err:
+                    #~ print "Exception raised in user parse action:", err
+                    if (self.debugActions[2] ):
+                        self.debugActions[2]( instring, tokensStart, self, err )
+                    raise
+            else:
+                for fn in self.parseAction:
+                    tokens = fn( instring, tokensStart, retTokens )
+                    if tokens is not None:
+                        retTokens = ParseResults( tokens,
+                                                  self.resultsName,
+                                                  asList=self.saveAsList and isinstance(tokens,(ParseResults,list)),
+                                                  modal=self.modalResults )
+        if debugging:
+            #~ print ("Matched",self,"->",retTokens.asList())
+            if (self.debugActions[1] ):
+                self.debugActions[1]( instring, tokensStart, loc, self, retTokens )
+
+        return loc, retTokens
+
+    def tryParse( self, instring, loc ):
+        try:
+            return self._parse( instring, loc, doActions=False )[0]
+        except ParseFatalException:
+            raise ParseException( instring, loc, self.errmsg, self)
+    
+    def canParseNext(self, instring, loc):
+        try:
+            self.tryParse(instring, loc)
+        except (ParseException, IndexError):
+            return False
+        else:
+            return True
+
+    class _UnboundedCache(object):
+        def __init__(self):
+            cache = {}
+            self.not_in_cache = not_in_cache = object()
+
+            def get(self, key):
+                return cache.get(key, not_in_cache)
+
+            def set(self, key, value):
+                cache[key] = value
+
+            def clear(self):
+                cache.clear()
+                
+            def cache_len(self):
+                return len(cache)
+
+            self.get = types.MethodType(get, self)
+            self.set = types.MethodType(set, self)
+            self.clear = types.MethodType(clear, self)
+            self.__len__ = types.MethodType(cache_len, self)
+
+    if _OrderedDict is not None:
+        class _FifoCache(object):
+            def __init__(self, size):
+                self.not_in_cache = not_in_cache = object()
+
+                cache = _OrderedDict()
+
+                def get(self, key):
+                    return cache.get(key, not_in_cache)
+
+                def set(self, key, value):
+                    cache[key] = value
+                    while len(cache) > size:
+                        try:
+                            cache.popitem(False)
+                        except KeyError:
+                            pass
+
+                def clear(self):
+                    cache.clear()
+
+                def cache_len(self):
+                    return len(cache)
+
+                self.get = types.MethodType(get, self)
+                self.set = types.MethodType(set, self)
+                self.clear = types.MethodType(clear, self)
+                self.__len__ = types.MethodType(cache_len, self)
+
+    else:
+        class _FifoCache(object):
+            def __init__(self, size):
+                self.not_in_cache = not_in_cache = object()
+
+                cache = {}
+                key_fifo = collections.deque([], size)
+
+                def get(self, key):
+                    return cache.get(key, not_in_cache)
+
+                def set(self, key, value):
+                    cache[key] = value
+                    while len(key_fifo) > size:
+                        cache.pop(key_fifo.popleft(), None)
+                    key_fifo.append(key)
+
+                def clear(self):
+                    cache.clear()
+                    key_fifo.clear()
+
+                def cache_len(self):
+                    return len(cache)
+
+                self.get = types.MethodType(get, self)
+                self.set = types.MethodType(set, self)
+                self.clear = types.MethodType(clear, self)
+                self.__len__ = types.MethodType(cache_len, self)
+
+    # argument cache for optimizing repeated calls when backtracking through recursive expressions
+    packrat_cache = {} # this is set later by enabledPackrat(); this is here so that resetCache() doesn't fail
+    packrat_cache_lock = RLock()
+    packrat_cache_stats = [0, 0]
+
+    # this method gets repeatedly called during backtracking with the same arguments -
+    # we can cache these arguments and save ourselves the trouble of re-parsing the contained expression
+    def _parseCache( self, instring, loc, doActions=True, callPreParse=True ):
+        HIT, MISS = 0, 1
+        lookup = (self, instring, loc, callPreParse, doActions)
+        with ParserElement.packrat_cache_lock:
+            cache = ParserElement.packrat_cache
+            value = cache.get(lookup)
+            if value is cache.not_in_cache:
+                ParserElement.packrat_cache_stats[MISS] += 1
+                try:
+                    value = self._parseNoCache(instring, loc, doActions, callPreParse)
+                except ParseBaseException as pe:
+                    # cache a copy of the exception, without the traceback
+                    cache.set(lookup, pe.__class__(*pe.args))
+                    raise
+                else:
+                    cache.set(lookup, (value[0], value[1].copy()))
+                    return value
+            else:
+                ParserElement.packrat_cache_stats[HIT] += 1
+                if isinstance(value, Exception):
+                    raise value
+                return (value[0], value[1].copy())
+
+    _parse = _parseNoCache
+
+    @staticmethod
+    def resetCache():
+        ParserElement.packrat_cache.clear()
+        ParserElement.packrat_cache_stats[:] = [0] * len(ParserElement.packrat_cache_stats)
+
+    _packratEnabled = False
+    @staticmethod
+    def enablePackrat(cache_size_limit=128):
+        """Enables "packrat" parsing, which adds memoizing to the parsing logic.
+           Repeated parse attempts at the same string location (which happens
+           often in many complex grammars) can immediately return a cached value,
+           instead of re-executing parsing/validating code.  Memoizing is done of
+           both valid results and parsing exceptions.
+           
+           Parameters:
+            - cache_size_limit - (default=C{128}) - if an integer value is provided
+              will limit the size of the packrat cache; if None is passed, then
+              the cache size will be unbounded; if 0 is passed, the cache will
+              be effectively disabled.
+            
+           This speedup may break existing programs that use parse actions that
+           have side-effects.  For this reason, packrat parsing is disabled when
+           you first import pyparsing.  To activate the packrat feature, your
+           program must call the class method C{ParserElement.enablePackrat()}.  If
+           your program uses C{psyco} to "compile as you go", you must call
+           C{enablePackrat} before calling C{psyco.full()}.  If you do not do this,
+           Python will crash.  For best results, call C{enablePackrat()} immediately
+           after importing pyparsing.
+           
+           Example::
+               import pyparsing
+               pyparsing.ParserElement.enablePackrat()
+        """
+        if not ParserElement._packratEnabled:
+            ParserElement._packratEnabled = True
+            if cache_size_limit is None:
+                ParserElement.packrat_cache = ParserElement._UnboundedCache()
+            else:
+                ParserElement.packrat_cache = ParserElement._FifoCache(cache_size_limit)
+            ParserElement._parse = ParserElement._parseCache
+
+    def parseString( self, instring, parseAll=False ):
+        """
+        Execute the parse expression with the given string.
+        This is the main interface to the client code, once the complete
+        expression has been built.
+
+        If you want the grammar to require that the entire input string be
+        successfully parsed, then set C{parseAll} to True (equivalent to ending
+        the grammar with C{L{StringEnd()}}).
+
+        Note: C{parseString} implicitly calls C{expandtabs()} on the input string,
+        in order to report proper column numbers in parse actions.
+        If the input string contains tabs and
+        the grammar uses parse actions that use the C{loc} argument to index into the
+        string being parsed, you can ensure you have a consistent view of the input
+        string by:
+         - calling C{parseWithTabs} on your grammar before calling C{parseString}
+           (see L{I{parseWithTabs}})
+         - define your parse action using the full C{(s,loc,toks)} signature, and
+           reference the input string using the parse action's C{s} argument
+         - explictly expand the tabs in your input string before calling
+           C{parseString}
+        
+        Example::
+            Word('a').parseString('aaaaabaaa')  # -> ['aaaaa']
+            Word('a').parseString('aaaaabaaa', parseAll=True)  # -> Exception: Expected end of text
+        """
+        ParserElement.resetCache()
+        if not self.streamlined:
+            self.streamline()
+            #~ self.saveAsList = True
+        for e in self.ignoreExprs:
+            e.streamline()
+        if not self.keepTabs:
+            instring = instring.expandtabs()
+        try:
+            loc, tokens = self._parse( instring, 0 )
+            if parseAll:
+                loc = self.preParse( instring, loc )
+                se = Empty() + StringEnd()
+                se._parse( instring, loc )
+        except ParseBaseException as exc:
+            if ParserElement.verbose_stacktrace:
+                raise
+            else:
+                # catch and re-raise exception from here, clears out pyparsing internal stack trace
+                raise exc
+        else:
+            return tokens
+
+    def scanString( self, instring, maxMatches=_MAX_INT, overlap=False ):
+        """
+        Scan the input string for expression matches.  Each match will return the
+        matching tokens, start location, and end location.  May be called with optional
+        C{maxMatches} argument, to clip scanning after 'n' matches are found.  If
+        C{overlap} is specified, then overlapping matches will be reported.
+
+        Note that the start and end locations are reported relative to the string
+        being parsed.  See L{I{parseString}} for more information on parsing
+        strings with embedded tabs.
+
+        Example::
+            source = "sldjf123lsdjjkf345sldkjf879lkjsfd987"
+            print(source)
+            for tokens,start,end in Word(alphas).scanString(source):
+                print(' '*start + '^'*(end-start))
+                print(' '*start + tokens[0])
+        
+        prints::
+        
+            sldjf123lsdjjkf345sldkjf879lkjsfd987
+            ^^^^^
+            sldjf
+                    ^^^^^^^
+                    lsdjjkf
+                              ^^^^^^
+                              sldkjf
+                                       ^^^^^^
+                                       lkjsfd
+        """
+        if not self.streamlined:
+            self.streamline()
+        for e in self.ignoreExprs:
+            e.streamline()
+
+        if not self.keepTabs:
+            instring = _ustr(instring).expandtabs()
+        instrlen = len(instring)
+        loc = 0
+        preparseFn = self.preParse
+        parseFn = self._parse
+        ParserElement.resetCache()
+        matches = 0
+        try:
+            while loc <= instrlen and matches < maxMatches:
+                try:
+                    preloc = preparseFn( instring, loc )
+                    nextLoc,tokens = parseFn( instring, preloc, callPreParse=False )
+                except ParseException:
+                    loc = preloc+1
+                else:
+                    if nextLoc > loc:
+                        matches += 1
+                        yield tokens, preloc, nextLoc
+                        if overlap:
+                            nextloc = preparseFn( instring, loc )
+                            if nextloc > loc:
+                                loc = nextLoc
+                            else:
+                                loc += 1
+                        else:
+                            loc = nextLoc
+                    else:
+                        loc = preloc+1
+        except ParseBaseException as exc:
+            if ParserElement.verbose_stacktrace:
+                raise
+            else:
+                # catch and re-raise exception from here, clears out pyparsing internal stack trace
+                raise exc
+
+    def transformString( self, instring ):
+        """
+        Extension to C{L{scanString}}, to modify matching text with modified tokens that may
+        be returned from a parse action.  To use C{transformString}, define a grammar and
+        attach a parse action to it that modifies the returned token list.
+        Invoking C{transformString()} on a target string will then scan for matches,
+        and replace the matched text patterns according to the logic in the parse
+        action.  C{transformString()} returns the resulting transformed string.
+        
+        Example::
+            wd = Word(alphas)
+            wd.setParseAction(lambda toks: toks[0].title())
+            
+            print(wd.transformString("now is the winter of our discontent made glorious summer by this sun of york."))
+        Prints::
+            Now Is The Winter Of Our Discontent Made Glorious Summer By This Sun Of York.
+        """
+        out = []
+        lastE = 0
+        # force preservation of s, to minimize unwanted transformation of string, and to
+        # keep string locs straight between transformString and scanString
+        self.keepTabs = True
+        try:
+            for t,s,e in self.scanString( instring ):
+                out.append( instring[lastE:s] )
+                if t:
+                    if isinstance(t,ParseResults):
+                        out += t.asList()
+                    elif isinstance(t,list):
+                        out += t
+                    else:
+                        out.append(t)
+                lastE = e
+            out.append(instring[lastE:])
+            out = [o for o in out if o]
+            return "".join(map(_ustr,_flatten(out)))
+        except ParseBaseException as exc:
+            if ParserElement.verbose_stacktrace:
+                raise
+            else:
+                # catch and re-raise exception from here, clears out pyparsing internal stack trace
+                raise exc
+
+    def searchString( self, instring, maxMatches=_MAX_INT ):
+        """
+        Another extension to C{L{scanString}}, simplifying the access to the tokens found
+        to match the given parse expression.  May be called with optional
+        C{maxMatches} argument, to clip searching after 'n' matches are found.
+        
+        Example::
+            # a capitalized word starts with an uppercase letter, followed by zero or more lowercase letters
+            cap_word = Word(alphas.upper(), alphas.lower())
+            
+            print(cap_word.searchString("More than Iron, more than Lead, more than Gold I need Electricity"))
+
+            # the sum() builtin can be used to merge results into a single ParseResults object
+            print(sum(cap_word.searchString("More than Iron, more than Lead, more than Gold I need Electricity")))
+        prints::
+            [['More'], ['Iron'], ['Lead'], ['Gold'], ['I'], ['Electricity']]
+            ['More', 'Iron', 'Lead', 'Gold', 'I', 'Electricity']
+        """
+        try:
+            return ParseResults([ t for t,s,e in self.scanString( instring, maxMatches ) ])
+        except ParseBaseException as exc:
+            if ParserElement.verbose_stacktrace:
+                raise
+            else:
+                # catch and re-raise exception from here, clears out pyparsing internal stack trace
+                raise exc
+
+    def split(self, instring, maxsplit=_MAX_INT, includeSeparators=False):
+        """
+        Generator method to split a string using the given expression as a separator.
+        May be called with optional C{maxsplit} argument, to limit the number of splits;
+        and the optional C{includeSeparators} argument (default=C{False}), if the separating
+        matching text should be included in the split results.
+        
+        Example::        
+            punc = oneOf(list(".,;:/-!?"))
+            print(list(punc.split("This, this?, this sentence, is badly punctuated!")))
+        prints::
+            ['This', ' this', '', ' this sentence', ' is badly punctuated', '']
+        """
+        splits = 0
+        last = 0
+        for t,s,e in self.scanString(instring, maxMatches=maxsplit):
+            yield instring[last:s]
+            if includeSeparators:
+                yield t[0]
+            last = e
+        yield instring[last:]
+
+    def __add__(self, other ):
+        """
+        Implementation of + operator - returns C{L{And}}. Adding strings to a ParserElement
+        converts them to L{Literal}s by default.
+        
+        Example::
+            greet = Word(alphas) + "," + Word(alphas) + "!"
+            hello = "Hello, World!"
+            print (hello, "->", greet.parseString(hello))
+        Prints::
+            Hello, World! -> ['Hello', ',', 'World', '!']
+        """
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return And( [ self, other ] )
+
+    def __radd__(self, other ):
+        """
+        Implementation of + operator when left operand is not a C{L{ParserElement}}
+        """
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return other + self
+
+    def __sub__(self, other):
+        """
+        Implementation of - operator, returns C{L{And}} with error stop
+        """
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return self + And._ErrorStop() + other
+
+    def __rsub__(self, other ):
+        """
+        Implementation of - operator when left operand is not a C{L{ParserElement}}
+        """
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return other - self
+
+    def __mul__(self,other):
+        """
+        Implementation of * operator, allows use of C{expr * 3} in place of
+        C{expr + expr + expr}.  Expressions may also me multiplied by a 2-integer
+        tuple, similar to C{{min,max}} multipliers in regular expressions.  Tuples
+        may also include C{None} as in:
+         - C{expr*(n,None)} or C{expr*(n,)} is equivalent
+              to C{expr*n + L{ZeroOrMore}(expr)}
+              (read as "at least n instances of C{expr}")
+         - C{expr*(None,n)} is equivalent to C{expr*(0,n)}
+              (read as "0 to n instances of C{expr}")
+         - C{expr*(None,None)} is equivalent to C{L{ZeroOrMore}(expr)}
+         - C{expr*(1,None)} is equivalent to C{L{OneOrMore}(expr)}
+
+        Note that C{expr*(None,n)} does not raise an exception if
+        more than n exprs exist in the input stream; that is,
+        C{expr*(None,n)} does not enforce a maximum number of expr
+        occurrences.  If this behavior is desired, then write
+        C{expr*(None,n) + ~expr}
+        """
+        if isinstance(other,int):
+            minElements, optElements = other,0
+        elif isinstance(other,tuple):
+            other = (other + (None, None))[:2]
+            if other[0] is None:
+                other = (0, other[1])
+            if isinstance(other[0],int) and other[1] is None:
+                if other[0] == 0:
+                    return ZeroOrMore(self)
+                if other[0] == 1:
+                    return OneOrMore(self)
+                else:
+                    return self*other[0] + ZeroOrMore(self)
+            elif isinstance(other[0],int) and isinstance(other[1],int):
+                minElements, optElements = other
+                optElements -= minElements
+            else:
+                raise TypeError("cannot multiply 'ParserElement' and ('%s','%s') objects", type(other[0]),type(other[1]))
+        else:
+            raise TypeError("cannot multiply 'ParserElement' and '%s' objects", type(other))
+
+        if minElements < 0:
+            raise ValueError("cannot multiply ParserElement by negative value")
+        if optElements < 0:
+            raise ValueError("second tuple value must be greater or equal to first tuple value")
+        if minElements == optElements == 0:
+            raise ValueError("cannot multiply ParserElement by 0 or (0,0)")
+
+        if (optElements):
+            def makeOptionalList(n):
+                if n>1:
+                    return Optional(self + makeOptionalList(n-1))
+                else:
+                    return Optional(self)
+            if minElements:
+                if minElements == 1:
+                    ret = self + makeOptionalList(optElements)
+                else:
+                    ret = And([self]*minElements) + makeOptionalList(optElements)
+            else:
+                ret = makeOptionalList(optElements)
+        else:
+            if minElements == 1:
+                ret = self
+            else:
+                ret = And([self]*minElements)
+        return ret
+
+    def __rmul__(self, other):
+        return self.__mul__(other)
+
+    def __or__(self, other ):
+        """
+        Implementation of | operator - returns C{L{MatchFirst}}
+        """
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return MatchFirst( [ self, other ] )
+
+    def __ror__(self, other ):
+        """
+        Implementation of | operator when left operand is not a C{L{ParserElement}}
+        """
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return other | self
+
+    def __xor__(self, other ):
+        """
+        Implementation of ^ operator - returns C{L{Or}}
+        """
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return Or( [ self, other ] )
+
+    def __rxor__(self, other ):
+        """
+        Implementation of ^ operator when left operand is not a C{L{ParserElement}}
+        """
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return other ^ self
+
+    def __and__(self, other ):
+        """
+        Implementation of & operator - returns C{L{Each}}
+        """
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return Each( [ self, other ] )
+
+    def __rand__(self, other ):
+        """
+        Implementation of & operator when left operand is not a C{L{ParserElement}}
+        """
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        if not isinstance( other, ParserElement ):
+            warnings.warn("Cannot combine element of type %s with ParserElement" % type(other),
+                    SyntaxWarning, stacklevel=2)
+            return None
+        return other & self
+
+    def __invert__( self ):
+        """
+        Implementation of ~ operator - returns C{L{NotAny}}
+        """
+        return NotAny( self )
+
+    def __call__(self, name=None):
+        """
+        Shortcut for C{L{setResultsName}}, with C{listAllMatches=False}.
+        
+        If C{name} is given with a trailing C{'*'} character, then C{listAllMatches} will be
+        passed as C{True}.
+           
+        If C{name} is omitted, same as calling C{L{copy}}.
+
+        Example::
+            # these are equivalent
+            userdata = Word(alphas).setResultsName("name") + Word(nums+"-").setResultsName("socsecno")
+            userdata = Word(alphas)("name") + Word(nums+"-")("socsecno")             
+        """
+        if name is not None:
+            return self.setResultsName(name)
+        else:
+            return self.copy()
+
+    def suppress( self ):
+        """
+        Suppresses the output of this C{ParserElement}; useful to keep punctuation from
+        cluttering up returned output.
+        """
+        return Suppress( self )
+
+    def leaveWhitespace( self ):
+        """
+        Disables the skipping of whitespace before matching the characters in the
+        C{ParserElement}'s defined pattern.  This is normally only used internally by
+        the pyparsing module, but may be needed in some whitespace-sensitive grammars.
+        """
+        self.skipWhitespace = False
+        return self
+
+    def setWhitespaceChars( self, chars ):
+        """
+        Overrides the default whitespace chars
+        """
+        self.skipWhitespace = True
+        self.whiteChars = chars
+        self.copyDefaultWhiteChars = False
+        return self
+
+    def parseWithTabs( self ):
+        """
+        Overrides default behavior to expand C{}s to spaces before parsing the input string.
+        Must be called before C{parseString} when the input grammar contains elements that
+        match C{} characters.
+        """
+        self.keepTabs = True
+        return self
+
+    def ignore( self, other ):
+        """
+        Define expression to be ignored (e.g., comments) while doing pattern
+        matching; may be called repeatedly, to define multiple comment or other
+        ignorable patterns.
+        
+        Example::
+            patt = OneOrMore(Word(alphas))
+            patt.parseString('ablaj /* comment */ lskjd') # -> ['ablaj']
+            
+            patt.ignore(cStyleComment)
+            patt.parseString('ablaj /* comment */ lskjd') # -> ['ablaj', 'lskjd']
+        """
+        if isinstance(other, basestring):
+            other = Suppress(other)
+
+        if isinstance( other, Suppress ):
+            if other not in self.ignoreExprs:
+                self.ignoreExprs.append(other)
+        else:
+            self.ignoreExprs.append( Suppress( other.copy() ) )
+        return self
+
+    def setDebugActions( self, startAction, successAction, exceptionAction ):
+        """
+        Enable display of debugging messages while doing pattern matching.
+        """
+        self.debugActions = (startAction or _defaultStartDebugAction,
+                             successAction or _defaultSuccessDebugAction,
+                             exceptionAction or _defaultExceptionDebugAction)
+        self.debug = True
+        return self
+
+    def setDebug( self, flag=True ):
+        """
+        Enable display of debugging messages while doing pattern matching.
+        Set C{flag} to True to enable, False to disable.
+
+        Example::
+            wd = Word(alphas).setName("alphaword")
+            integer = Word(nums).setName("numword")
+            term = wd | integer
+            
+            # turn on debugging for wd
+            wd.setDebug()
+
+            OneOrMore(term).parseString("abc 123 xyz 890")
+        
+        prints::
+            Match alphaword at loc 0(1,1)
+            Matched alphaword -> ['abc']
+            Match alphaword at loc 3(1,4)
+            Exception raised:Expected alphaword (at char 4), (line:1, col:5)
+            Match alphaword at loc 7(1,8)
+            Matched alphaword -> ['xyz']
+            Match alphaword at loc 11(1,12)
+            Exception raised:Expected alphaword (at char 12), (line:1, col:13)
+            Match alphaword at loc 15(1,16)
+            Exception raised:Expected alphaword (at char 15), (line:1, col:16)
+
+        The output shown is that produced by the default debug actions - custom debug actions can be
+        specified using L{setDebugActions}. Prior to attempting
+        to match the C{wd} expression, the debugging message C{"Match  at loc (,)"}
+        is shown. Then if the parse succeeds, a C{"Matched"} message is shown, or an C{"Exception raised"}
+        message is shown. Also note the use of L{setName} to assign a human-readable name to the expression,
+        which makes debugging and exception messages easier to understand - for instance, the default
+        name created for the C{Word} expression without calling C{setName} is C{"W:(ABCD...)"}.
+        """
+        if flag:
+            self.setDebugActions( _defaultStartDebugAction, _defaultSuccessDebugAction, _defaultExceptionDebugAction )
+        else:
+            self.debug = False
+        return self
+
+    def __str__( self ):
+        return self.name
+
+    def __repr__( self ):
+        return _ustr(self)
+
+    def streamline( self ):
+        self.streamlined = True
+        self.strRepr = None
+        return self
+
+    def checkRecursion( self, parseElementList ):
+        pass
+
+    def validate( self, validateTrace=[] ):
+        """
+        Check defined expressions for valid structure, check for infinite recursive definitions.
+        """
+        self.checkRecursion( [] )
+
+    def parseFile( self, file_or_filename, parseAll=False ):
+        """
+        Execute the parse expression on the given file or filename.
+        If a filename is specified (instead of a file object),
+        the entire file is opened, read, and closed before parsing.
+        """
+        try:
+            file_contents = file_or_filename.read()
+        except AttributeError:
+            with open(file_or_filename, "r") as f:
+                file_contents = f.read()
+        try:
+            return self.parseString(file_contents, parseAll)
+        except ParseBaseException as exc:
+            if ParserElement.verbose_stacktrace:
+                raise
+            else:
+                # catch and re-raise exception from here, clears out pyparsing internal stack trace
+                raise exc
+
+    def __eq__(self,other):
+        if isinstance(other, ParserElement):
+            return self is other or vars(self) == vars(other)
+        elif isinstance(other, basestring):
+            return self.matches(other)
+        else:
+            return super(ParserElement,self)==other
+
+    def __ne__(self,other):
+        return not (self == other)
+
+    def __hash__(self):
+        return hash(id(self))
+
+    def __req__(self,other):
+        return self == other
+
+    def __rne__(self,other):
+        return not (self == other)
+
+    def matches(self, testString, parseAll=True):
+        """
+        Method for quick testing of a parser against a test string. Good for simple 
+        inline microtests of sub expressions while building up larger parser.
+           
+        Parameters:
+         - testString - to test against this expression for a match
+         - parseAll - (default=C{True}) - flag to pass to C{L{parseString}} when running tests
+            
+        Example::
+            expr = Word(nums)
+            assert expr.matches("100")
+        """
+        try:
+            self.parseString(_ustr(testString), parseAll=parseAll)
+            return True
+        except ParseBaseException:
+            return False
+                
+    def runTests(self, tests, parseAll=True, comment='#', fullDump=True, printResults=True, failureTests=False):
+        """
+        Execute the parse expression on a series of test strings, showing each
+        test, the parsed results or where the parse failed. Quick and easy way to
+        run a parse expression against a list of sample strings.
+           
+        Parameters:
+         - tests - a list of separate test strings, or a multiline string of test strings
+         - parseAll - (default=C{True}) - flag to pass to C{L{parseString}} when running tests           
+         - comment - (default=C{'#'}) - expression for indicating embedded comments in the test 
+              string; pass None to disable comment filtering
+         - fullDump - (default=C{True}) - dump results as list followed by results names in nested outline;
+              if False, only dump nested list
+         - printResults - (default=C{True}) prints test output to stdout
+         - failureTests - (default=C{False}) indicates if these tests are expected to fail parsing
+
+        Returns: a (success, results) tuple, where success indicates that all tests succeeded
+        (or failed if C{failureTests} is True), and the results contain a list of lines of each 
+        test's output
+        
+        Example::
+            number_expr = pyparsing_common.number.copy()
+
+            result = number_expr.runTests('''
+                # unsigned integer
+                100
+                # negative integer
+                -100
+                # float with scientific notation
+                6.02e23
+                # integer with scientific notation
+                1e-12
+                ''')
+            print("Success" if result[0] else "Failed!")
+
+            result = number_expr.runTests('''
+                # stray character
+                100Z
+                # missing leading digit before '.'
+                -.100
+                # too many '.'
+                3.14.159
+                ''', failureTests=True)
+            print("Success" if result[0] else "Failed!")
+        prints::
+            # unsigned integer
+            100
+            [100]
+
+            # negative integer
+            -100
+            [-100]
+
+            # float with scientific notation
+            6.02e23
+            [6.02e+23]
+
+            # integer with scientific notation
+            1e-12
+            [1e-12]
+
+            Success
+            
+            # stray character
+            100Z
+               ^
+            FAIL: Expected end of text (at char 3), (line:1, col:4)
+
+            # missing leading digit before '.'
+            -.100
+            ^
+            FAIL: Expected {real number with scientific notation | real number | signed integer} (at char 0), (line:1, col:1)
+
+            # too many '.'
+            3.14.159
+                ^
+            FAIL: Expected end of text (at char 4), (line:1, col:5)
+
+            Success
+
+        Each test string must be on a single line. If you want to test a string that spans multiple
+        lines, create a test like this::
+
+            expr.runTest(r"this is a test\\n of strings that spans \\n 3 lines")
+        
+        (Note that this is a raw string literal, you must include the leading 'r'.)
+        """
+        if isinstance(tests, basestring):
+            tests = list(map(str.strip, tests.rstrip().splitlines()))
+        if isinstance(comment, basestring):
+            comment = Literal(comment)
+        allResults = []
+        comments = []
+        success = True
+        for t in tests:
+            if comment is not None and comment.matches(t, False) or comments and not t:
+                comments.append(t)
+                continue
+            if not t:
+                continue
+            out = ['\n'.join(comments), t]
+            comments = []
+            try:
+                t = t.replace(r'\n','\n')
+                result = self.parseString(t, parseAll=parseAll)
+                out.append(result.dump(full=fullDump))
+                success = success and not failureTests
+            except ParseBaseException as pe:
+                fatal = "(FATAL)" if isinstance(pe, ParseFatalException) else ""
+                if '\n' in t:
+                    out.append(line(pe.loc, t))
+                    out.append(' '*(col(pe.loc,t)-1) + '^' + fatal)
+                else:
+                    out.append(' '*pe.loc + '^' + fatal)
+                out.append("FAIL: " + str(pe))
+                success = success and failureTests
+                result = pe
+            except Exception as exc:
+                out.append("FAIL-EXCEPTION: " + str(exc))
+                success = success and failureTests
+                result = exc
+
+            if printResults:
+                if fullDump:
+                    out.append('')
+                print('\n'.join(out))
+
+            allResults.append((t, result))
+        
+        return success, allResults
+
+        
+class Token(ParserElement):
+    """
+    Abstract C{ParserElement} subclass, for defining atomic matching patterns.
+    """
+    def __init__( self ):
+        super(Token,self).__init__( savelist=False )
+
+
+class Empty(Token):
+    """
+    An empty token, will always match.
+    """
+    def __init__( self ):
+        super(Empty,self).__init__()
+        self.name = "Empty"
+        self.mayReturnEmpty = True
+        self.mayIndexError = False
+
+
+class NoMatch(Token):
+    """
+    A token that will never match.
+    """
+    def __init__( self ):
+        super(NoMatch,self).__init__()
+        self.name = "NoMatch"
+        self.mayReturnEmpty = True
+        self.mayIndexError = False
+        self.errmsg = "Unmatchable token"
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        raise ParseException(instring, loc, self.errmsg, self)
+
+
+class Literal(Token):
+    """
+    Token to exactly match a specified string.
+    
+    Example::
+        Literal('blah').parseString('blah')  # -> ['blah']
+        Literal('blah').parseString('blahfooblah')  # -> ['blah']
+        Literal('blah').parseString('bla')  # -> Exception: Expected "blah"
+    
+    For case-insensitive matching, use L{CaselessLiteral}.
+    
+    For keyword matching (force word break before and after the matched string),
+    use L{Keyword} or L{CaselessKeyword}.
+    """
+    def __init__( self, matchString ):
+        super(Literal,self).__init__()
+        self.match = matchString
+        self.matchLen = len(matchString)
+        try:
+            self.firstMatchChar = matchString[0]
+        except IndexError:
+            warnings.warn("null string passed to Literal; use Empty() instead",
+                            SyntaxWarning, stacklevel=2)
+            self.__class__ = Empty
+        self.name = '"%s"' % _ustr(self.match)
+        self.errmsg = "Expected " + self.name
+        self.mayReturnEmpty = False
+        self.mayIndexError = False
+
+    # Performance tuning: this routine gets called a *lot*
+    # if this is a single character match string  and the first character matches,
+    # short-circuit as quickly as possible, and avoid calling startswith
+    #~ @profile
+    def parseImpl( self, instring, loc, doActions=True ):
+        if (instring[loc] == self.firstMatchChar and
+            (self.matchLen==1 or instring.startswith(self.match,loc)) ):
+            return loc+self.matchLen, self.match
+        raise ParseException(instring, loc, self.errmsg, self)
+_L = Literal
+ParserElement._literalStringClass = Literal
+
+class Keyword(Token):
+    """
+    Token to exactly match a specified string as a keyword, that is, it must be
+    immediately followed by a non-keyword character.  Compare with C{L{Literal}}:
+     - C{Literal("if")} will match the leading C{'if'} in C{'ifAndOnlyIf'}.
+     - C{Keyword("if")} will not; it will only match the leading C{'if'} in C{'if x=1'}, or C{'if(y==2)'}
+    Accepts two optional constructor arguments in addition to the keyword string:
+     - C{identChars} is a string of characters that would be valid identifier characters,
+          defaulting to all alphanumerics + "_" and "$"
+     - C{caseless} allows case-insensitive matching, default is C{False}.
+       
+    Example::
+        Keyword("start").parseString("start")  # -> ['start']
+        Keyword("start").parseString("starting")  # -> Exception
+
+    For case-insensitive matching, use L{CaselessKeyword}.
+    """
+    DEFAULT_KEYWORD_CHARS = alphanums+"_$"
+
+    def __init__( self, matchString, identChars=None, caseless=False ):
+        super(Keyword,self).__init__()
+        if identChars is None:
+            identChars = Keyword.DEFAULT_KEYWORD_CHARS
+        self.match = matchString
+        self.matchLen = len(matchString)
+        try:
+            self.firstMatchChar = matchString[0]
+        except IndexError:
+            warnings.warn("null string passed to Keyword; use Empty() instead",
+                            SyntaxWarning, stacklevel=2)
+        self.name = '"%s"' % self.match
+        self.errmsg = "Expected " + self.name
+        self.mayReturnEmpty = False
+        self.mayIndexError = False
+        self.caseless = caseless
+        if caseless:
+            self.caselessmatch = matchString.upper()
+            identChars = identChars.upper()
+        self.identChars = set(identChars)
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if self.caseless:
+            if ( (instring[ loc:loc+self.matchLen ].upper() == self.caselessmatch) and
+                 (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen].upper() not in self.identChars) and
+                 (loc == 0 or instring[loc-1].upper() not in self.identChars) ):
+                return loc+self.matchLen, self.match
+        else:
+            if (instring[loc] == self.firstMatchChar and
+                (self.matchLen==1 or instring.startswith(self.match,loc)) and
+                (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen] not in self.identChars) and
+                (loc == 0 or instring[loc-1] not in self.identChars) ):
+                return loc+self.matchLen, self.match
+        raise ParseException(instring, loc, self.errmsg, self)
+
+    def copy(self):
+        c = super(Keyword,self).copy()
+        c.identChars = Keyword.DEFAULT_KEYWORD_CHARS
+        return c
+
+    @staticmethod
+    def setDefaultKeywordChars( chars ):
+        """Overrides the default Keyword chars
+        """
+        Keyword.DEFAULT_KEYWORD_CHARS = chars
+
+class CaselessLiteral(Literal):
+    """
+    Token to match a specified string, ignoring case of letters.
+    Note: the matched results will always be in the case of the given
+    match string, NOT the case of the input text.
+
+    Example::
+        OneOrMore(CaselessLiteral("CMD")).parseString("cmd CMD Cmd10") # -> ['CMD', 'CMD', 'CMD']
+        
+    (Contrast with example for L{CaselessKeyword}.)
+    """
+    def __init__( self, matchString ):
+        super(CaselessLiteral,self).__init__( matchString.upper() )
+        # Preserve the defining literal.
+        self.returnString = matchString
+        self.name = "'%s'" % self.returnString
+        self.errmsg = "Expected " + self.name
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if instring[ loc:loc+self.matchLen ].upper() == self.match:
+            return loc+self.matchLen, self.returnString
+        raise ParseException(instring, loc, self.errmsg, self)
+
+class CaselessKeyword(Keyword):
+    """
+    Caseless version of L{Keyword}.
+
+    Example::
+        OneOrMore(CaselessKeyword("CMD")).parseString("cmd CMD Cmd10") # -> ['CMD', 'CMD']
+        
+    (Contrast with example for L{CaselessLiteral}.)
+    """
+    def __init__( self, matchString, identChars=None ):
+        super(CaselessKeyword,self).__init__( matchString, identChars, caseless=True )
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if ( (instring[ loc:loc+self.matchLen ].upper() == self.caselessmatch) and
+             (loc >= len(instring)-self.matchLen or instring[loc+self.matchLen].upper() not in self.identChars) ):
+            return loc+self.matchLen, self.match
+        raise ParseException(instring, loc, self.errmsg, self)
+
+class CloseMatch(Token):
+    """
+    A variation on L{Literal} which matches "close" matches, that is, 
+    strings with at most 'n' mismatching characters. C{CloseMatch} takes parameters:
+     - C{match_string} - string to be matched
+     - C{maxMismatches} - (C{default=1}) maximum number of mismatches allowed to count as a match
+    
+    The results from a successful parse will contain the matched text from the input string and the following named results:
+     - C{mismatches} - a list of the positions within the match_string where mismatches were found
+     - C{original} - the original match_string used to compare against the input string
+    
+    If C{mismatches} is an empty list, then the match was an exact match.
+    
+    Example::
+        patt = CloseMatch("ATCATCGAATGGA")
+        patt.parseString("ATCATCGAAXGGA") # -> (['ATCATCGAAXGGA'], {'mismatches': [[9]], 'original': ['ATCATCGAATGGA']})
+        patt.parseString("ATCAXCGAAXGGA") # -> Exception: Expected 'ATCATCGAATGGA' (with up to 1 mismatches) (at char 0), (line:1, col:1)
+
+        # exact match
+        patt.parseString("ATCATCGAATGGA") # -> (['ATCATCGAATGGA'], {'mismatches': [[]], 'original': ['ATCATCGAATGGA']})
+
+        # close match allowing up to 2 mismatches
+        patt = CloseMatch("ATCATCGAATGGA", maxMismatches=2)
+        patt.parseString("ATCAXCGAAXGGA") # -> (['ATCAXCGAAXGGA'], {'mismatches': [[4, 9]], 'original': ['ATCATCGAATGGA']})
+    """
+    def __init__(self, match_string, maxMismatches=1):
+        super(CloseMatch,self).__init__()
+        self.name = match_string
+        self.match_string = match_string
+        self.maxMismatches = maxMismatches
+        self.errmsg = "Expected %r (with up to %d mismatches)" % (self.match_string, self.maxMismatches)
+        self.mayIndexError = False
+        self.mayReturnEmpty = False
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        start = loc
+        instrlen = len(instring)
+        maxloc = start + len(self.match_string)
+
+        if maxloc <= instrlen:
+            match_string = self.match_string
+            match_stringloc = 0
+            mismatches = []
+            maxMismatches = self.maxMismatches
+
+            for match_stringloc,s_m in enumerate(zip(instring[loc:maxloc], self.match_string)):
+                src,mat = s_m
+                if src != mat:
+                    mismatches.append(match_stringloc)
+                    if len(mismatches) > maxMismatches:
+                        break
+            else:
+                loc = match_stringloc + 1
+                results = ParseResults([instring[start:loc]])
+                results['original'] = self.match_string
+                results['mismatches'] = mismatches
+                return loc, results
+
+        raise ParseException(instring, loc, self.errmsg, self)
+
+
+class Word(Token):
+    """
+    Token for matching words composed of allowed character sets.
+    Defined with string containing all allowed initial characters,
+    an optional string containing allowed body characters (if omitted,
+    defaults to the initial character set), and an optional minimum,
+    maximum, and/or exact length.  The default value for C{min} is 1 (a
+    minimum value < 1 is not valid); the default values for C{max} and C{exact}
+    are 0, meaning no maximum or exact length restriction. An optional
+    C{excludeChars} parameter can list characters that might be found in 
+    the input C{bodyChars} string; useful to define a word of all printables
+    except for one or two characters, for instance.
+    
+    L{srange} is useful for defining custom character set strings for defining 
+    C{Word} expressions, using range notation from regular expression character sets.
+    
+    A common mistake is to use C{Word} to match a specific literal string, as in 
+    C{Word("Address")}. Remember that C{Word} uses the string argument to define
+    I{sets} of matchable characters. This expression would match "Add", "AAA",
+    "dAred", or any other word made up of the characters 'A', 'd', 'r', 'e', and 's'.
+    To match an exact literal string, use L{Literal} or L{Keyword}.
+
+    pyparsing includes helper strings for building Words:
+     - L{alphas}
+     - L{nums}
+     - L{alphanums}
+     - L{hexnums}
+     - L{alphas8bit} (alphabetic characters in ASCII range 128-255 - accented, tilded, umlauted, etc.)
+     - L{punc8bit} (non-alphabetic characters in ASCII range 128-255 - currency, symbols, superscripts, diacriticals, etc.)
+     - L{printables} (any non-whitespace character)
+
+    Example::
+        # a word composed of digits
+        integer = Word(nums) # equivalent to Word("0123456789") or Word(srange("0-9"))
+        
+        # a word with a leading capital, and zero or more lowercase
+        capital_word = Word(alphas.upper(), alphas.lower())
+
+        # hostnames are alphanumeric, with leading alpha, and '-'
+        hostname = Word(alphas, alphanums+'-')
+        
+        # roman numeral (not a strict parser, accepts invalid mix of characters)
+        roman = Word("IVXLCDM")
+        
+        # any string of non-whitespace characters, except for ','
+        csv_value = Word(printables, excludeChars=",")
+    """
+    def __init__( self, initChars, bodyChars=None, min=1, max=0, exact=0, asKeyword=False, excludeChars=None ):
+        super(Word,self).__init__()
+        if excludeChars:
+            initChars = ''.join(c for c in initChars if c not in excludeChars)
+            if bodyChars:
+                bodyChars = ''.join(c for c in bodyChars if c not in excludeChars)
+        self.initCharsOrig = initChars
+        self.initChars = set(initChars)
+        if bodyChars :
+            self.bodyCharsOrig = bodyChars
+            self.bodyChars = set(bodyChars)
+        else:
+            self.bodyCharsOrig = initChars
+            self.bodyChars = set(initChars)
+
+        self.maxSpecified = max > 0
+
+        if min < 1:
+            raise ValueError("cannot specify a minimum length < 1; use Optional(Word()) if zero-length word is permitted")
+
+        self.minLen = min
+
+        if max > 0:
+            self.maxLen = max
+        else:
+            self.maxLen = _MAX_INT
+
+        if exact > 0:
+            self.maxLen = exact
+            self.minLen = exact
+
+        self.name = _ustr(self)
+        self.errmsg = "Expected " + self.name
+        self.mayIndexError = False
+        self.asKeyword = asKeyword
+
+        if ' ' not in self.initCharsOrig+self.bodyCharsOrig and (min==1 and max==0 and exact==0):
+            if self.bodyCharsOrig == self.initCharsOrig:
+                self.reString = "[%s]+" % _escapeRegexRangeChars(self.initCharsOrig)
+            elif len(self.initCharsOrig) == 1:
+                self.reString = "%s[%s]*" % \
+                                      (re.escape(self.initCharsOrig),
+                                      _escapeRegexRangeChars(self.bodyCharsOrig),)
+            else:
+                self.reString = "[%s][%s]*" % \
+                                      (_escapeRegexRangeChars(self.initCharsOrig),
+                                      _escapeRegexRangeChars(self.bodyCharsOrig),)
+            if self.asKeyword:
+                self.reString = r"\b"+self.reString+r"\b"
+            try:
+                self.re = re.compile( self.reString )
+            except Exception:
+                self.re = None
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if self.re:
+            result = self.re.match(instring,loc)
+            if not result:
+                raise ParseException(instring, loc, self.errmsg, self)
+
+            loc = result.end()
+            return loc, result.group()
+
+        if not(instring[ loc ] in self.initChars):
+            raise ParseException(instring, loc, self.errmsg, self)
+
+        start = loc
+        loc += 1
+        instrlen = len(instring)
+        bodychars = self.bodyChars
+        maxloc = start + self.maxLen
+        maxloc = min( maxloc, instrlen )
+        while loc < maxloc and instring[loc] in bodychars:
+            loc += 1
+
+        throwException = False
+        if loc - start < self.minLen:
+            throwException = True
+        if self.maxSpecified and loc < instrlen and instring[loc] in bodychars:
+            throwException = True
+        if self.asKeyword:
+            if (start>0 and instring[start-1] in bodychars) or (loc4:
+                    return s[:4]+"..."
+                else:
+                    return s
+
+            if ( self.initCharsOrig != self.bodyCharsOrig ):
+                self.strRepr = "W:(%s,%s)" % ( charsAsStr(self.initCharsOrig), charsAsStr(self.bodyCharsOrig) )
+            else:
+                self.strRepr = "W:(%s)" % charsAsStr(self.initCharsOrig)
+
+        return self.strRepr
+
+
+class Regex(Token):
+    r"""
+    Token for matching strings that match a given regular expression.
+    Defined with string specifying the regular expression in a form recognized by the inbuilt Python re module.
+    If the given regex contains named groups (defined using C{(?P...)}), these will be preserved as 
+    named parse results.
+
+    Example::
+        realnum = Regex(r"[+-]?\d+\.\d*")
+        date = Regex(r'(?P\d{4})-(?P\d\d?)-(?P\d\d?)')
+        # ref: http://stackoverflow.com/questions/267399/how-do-you-match-only-valid-roman-numerals-with-a-regular-expression
+        roman = Regex(r"M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})")
+    """
+    compiledREtype = type(re.compile("[A-Z]"))
+    def __init__( self, pattern, flags=0):
+        """The parameters C{pattern} and C{flags} are passed to the C{re.compile()} function as-is. See the Python C{re} module for an explanation of the acceptable patterns and flags."""
+        super(Regex,self).__init__()
+
+        if isinstance(pattern, basestring):
+            if not pattern:
+                warnings.warn("null string passed to Regex; use Empty() instead",
+                        SyntaxWarning, stacklevel=2)
+
+            self.pattern = pattern
+            self.flags = flags
+
+            try:
+                self.re = re.compile(self.pattern, self.flags)
+                self.reString = self.pattern
+            except sre_constants.error:
+                warnings.warn("invalid pattern (%s) passed to Regex" % pattern,
+                    SyntaxWarning, stacklevel=2)
+                raise
+
+        elif isinstance(pattern, Regex.compiledREtype):
+            self.re = pattern
+            self.pattern = \
+            self.reString = str(pattern)
+            self.flags = flags
+            
+        else:
+            raise ValueError("Regex may only be constructed with a string or a compiled RE object")
+
+        self.name = _ustr(self)
+        self.errmsg = "Expected " + self.name
+        self.mayIndexError = False
+        self.mayReturnEmpty = True
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        result = self.re.match(instring,loc)
+        if not result:
+            raise ParseException(instring, loc, self.errmsg, self)
+
+        loc = result.end()
+        d = result.groupdict()
+        ret = ParseResults(result.group())
+        if d:
+            for k in d:
+                ret[k] = d[k]
+        return loc,ret
+
+    def __str__( self ):
+        try:
+            return super(Regex,self).__str__()
+        except Exception:
+            pass
+
+        if self.strRepr is None:
+            self.strRepr = "Re:(%s)" % repr(self.pattern)
+
+        return self.strRepr
+
+
+class QuotedString(Token):
+    r"""
+    Token for matching strings that are delimited by quoting characters.
+    
+    Defined with the following parameters:
+        - quoteChar - string of one or more characters defining the quote delimiting string
+        - escChar - character to escape quotes, typically backslash (default=C{None})
+        - escQuote - special quote sequence to escape an embedded quote string (such as SQL's "" to escape an embedded ") (default=C{None})
+        - multiline - boolean indicating whether quotes can span multiple lines (default=C{False})
+        - unquoteResults - boolean indicating whether the matched text should be unquoted (default=C{True})
+        - endQuoteChar - string of one or more characters defining the end of the quote delimited string (default=C{None} => same as quoteChar)
+        - convertWhitespaceEscapes - convert escaped whitespace (C{'\t'}, C{'\n'}, etc.) to actual whitespace (default=C{True})
+
+    Example::
+        qs = QuotedString('"')
+        print(qs.searchString('lsjdf "This is the quote" sldjf'))
+        complex_qs = QuotedString('{{', endQuoteChar='}}')
+        print(complex_qs.searchString('lsjdf {{This is the "quote"}} sldjf'))
+        sql_qs = QuotedString('"', escQuote='""')
+        print(sql_qs.searchString('lsjdf "This is the quote with ""embedded"" quotes" sldjf'))
+    prints::
+        [['This is the quote']]
+        [['This is the "quote"']]
+        [['This is the quote with "embedded" quotes']]
+    """
+    def __init__( self, quoteChar, escChar=None, escQuote=None, multiline=False, unquoteResults=True, endQuoteChar=None, convertWhitespaceEscapes=True):
+        super(QuotedString,self).__init__()
+
+        # remove white space from quote chars - wont work anyway
+        quoteChar = quoteChar.strip()
+        if not quoteChar:
+            warnings.warn("quoteChar cannot be the empty string",SyntaxWarning,stacklevel=2)
+            raise SyntaxError()
+
+        if endQuoteChar is None:
+            endQuoteChar = quoteChar
+        else:
+            endQuoteChar = endQuoteChar.strip()
+            if not endQuoteChar:
+                warnings.warn("endQuoteChar cannot be the empty string",SyntaxWarning,stacklevel=2)
+                raise SyntaxError()
+
+        self.quoteChar = quoteChar
+        self.quoteCharLen = len(quoteChar)
+        self.firstQuoteChar = quoteChar[0]
+        self.endQuoteChar = endQuoteChar
+        self.endQuoteCharLen = len(endQuoteChar)
+        self.escChar = escChar
+        self.escQuote = escQuote
+        self.unquoteResults = unquoteResults
+        self.convertWhitespaceEscapes = convertWhitespaceEscapes
+
+        if multiline:
+            self.flags = re.MULTILINE | re.DOTALL
+            self.pattern = r'%s(?:[^%s%s]' % \
+                ( re.escape(self.quoteChar),
+                  _escapeRegexRangeChars(self.endQuoteChar[0]),
+                  (escChar is not None and _escapeRegexRangeChars(escChar) or '') )
+        else:
+            self.flags = 0
+            self.pattern = r'%s(?:[^%s\n\r%s]' % \
+                ( re.escape(self.quoteChar),
+                  _escapeRegexRangeChars(self.endQuoteChar[0]),
+                  (escChar is not None and _escapeRegexRangeChars(escChar) or '') )
+        if len(self.endQuoteChar) > 1:
+            self.pattern += (
+                '|(?:' + ')|(?:'.join("%s[^%s]" % (re.escape(self.endQuoteChar[:i]),
+                                               _escapeRegexRangeChars(self.endQuoteChar[i]))
+                                    for i in range(len(self.endQuoteChar)-1,0,-1)) + ')'
+                )
+        if escQuote:
+            self.pattern += (r'|(?:%s)' % re.escape(escQuote))
+        if escChar:
+            self.pattern += (r'|(?:%s.)' % re.escape(escChar))
+            self.escCharReplacePattern = re.escape(self.escChar)+"(.)"
+        self.pattern += (r')*%s' % re.escape(self.endQuoteChar))
+
+        try:
+            self.re = re.compile(self.pattern, self.flags)
+            self.reString = self.pattern
+        except sre_constants.error:
+            warnings.warn("invalid pattern (%s) passed to Regex" % self.pattern,
+                SyntaxWarning, stacklevel=2)
+            raise
+
+        self.name = _ustr(self)
+        self.errmsg = "Expected " + self.name
+        self.mayIndexError = False
+        self.mayReturnEmpty = True
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        result = instring[loc] == self.firstQuoteChar and self.re.match(instring,loc) or None
+        if not result:
+            raise ParseException(instring, loc, self.errmsg, self)
+
+        loc = result.end()
+        ret = result.group()
+
+        if self.unquoteResults:
+
+            # strip off quotes
+            ret = ret[self.quoteCharLen:-self.endQuoteCharLen]
+
+            if isinstance(ret,basestring):
+                # replace escaped whitespace
+                if '\\' in ret and self.convertWhitespaceEscapes:
+                    ws_map = {
+                        r'\t' : '\t',
+                        r'\n' : '\n',
+                        r'\f' : '\f',
+                        r'\r' : '\r',
+                    }
+                    for wslit,wschar in ws_map.items():
+                        ret = ret.replace(wslit, wschar)
+
+                # replace escaped characters
+                if self.escChar:
+                    ret = re.sub(self.escCharReplacePattern, r"\g<1>", ret)
+
+                # replace escaped quotes
+                if self.escQuote:
+                    ret = ret.replace(self.escQuote, self.endQuoteChar)
+
+        return loc, ret
+
+    def __str__( self ):
+        try:
+            return super(QuotedString,self).__str__()
+        except Exception:
+            pass
+
+        if self.strRepr is None:
+            self.strRepr = "quoted string, starting with %s ending with %s" % (self.quoteChar, self.endQuoteChar)
+
+        return self.strRepr
+
+
+class CharsNotIn(Token):
+    """
+    Token for matching words composed of characters I{not} in a given set (will
+    include whitespace in matched characters if not listed in the provided exclusion set - see example).
+    Defined with string containing all disallowed characters, and an optional
+    minimum, maximum, and/or exact length.  The default value for C{min} is 1 (a
+    minimum value < 1 is not valid); the default values for C{max} and C{exact}
+    are 0, meaning no maximum or exact length restriction.
+
+    Example::
+        # define a comma-separated-value as anything that is not a ','
+        csv_value = CharsNotIn(',')
+        print(delimitedList(csv_value).parseString("dkls,lsdkjf,s12 34,@!#,213"))
+    prints::
+        ['dkls', 'lsdkjf', 's12 34', '@!#', '213']
+    """
+    def __init__( self, notChars, min=1, max=0, exact=0 ):
+        super(CharsNotIn,self).__init__()
+        self.skipWhitespace = False
+        self.notChars = notChars
+
+        if min < 1:
+            raise ValueError("cannot specify a minimum length < 1; use Optional(CharsNotIn()) if zero-length char group is permitted")
+
+        self.minLen = min
+
+        if max > 0:
+            self.maxLen = max
+        else:
+            self.maxLen = _MAX_INT
+
+        if exact > 0:
+            self.maxLen = exact
+            self.minLen = exact
+
+        self.name = _ustr(self)
+        self.errmsg = "Expected " + self.name
+        self.mayReturnEmpty = ( self.minLen == 0 )
+        self.mayIndexError = False
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if instring[loc] in self.notChars:
+            raise ParseException(instring, loc, self.errmsg, self)
+
+        start = loc
+        loc += 1
+        notchars = self.notChars
+        maxlen = min( start+self.maxLen, len(instring) )
+        while loc < maxlen and \
+              (instring[loc] not in notchars):
+            loc += 1
+
+        if loc - start < self.minLen:
+            raise ParseException(instring, loc, self.errmsg, self)
+
+        return loc, instring[start:loc]
+
+    def __str__( self ):
+        try:
+            return super(CharsNotIn, self).__str__()
+        except Exception:
+            pass
+
+        if self.strRepr is None:
+            if len(self.notChars) > 4:
+                self.strRepr = "!W:(%s...)" % self.notChars[:4]
+            else:
+                self.strRepr = "!W:(%s)" % self.notChars
+
+        return self.strRepr
+
+class White(Token):
+    """
+    Special matching class for matching whitespace.  Normally, whitespace is ignored
+    by pyparsing grammars.  This class is included when some whitespace structures
+    are significant.  Define with a string containing the whitespace characters to be
+    matched; default is C{" \\t\\r\\n"}.  Also takes optional C{min}, C{max}, and C{exact} arguments,
+    as defined for the C{L{Word}} class.
+    """
+    whiteStrs = {
+        " " : "",
+        "\t": "",
+        "\n": "",
+        "\r": "",
+        "\f": "",
+        }
+    def __init__(self, ws=" \t\r\n", min=1, max=0, exact=0):
+        super(White,self).__init__()
+        self.matchWhite = ws
+        self.setWhitespaceChars( "".join(c for c in self.whiteChars if c not in self.matchWhite) )
+        #~ self.leaveWhitespace()
+        self.name = ("".join(White.whiteStrs[c] for c in self.matchWhite))
+        self.mayReturnEmpty = True
+        self.errmsg = "Expected " + self.name
+
+        self.minLen = min
+
+        if max > 0:
+            self.maxLen = max
+        else:
+            self.maxLen = _MAX_INT
+
+        if exact > 0:
+            self.maxLen = exact
+            self.minLen = exact
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if not(instring[ loc ] in self.matchWhite):
+            raise ParseException(instring, loc, self.errmsg, self)
+        start = loc
+        loc += 1
+        maxloc = start + self.maxLen
+        maxloc = min( maxloc, len(instring) )
+        while loc < maxloc and instring[loc] in self.matchWhite:
+            loc += 1
+
+        if loc - start < self.minLen:
+            raise ParseException(instring, loc, self.errmsg, self)
+
+        return loc, instring[start:loc]
+
+
+class _PositionToken(Token):
+    def __init__( self ):
+        super(_PositionToken,self).__init__()
+        self.name=self.__class__.__name__
+        self.mayReturnEmpty = True
+        self.mayIndexError = False
+
+class GoToColumn(_PositionToken):
+    """
+    Token to advance to a specific column of input text; useful for tabular report scraping.
+    """
+    def __init__( self, colno ):
+        super(GoToColumn,self).__init__()
+        self.col = colno
+
+    def preParse( self, instring, loc ):
+        if col(loc,instring) != self.col:
+            instrlen = len(instring)
+            if self.ignoreExprs:
+                loc = self._skipIgnorables( instring, loc )
+            while loc < instrlen and instring[loc].isspace() and col( loc, instring ) != self.col :
+                loc += 1
+        return loc
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        thiscol = col( loc, instring )
+        if thiscol > self.col:
+            raise ParseException( instring, loc, "Text not in expected column", self )
+        newloc = loc + self.col - thiscol
+        ret = instring[ loc: newloc ]
+        return newloc, ret
+
+
+class LineStart(_PositionToken):
+    """
+    Matches if current position is at the beginning of a line within the parse string
+    
+    Example::
+    
+        test = '''\
+        AAA this line
+        AAA and this line
+          AAA but not this one
+        B AAA and definitely not this one
+        '''
+
+        for t in (LineStart() + 'AAA' + restOfLine).searchString(test):
+            print(t)
+    
+    Prints::
+        ['AAA', ' this line']
+        ['AAA', ' and this line']    
+
+    """
+    def __init__( self ):
+        super(LineStart,self).__init__()
+        self.errmsg = "Expected start of line"
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if col(loc, instring) == 1:
+            return loc, []
+        raise ParseException(instring, loc, self.errmsg, self)
+
+class LineEnd(_PositionToken):
+    """
+    Matches if current position is at the end of a line within the parse string
+    """
+    def __init__( self ):
+        super(LineEnd,self).__init__()
+        self.setWhitespaceChars( ParserElement.DEFAULT_WHITE_CHARS.replace("\n","") )
+        self.errmsg = "Expected end of line"
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if loc len(instring):
+            return loc, []
+        else:
+            raise ParseException(instring, loc, self.errmsg, self)
+
+class WordStart(_PositionToken):
+    """
+    Matches if the current position is at the beginning of a Word, and
+    is not preceded by any character in a given set of C{wordChars}
+    (default=C{printables}). To emulate the C{\b} behavior of regular expressions,
+    use C{WordStart(alphanums)}. C{WordStart} will also match at the beginning of
+    the string being parsed, or at the beginning of a line.
+    """
+    def __init__(self, wordChars = printables):
+        super(WordStart,self).__init__()
+        self.wordChars = set(wordChars)
+        self.errmsg = "Not at the start of a word"
+
+    def parseImpl(self, instring, loc, doActions=True ):
+        if loc != 0:
+            if (instring[loc-1] in self.wordChars or
+                instring[loc] not in self.wordChars):
+                raise ParseException(instring, loc, self.errmsg, self)
+        return loc, []
+
+class WordEnd(_PositionToken):
+    """
+    Matches if the current position is at the end of a Word, and
+    is not followed by any character in a given set of C{wordChars}
+    (default=C{printables}). To emulate the C{\b} behavior of regular expressions,
+    use C{WordEnd(alphanums)}. C{WordEnd} will also match at the end of
+    the string being parsed, or at the end of a line.
+    """
+    def __init__(self, wordChars = printables):
+        super(WordEnd,self).__init__()
+        self.wordChars = set(wordChars)
+        self.skipWhitespace = False
+        self.errmsg = "Not at the end of a word"
+
+    def parseImpl(self, instring, loc, doActions=True ):
+        instrlen = len(instring)
+        if instrlen>0 and loc maxExcLoc:
+                    maxException = err
+                    maxExcLoc = err.loc
+            except IndexError:
+                if len(instring) > maxExcLoc:
+                    maxException = ParseException(instring,len(instring),e.errmsg,self)
+                    maxExcLoc = len(instring)
+            else:
+                # save match among all matches, to retry longest to shortest
+                matches.append((loc2, e))
+
+        if matches:
+            matches.sort(key=lambda x: -x[0])
+            for _,e in matches:
+                try:
+                    return e._parse( instring, loc, doActions )
+                except ParseException as err:
+                    err.__traceback__ = None
+                    if err.loc > maxExcLoc:
+                        maxException = err
+                        maxExcLoc = err.loc
+
+        if maxException is not None:
+            maxException.msg = self.errmsg
+            raise maxException
+        else:
+            raise ParseException(instring, loc, "no defined alternatives to match", self)
+
+
+    def __ixor__(self, other ):
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        return self.append( other ) #Or( [ self, other ] )
+
+    def __str__( self ):
+        if hasattr(self,"name"):
+            return self.name
+
+        if self.strRepr is None:
+            self.strRepr = "{" + " ^ ".join(_ustr(e) for e in self.exprs) + "}"
+
+        return self.strRepr
+
+    def checkRecursion( self, parseElementList ):
+        subRecCheckList = parseElementList[:] + [ self ]
+        for e in self.exprs:
+            e.checkRecursion( subRecCheckList )
+
+
+class MatchFirst(ParseExpression):
+    """
+    Requires that at least one C{ParseExpression} is found.
+    If two expressions match, the first one listed is the one that will match.
+    May be constructed using the C{'|'} operator.
+
+    Example::
+        # construct MatchFirst using '|' operator
+        
+        # watch the order of expressions to match
+        number = Word(nums) | Combine(Word(nums) + '.' + Word(nums))
+        print(number.searchString("123 3.1416 789")) #  Fail! -> [['123'], ['3'], ['1416'], ['789']]
+
+        # put more selective expression first
+        number = Combine(Word(nums) + '.' + Word(nums)) | Word(nums)
+        print(number.searchString("123 3.1416 789")) #  Better -> [['123'], ['3.1416'], ['789']]
+    """
+    def __init__( self, exprs, savelist = False ):
+        super(MatchFirst,self).__init__(exprs, savelist)
+        if self.exprs:
+            self.mayReturnEmpty = any(e.mayReturnEmpty for e in self.exprs)
+        else:
+            self.mayReturnEmpty = True
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        maxExcLoc = -1
+        maxException = None
+        for e in self.exprs:
+            try:
+                ret = e._parse( instring, loc, doActions )
+                return ret
+            except ParseException as err:
+                if err.loc > maxExcLoc:
+                    maxException = err
+                    maxExcLoc = err.loc
+            except IndexError:
+                if len(instring) > maxExcLoc:
+                    maxException = ParseException(instring,len(instring),e.errmsg,self)
+                    maxExcLoc = len(instring)
+
+        # only got here if no expression matched, raise exception for match that made it the furthest
+        else:
+            if maxException is not None:
+                maxException.msg = self.errmsg
+                raise maxException
+            else:
+                raise ParseException(instring, loc, "no defined alternatives to match", self)
+
+    def __ior__(self, other ):
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass( other )
+        return self.append( other ) #MatchFirst( [ self, other ] )
+
+    def __str__( self ):
+        if hasattr(self,"name"):
+            return self.name
+
+        if self.strRepr is None:
+            self.strRepr = "{" + " | ".join(_ustr(e) for e in self.exprs) + "}"
+
+        return self.strRepr
+
+    def checkRecursion( self, parseElementList ):
+        subRecCheckList = parseElementList[:] + [ self ]
+        for e in self.exprs:
+            e.checkRecursion( subRecCheckList )
+
+
+class Each(ParseExpression):
+    """
+    Requires all given C{ParseExpression}s to be found, but in any order.
+    Expressions may be separated by whitespace.
+    May be constructed using the C{'&'} operator.
+
+    Example::
+        color = oneOf("RED ORANGE YELLOW GREEN BLUE PURPLE BLACK WHITE BROWN")
+        shape_type = oneOf("SQUARE CIRCLE TRIANGLE STAR HEXAGON OCTAGON")
+        integer = Word(nums)
+        shape_attr = "shape:" + shape_type("shape")
+        posn_attr = "posn:" + Group(integer("x") + ',' + integer("y"))("posn")
+        color_attr = "color:" + color("color")
+        size_attr = "size:" + integer("size")
+
+        # use Each (using operator '&') to accept attributes in any order 
+        # (shape and posn are required, color and size are optional)
+        shape_spec = shape_attr & posn_attr & Optional(color_attr) & Optional(size_attr)
+
+        shape_spec.runTests('''
+            shape: SQUARE color: BLACK posn: 100, 120
+            shape: CIRCLE size: 50 color: BLUE posn: 50,80
+            color:GREEN size:20 shape:TRIANGLE posn:20,40
+            '''
+            )
+    prints::
+        shape: SQUARE color: BLACK posn: 100, 120
+        ['shape:', 'SQUARE', 'color:', 'BLACK', 'posn:', ['100', ',', '120']]
+        - color: BLACK
+        - posn: ['100', ',', '120']
+          - x: 100
+          - y: 120
+        - shape: SQUARE
+
+
+        shape: CIRCLE size: 50 color: BLUE posn: 50,80
+        ['shape:', 'CIRCLE', 'size:', '50', 'color:', 'BLUE', 'posn:', ['50', ',', '80']]
+        - color: BLUE
+        - posn: ['50', ',', '80']
+          - x: 50
+          - y: 80
+        - shape: CIRCLE
+        - size: 50
+
+
+        color: GREEN size: 20 shape: TRIANGLE posn: 20,40
+        ['color:', 'GREEN', 'size:', '20', 'shape:', 'TRIANGLE', 'posn:', ['20', ',', '40']]
+        - color: GREEN
+        - posn: ['20', ',', '40']
+          - x: 20
+          - y: 40
+        - shape: TRIANGLE
+        - size: 20
+    """
+    def __init__( self, exprs, savelist = True ):
+        super(Each,self).__init__(exprs, savelist)
+        self.mayReturnEmpty = all(e.mayReturnEmpty for e in self.exprs)
+        self.skipWhitespace = True
+        self.initExprGroups = True
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if self.initExprGroups:
+            self.opt1map = dict((id(e.expr),e) for e in self.exprs if isinstance(e,Optional))
+            opt1 = [ e.expr for e in self.exprs if isinstance(e,Optional) ]
+            opt2 = [ e for e in self.exprs if e.mayReturnEmpty and not isinstance(e,Optional)]
+            self.optionals = opt1 + opt2
+            self.multioptionals = [ e.expr for e in self.exprs if isinstance(e,ZeroOrMore) ]
+            self.multirequired = [ e.expr for e in self.exprs if isinstance(e,OneOrMore) ]
+            self.required = [ e for e in self.exprs if not isinstance(e,(Optional,ZeroOrMore,OneOrMore)) ]
+            self.required += self.multirequired
+            self.initExprGroups = False
+        tmpLoc = loc
+        tmpReqd = self.required[:]
+        tmpOpt  = self.optionals[:]
+        matchOrder = []
+
+        keepMatching = True
+        while keepMatching:
+            tmpExprs = tmpReqd + tmpOpt + self.multioptionals + self.multirequired
+            failed = []
+            for e in tmpExprs:
+                try:
+                    tmpLoc = e.tryParse( instring, tmpLoc )
+                except ParseException:
+                    failed.append(e)
+                else:
+                    matchOrder.append(self.opt1map.get(id(e),e))
+                    if e in tmpReqd:
+                        tmpReqd.remove(e)
+                    elif e in tmpOpt:
+                        tmpOpt.remove(e)
+            if len(failed) == len(tmpExprs):
+                keepMatching = False
+
+        if tmpReqd:
+            missing = ", ".join(_ustr(e) for e in tmpReqd)
+            raise ParseException(instring,loc,"Missing one or more required elements (%s)" % missing )
+
+        # add any unmatched Optionals, in case they have default values defined
+        matchOrder += [e for e in self.exprs if isinstance(e,Optional) and e.expr in tmpOpt]
+
+        resultlist = []
+        for e in matchOrder:
+            loc,results = e._parse(instring,loc,doActions)
+            resultlist.append(results)
+
+        finalResults = sum(resultlist, ParseResults([]))
+        return loc, finalResults
+
+    def __str__( self ):
+        if hasattr(self,"name"):
+            return self.name
+
+        if self.strRepr is None:
+            self.strRepr = "{" + " & ".join(_ustr(e) for e in self.exprs) + "}"
+
+        return self.strRepr
+
+    def checkRecursion( self, parseElementList ):
+        subRecCheckList = parseElementList[:] + [ self ]
+        for e in self.exprs:
+            e.checkRecursion( subRecCheckList )
+
+
+class ParseElementEnhance(ParserElement):
+    """
+    Abstract subclass of C{ParserElement}, for combining and post-processing parsed tokens.
+    """
+    def __init__( self, expr, savelist=False ):
+        super(ParseElementEnhance,self).__init__(savelist)
+        if isinstance( expr, basestring ):
+            if issubclass(ParserElement._literalStringClass, Token):
+                expr = ParserElement._literalStringClass(expr)
+            else:
+                expr = ParserElement._literalStringClass(Literal(expr))
+        self.expr = expr
+        self.strRepr = None
+        if expr is not None:
+            self.mayIndexError = expr.mayIndexError
+            self.mayReturnEmpty = expr.mayReturnEmpty
+            self.setWhitespaceChars( expr.whiteChars )
+            self.skipWhitespace = expr.skipWhitespace
+            self.saveAsList = expr.saveAsList
+            self.callPreparse = expr.callPreparse
+            self.ignoreExprs.extend(expr.ignoreExprs)
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if self.expr is not None:
+            return self.expr._parse( instring, loc, doActions, callPreParse=False )
+        else:
+            raise ParseException("",loc,self.errmsg,self)
+
+    def leaveWhitespace( self ):
+        self.skipWhitespace = False
+        self.expr = self.expr.copy()
+        if self.expr is not None:
+            self.expr.leaveWhitespace()
+        return self
+
+    def ignore( self, other ):
+        if isinstance( other, Suppress ):
+            if other not in self.ignoreExprs:
+                super( ParseElementEnhance, self).ignore( other )
+                if self.expr is not None:
+                    self.expr.ignore( self.ignoreExprs[-1] )
+        else:
+            super( ParseElementEnhance, self).ignore( other )
+            if self.expr is not None:
+                self.expr.ignore( self.ignoreExprs[-1] )
+        return self
+
+    def streamline( self ):
+        super(ParseElementEnhance,self).streamline()
+        if self.expr is not None:
+            self.expr.streamline()
+        return self
+
+    def checkRecursion( self, parseElementList ):
+        if self in parseElementList:
+            raise RecursiveGrammarException( parseElementList+[self] )
+        subRecCheckList = parseElementList[:] + [ self ]
+        if self.expr is not None:
+            self.expr.checkRecursion( subRecCheckList )
+
+    def validate( self, validateTrace=[] ):
+        tmp = validateTrace[:]+[self]
+        if self.expr is not None:
+            self.expr.validate(tmp)
+        self.checkRecursion( [] )
+
+    def __str__( self ):
+        try:
+            return super(ParseElementEnhance,self).__str__()
+        except Exception:
+            pass
+
+        if self.strRepr is None and self.expr is not None:
+            self.strRepr = "%s:(%s)" % ( self.__class__.__name__, _ustr(self.expr) )
+        return self.strRepr
+
+
+class FollowedBy(ParseElementEnhance):
+    """
+    Lookahead matching of the given parse expression.  C{FollowedBy}
+    does I{not} advance the parsing position within the input string, it only
+    verifies that the specified parse expression matches at the current
+    position.  C{FollowedBy} always returns a null token list.
+
+    Example::
+        # use FollowedBy to match a label only if it is followed by a ':'
+        data_word = Word(alphas)
+        label = data_word + FollowedBy(':')
+        attr_expr = Group(label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join))
+        
+        OneOrMore(attr_expr).parseString("shape: SQUARE color: BLACK posn: upper left").pprint()
+    prints::
+        [['shape', 'SQUARE'], ['color', 'BLACK'], ['posn', 'upper left']]
+    """
+    def __init__( self, expr ):
+        super(FollowedBy,self).__init__(expr)
+        self.mayReturnEmpty = True
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        self.expr.tryParse( instring, loc )
+        return loc, []
+
+
+class NotAny(ParseElementEnhance):
+    """
+    Lookahead to disallow matching with the given parse expression.  C{NotAny}
+    does I{not} advance the parsing position within the input string, it only
+    verifies that the specified parse expression does I{not} match at the current
+    position.  Also, C{NotAny} does I{not} skip over leading whitespace. C{NotAny}
+    always returns a null token list.  May be constructed using the '~' operator.
+
+    Example::
+        
+    """
+    def __init__( self, expr ):
+        super(NotAny,self).__init__(expr)
+        #~ self.leaveWhitespace()
+        self.skipWhitespace = False  # do NOT use self.leaveWhitespace(), don't want to propagate to exprs
+        self.mayReturnEmpty = True
+        self.errmsg = "Found unwanted token, "+_ustr(self.expr)
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        if self.expr.canParseNext(instring, loc):
+            raise ParseException(instring, loc, self.errmsg, self)
+        return loc, []
+
+    def __str__( self ):
+        if hasattr(self,"name"):
+            return self.name
+
+        if self.strRepr is None:
+            self.strRepr = "~{" + _ustr(self.expr) + "}"
+
+        return self.strRepr
+
+class _MultipleMatch(ParseElementEnhance):
+    def __init__( self, expr, stopOn=None):
+        super(_MultipleMatch, self).__init__(expr)
+        self.saveAsList = True
+        ender = stopOn
+        if isinstance(ender, basestring):
+            ender = ParserElement._literalStringClass(ender)
+        self.not_ender = ~ender if ender is not None else None
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        self_expr_parse = self.expr._parse
+        self_skip_ignorables = self._skipIgnorables
+        check_ender = self.not_ender is not None
+        if check_ender:
+            try_not_ender = self.not_ender.tryParse
+        
+        # must be at least one (but first see if we are the stopOn sentinel;
+        # if so, fail)
+        if check_ender:
+            try_not_ender(instring, loc)
+        loc, tokens = self_expr_parse( instring, loc, doActions, callPreParse=False )
+        try:
+            hasIgnoreExprs = (not not self.ignoreExprs)
+            while 1:
+                if check_ender:
+                    try_not_ender(instring, loc)
+                if hasIgnoreExprs:
+                    preloc = self_skip_ignorables( instring, loc )
+                else:
+                    preloc = loc
+                loc, tmptokens = self_expr_parse( instring, preloc, doActions )
+                if tmptokens or tmptokens.haskeys():
+                    tokens += tmptokens
+        except (ParseException,IndexError):
+            pass
+
+        return loc, tokens
+        
+class OneOrMore(_MultipleMatch):
+    """
+    Repetition of one or more of the given expression.
+    
+    Parameters:
+     - expr - expression that must match one or more times
+     - stopOn - (default=C{None}) - expression for a terminating sentinel
+          (only required if the sentinel would ordinarily match the repetition 
+          expression)          
+
+    Example::
+        data_word = Word(alphas)
+        label = data_word + FollowedBy(':')
+        attr_expr = Group(label + Suppress(':') + OneOrMore(data_word).setParseAction(' '.join))
+
+        text = "shape: SQUARE posn: upper left color: BLACK"
+        OneOrMore(attr_expr).parseString(text).pprint()  # Fail! read 'color' as data instead of next label -> [['shape', 'SQUARE color']]
+
+        # use stopOn attribute for OneOrMore to avoid reading label string as part of the data
+        attr_expr = Group(label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join))
+        OneOrMore(attr_expr).parseString(text).pprint() # Better -> [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'BLACK']]
+        
+        # could also be written as
+        (attr_expr * (1,)).parseString(text).pprint()
+    """
+
+    def __str__( self ):
+        if hasattr(self,"name"):
+            return self.name
+
+        if self.strRepr is None:
+            self.strRepr = "{" + _ustr(self.expr) + "}..."
+
+        return self.strRepr
+
+class ZeroOrMore(_MultipleMatch):
+    """
+    Optional repetition of zero or more of the given expression.
+    
+    Parameters:
+     - expr - expression that must match zero or more times
+     - stopOn - (default=C{None}) - expression for a terminating sentinel
+          (only required if the sentinel would ordinarily match the repetition 
+          expression)          
+
+    Example: similar to L{OneOrMore}
+    """
+    def __init__( self, expr, stopOn=None):
+        super(ZeroOrMore,self).__init__(expr, stopOn=stopOn)
+        self.mayReturnEmpty = True
+        
+    def parseImpl( self, instring, loc, doActions=True ):
+        try:
+            return super(ZeroOrMore, self).parseImpl(instring, loc, doActions)
+        except (ParseException,IndexError):
+            return loc, []
+
+    def __str__( self ):
+        if hasattr(self,"name"):
+            return self.name
+
+        if self.strRepr is None:
+            self.strRepr = "[" + _ustr(self.expr) + "]..."
+
+        return self.strRepr
+
+class _NullToken(object):
+    def __bool__(self):
+        return False
+    __nonzero__ = __bool__
+    def __str__(self):
+        return ""
+
+_optionalNotMatched = _NullToken()
+class Optional(ParseElementEnhance):
+    """
+    Optional matching of the given expression.
+
+    Parameters:
+     - expr - expression that must match zero or more times
+     - default (optional) - value to be returned if the optional expression is not found.
+
+    Example::
+        # US postal code can be a 5-digit zip, plus optional 4-digit qualifier
+        zip = Combine(Word(nums, exact=5) + Optional('-' + Word(nums, exact=4)))
+        zip.runTests('''
+            # traditional ZIP code
+            12345
+            
+            # ZIP+4 form
+            12101-0001
+            
+            # invalid ZIP
+            98765-
+            ''')
+    prints::
+        # traditional ZIP code
+        12345
+        ['12345']
+
+        # ZIP+4 form
+        12101-0001
+        ['12101-0001']
+
+        # invalid ZIP
+        98765-
+             ^
+        FAIL: Expected end of text (at char 5), (line:1, col:6)
+    """
+    def __init__( self, expr, default=_optionalNotMatched ):
+        super(Optional,self).__init__( expr, savelist=False )
+        self.saveAsList = self.expr.saveAsList
+        self.defaultValue = default
+        self.mayReturnEmpty = True
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        try:
+            loc, tokens = self.expr._parse( instring, loc, doActions, callPreParse=False )
+        except (ParseException,IndexError):
+            if self.defaultValue is not _optionalNotMatched:
+                if self.expr.resultsName:
+                    tokens = ParseResults([ self.defaultValue ])
+                    tokens[self.expr.resultsName] = self.defaultValue
+                else:
+                    tokens = [ self.defaultValue ]
+            else:
+                tokens = []
+        return loc, tokens
+
+    def __str__( self ):
+        if hasattr(self,"name"):
+            return self.name
+
+        if self.strRepr is None:
+            self.strRepr = "[" + _ustr(self.expr) + "]"
+
+        return self.strRepr
+
+class SkipTo(ParseElementEnhance):
+    """
+    Token for skipping over all undefined text until the matched expression is found.
+
+    Parameters:
+     - expr - target expression marking the end of the data to be skipped
+     - include - (default=C{False}) if True, the target expression is also parsed 
+          (the skipped text and target expression are returned as a 2-element list).
+     - ignore - (default=C{None}) used to define grammars (typically quoted strings and 
+          comments) that might contain false matches to the target expression
+     - failOn - (default=C{None}) define expressions that are not allowed to be 
+          included in the skipped test; if found before the target expression is found, 
+          the SkipTo is not a match
+
+    Example::
+        report = '''
+            Outstanding Issues Report - 1 Jan 2000
+
+               # | Severity | Description                               |  Days Open
+            -----+----------+-------------------------------------------+-----------
+             101 | Critical | Intermittent system crash                 |          6
+              94 | Cosmetic | Spelling error on Login ('log|n')         |         14
+              79 | Minor    | System slow when running too many reports |         47
+            '''
+        integer = Word(nums)
+        SEP = Suppress('|')
+        # use SkipTo to simply match everything up until the next SEP
+        # - ignore quoted strings, so that a '|' character inside a quoted string does not match
+        # - parse action will call token.strip() for each matched token, i.e., the description body
+        string_data = SkipTo(SEP, ignore=quotedString)
+        string_data.setParseAction(tokenMap(str.strip))
+        ticket_expr = (integer("issue_num") + SEP 
+                      + string_data("sev") + SEP 
+                      + string_data("desc") + SEP 
+                      + integer("days_open"))
+        
+        for tkt in ticket_expr.searchString(report):
+            print tkt.dump()
+    prints::
+        ['101', 'Critical', 'Intermittent system crash', '6']
+        - days_open: 6
+        - desc: Intermittent system crash
+        - issue_num: 101
+        - sev: Critical
+        ['94', 'Cosmetic', "Spelling error on Login ('log|n')", '14']
+        - days_open: 14
+        - desc: Spelling error on Login ('log|n')
+        - issue_num: 94
+        - sev: Cosmetic
+        ['79', 'Minor', 'System slow when running too many reports', '47']
+        - days_open: 47
+        - desc: System slow when running too many reports
+        - issue_num: 79
+        - sev: Minor
+    """
+    def __init__( self, other, include=False, ignore=None, failOn=None ):
+        super( SkipTo, self ).__init__( other )
+        self.ignoreExpr = ignore
+        self.mayReturnEmpty = True
+        self.mayIndexError = False
+        self.includeMatch = include
+        self.asList = False
+        if isinstance(failOn, basestring):
+            self.failOn = ParserElement._literalStringClass(failOn)
+        else:
+            self.failOn = failOn
+        self.errmsg = "No match found for "+_ustr(self.expr)
+
+    def parseImpl( self, instring, loc, doActions=True ):
+        startloc = loc
+        instrlen = len(instring)
+        expr = self.expr
+        expr_parse = self.expr._parse
+        self_failOn_canParseNext = self.failOn.canParseNext if self.failOn is not None else None
+        self_ignoreExpr_tryParse = self.ignoreExpr.tryParse if self.ignoreExpr is not None else None
+        
+        tmploc = loc
+        while tmploc <= instrlen:
+            if self_failOn_canParseNext is not None:
+                # break if failOn expression matches
+                if self_failOn_canParseNext(instring, tmploc):
+                    break
+                    
+            if self_ignoreExpr_tryParse is not None:
+                # advance past ignore expressions
+                while 1:
+                    try:
+                        tmploc = self_ignoreExpr_tryParse(instring, tmploc)
+                    except ParseBaseException:
+                        break
+            
+            try:
+                expr_parse(instring, tmploc, doActions=False, callPreParse=False)
+            except (ParseException, IndexError):
+                # no match, advance loc in string
+                tmploc += 1
+            else:
+                # matched skipto expr, done
+                break
+
+        else:
+            # ran off the end of the input string without matching skipto expr, fail
+            raise ParseException(instring, loc, self.errmsg, self)
+
+        # build up return values
+        loc = tmploc
+        skiptext = instring[startloc:loc]
+        skipresult = ParseResults(skiptext)
+        
+        if self.includeMatch:
+            loc, mat = expr_parse(instring,loc,doActions,callPreParse=False)
+            skipresult += mat
+
+        return loc, skipresult
+
+class Forward(ParseElementEnhance):
+    """
+    Forward declaration of an expression to be defined later -
+    used for recursive grammars, such as algebraic infix notation.
+    When the expression is known, it is assigned to the C{Forward} variable using the '<<' operator.
+
+    Note: take care when assigning to C{Forward} not to overlook precedence of operators.
+    Specifically, '|' has a lower precedence than '<<', so that::
+        fwdExpr << a | b | c
+    will actually be evaluated as::
+        (fwdExpr << a) | b | c
+    thereby leaving b and c out as parseable alternatives.  It is recommended that you
+    explicitly group the values inserted into the C{Forward}::
+        fwdExpr << (a | b | c)
+    Converting to use the '<<=' operator instead will avoid this problem.
+
+    See L{ParseResults.pprint} for an example of a recursive parser created using
+    C{Forward}.
+    """
+    def __init__( self, other=None ):
+        super(Forward,self).__init__( other, savelist=False )
+
+    def __lshift__( self, other ):
+        if isinstance( other, basestring ):
+            other = ParserElement._literalStringClass(other)
+        self.expr = other
+        self.strRepr = None
+        self.mayIndexError = self.expr.mayIndexError
+        self.mayReturnEmpty = self.expr.mayReturnEmpty
+        self.setWhitespaceChars( self.expr.whiteChars )
+        self.skipWhitespace = self.expr.skipWhitespace
+        self.saveAsList = self.expr.saveAsList
+        self.ignoreExprs.extend(self.expr.ignoreExprs)
+        return self
+        
+    def __ilshift__(self, other):
+        return self << other
+    
+    def leaveWhitespace( self ):
+        self.skipWhitespace = False
+        return self
+
+    def streamline( self ):
+        if not self.streamlined:
+            self.streamlined = True
+            if self.expr is not None:
+                self.expr.streamline()
+        return self
+
+    def validate( self, validateTrace=[] ):
+        if self not in validateTrace:
+            tmp = validateTrace[:]+[self]
+            if self.expr is not None:
+                self.expr.validate(tmp)
+        self.checkRecursion([])
+
+    def __str__( self ):
+        if hasattr(self,"name"):
+            return self.name
+        return self.__class__.__name__ + ": ..."
+
+        # stubbed out for now - creates awful memory and perf issues
+        self._revertClass = self.__class__
+        self.__class__ = _ForwardNoRecurse
+        try:
+            if self.expr is not None:
+                retString = _ustr(self.expr)
+            else:
+                retString = "None"
+        finally:
+            self.__class__ = self._revertClass
+        return self.__class__.__name__ + ": " + retString
+
+    def copy(self):
+        if self.expr is not None:
+            return super(Forward,self).copy()
+        else:
+            ret = Forward()
+            ret <<= self
+            return ret
+
+class _ForwardNoRecurse(Forward):
+    def __str__( self ):
+        return "..."
+
+class TokenConverter(ParseElementEnhance):
+    """
+    Abstract subclass of C{ParseExpression}, for converting parsed results.
+    """
+    def __init__( self, expr, savelist=False ):
+        super(TokenConverter,self).__init__( expr )#, savelist )
+        self.saveAsList = False
+
+class Combine(TokenConverter):
+    """
+    Converter to concatenate all matching tokens to a single string.
+    By default, the matching patterns must also be contiguous in the input string;
+    this can be disabled by specifying C{'adjacent=False'} in the constructor.
+
+    Example::
+        real = Word(nums) + '.' + Word(nums)
+        print(real.parseString('3.1416')) # -> ['3', '.', '1416']
+        # will also erroneously match the following
+        print(real.parseString('3. 1416')) # -> ['3', '.', '1416']
+
+        real = Combine(Word(nums) + '.' + Word(nums))
+        print(real.parseString('3.1416')) # -> ['3.1416']
+        # no match when there are internal spaces
+        print(real.parseString('3. 1416')) # -> Exception: Expected W:(0123...)
+    """
+    def __init__( self, expr, joinString="", adjacent=True ):
+        super(Combine,self).__init__( expr )
+        # suppress whitespace-stripping in contained parse expressions, but re-enable it on the Combine itself
+        if adjacent:
+            self.leaveWhitespace()
+        self.adjacent = adjacent
+        self.skipWhitespace = True
+        self.joinString = joinString
+        self.callPreparse = True
+
+    def ignore( self, other ):
+        if self.adjacent:
+            ParserElement.ignore(self, other)
+        else:
+            super( Combine, self).ignore( other )
+        return self
+
+    def postParse( self, instring, loc, tokenlist ):
+        retToks = tokenlist.copy()
+        del retToks[:]
+        retToks += ParseResults([ "".join(tokenlist._asStringList(self.joinString)) ], modal=self.modalResults)
+
+        if self.resultsName and retToks.haskeys():
+            return [ retToks ]
+        else:
+            return retToks
+
+class Group(TokenConverter):
+    """
+    Converter to return the matched tokens as a list - useful for returning tokens of C{L{ZeroOrMore}} and C{L{OneOrMore}} expressions.
+
+    Example::
+        ident = Word(alphas)
+        num = Word(nums)
+        term = ident | num
+        func = ident + Optional(delimitedList(term))
+        print(func.parseString("fn a,b,100"))  # -> ['fn', 'a', 'b', '100']
+
+        func = ident + Group(Optional(delimitedList(term)))
+        print(func.parseString("fn a,b,100"))  # -> ['fn', ['a', 'b', '100']]
+    """
+    def __init__( self, expr ):
+        super(Group,self).__init__( expr )
+        self.saveAsList = True
+
+    def postParse( self, instring, loc, tokenlist ):
+        return [ tokenlist ]
+
+class Dict(TokenConverter):
+    """
+    Converter to return a repetitive expression as a list, but also as a dictionary.
+    Each element can also be referenced using the first token in the expression as its key.
+    Useful for tabular report scraping when the first column can be used as a item key.
+
+    Example::
+        data_word = Word(alphas)
+        label = data_word + FollowedBy(':')
+        attr_expr = Group(label + Suppress(':') + OneOrMore(data_word).setParseAction(' '.join))
+
+        text = "shape: SQUARE posn: upper left color: light blue texture: burlap"
+        attr_expr = (label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join))
+        
+        # print attributes as plain groups
+        print(OneOrMore(attr_expr).parseString(text).dump())
+        
+        # instead of OneOrMore(expr), parse using Dict(OneOrMore(Group(expr))) - Dict will auto-assign names
+        result = Dict(OneOrMore(Group(attr_expr))).parseString(text)
+        print(result.dump())
+        
+        # access named fields as dict entries, or output as dict
+        print(result['shape'])        
+        print(result.asDict())
+    prints::
+        ['shape', 'SQUARE', 'posn', 'upper left', 'color', 'light blue', 'texture', 'burlap']
+
+        [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'light blue'], ['texture', 'burlap']]
+        - color: light blue
+        - posn: upper left
+        - shape: SQUARE
+        - texture: burlap
+        SQUARE
+        {'color': 'light blue', 'posn': 'upper left', 'texture': 'burlap', 'shape': 'SQUARE'}
+    See more examples at L{ParseResults} of accessing fields by results name.
+    """
+    def __init__( self, expr ):
+        super(Dict,self).__init__( expr )
+        self.saveAsList = True
+
+    def postParse( self, instring, loc, tokenlist ):
+        for i,tok in enumerate(tokenlist):
+            if len(tok) == 0:
+                continue
+            ikey = tok[0]
+            if isinstance(ikey,int):
+                ikey = _ustr(tok[0]).strip()
+            if len(tok)==1:
+                tokenlist[ikey] = _ParseResultsWithOffset("",i)
+            elif len(tok)==2 and not isinstance(tok[1],ParseResults):
+                tokenlist[ikey] = _ParseResultsWithOffset(tok[1],i)
+            else:
+                dictvalue = tok.copy() #ParseResults(i)
+                del dictvalue[0]
+                if len(dictvalue)!= 1 or (isinstance(dictvalue,ParseResults) and dictvalue.haskeys()):
+                    tokenlist[ikey] = _ParseResultsWithOffset(dictvalue,i)
+                else:
+                    tokenlist[ikey] = _ParseResultsWithOffset(dictvalue[0],i)
+
+        if self.resultsName:
+            return [ tokenlist ]
+        else:
+            return tokenlist
+
+
+class Suppress(TokenConverter):
+    """
+    Converter for ignoring the results of a parsed expression.
+
+    Example::
+        source = "a, b, c,d"
+        wd = Word(alphas)
+        wd_list1 = wd + ZeroOrMore(',' + wd)
+        print(wd_list1.parseString(source))
+
+        # often, delimiters that are useful during parsing are just in the
+        # way afterward - use Suppress to keep them out of the parsed output
+        wd_list2 = wd + ZeroOrMore(Suppress(',') + wd)
+        print(wd_list2.parseString(source))
+    prints::
+        ['a', ',', 'b', ',', 'c', ',', 'd']
+        ['a', 'b', 'c', 'd']
+    (See also L{delimitedList}.)
+    """
+    def postParse( self, instring, loc, tokenlist ):
+        return []
+
+    def suppress( self ):
+        return self
+
+
+class OnlyOnce(object):
+    """
+    Wrapper for parse actions, to ensure they are only called once.
+    """
+    def __init__(self, methodCall):
+        self.callable = _trim_arity(methodCall)
+        self.called = False
+    def __call__(self,s,l,t):
+        if not self.called:
+            results = self.callable(s,l,t)
+            self.called = True
+            return results
+        raise ParseException(s,l,"")
+    def reset(self):
+        self.called = False
+
+def traceParseAction(f):
+    """
+    Decorator for debugging parse actions. 
+    
+    When the parse action is called, this decorator will print C{">> entering I{method-name}(line:I{current_source_line}, I{parse_location}, I{matched_tokens})".}
+    When the parse action completes, the decorator will print C{"<<"} followed by the returned value, or any exception that the parse action raised.
+
+    Example::
+        wd = Word(alphas)
+
+        @traceParseAction
+        def remove_duplicate_chars(tokens):
+            return ''.join(sorted(set(''.join(tokens))))
+
+        wds = OneOrMore(wd).setParseAction(remove_duplicate_chars)
+        print(wds.parseString("slkdjs sld sldd sdlf sdljf"))
+    prints::
+        >>entering remove_duplicate_chars(line: 'slkdjs sld sldd sdlf sdljf', 0, (['slkdjs', 'sld', 'sldd', 'sdlf', 'sdljf'], {}))
+        <3:
+            thisFunc = paArgs[0].__class__.__name__ + '.' + thisFunc
+        sys.stderr.write( ">>entering %s(line: '%s', %d, %r)\n" % (thisFunc,line(l,s),l,t) )
+        try:
+            ret = f(*paArgs)
+        except Exception as exc:
+            sys.stderr.write( "< ['aa', 'bb', 'cc']
+        delimitedList(Word(hexnums), delim=':', combine=True).parseString("AA:BB:CC:DD:EE") # -> ['AA:BB:CC:DD:EE']
+    """
+    dlName = _ustr(expr)+" ["+_ustr(delim)+" "+_ustr(expr)+"]..."
+    if combine:
+        return Combine( expr + ZeroOrMore( delim + expr ) ).setName(dlName)
+    else:
+        return ( expr + ZeroOrMore( Suppress( delim ) + expr ) ).setName(dlName)
+
+def countedArray( expr, intExpr=None ):
+    """
+    Helper to define a counted list of expressions.
+    This helper defines a pattern of the form::
+        integer expr expr expr...
+    where the leading integer tells how many expr expressions follow.
+    The matched tokens returns the array of expr tokens as a list - the leading count token is suppressed.
+    
+    If C{intExpr} is specified, it should be a pyparsing expression that produces an integer value.
+
+    Example::
+        countedArray(Word(alphas)).parseString('2 ab cd ef')  # -> ['ab', 'cd']
+
+        # in this parser, the leading integer value is given in binary,
+        # '10' indicating that 2 values are in the array
+        binaryConstant = Word('01').setParseAction(lambda t: int(t[0], 2))
+        countedArray(Word(alphas), intExpr=binaryConstant).parseString('10 ab cd ef')  # -> ['ab', 'cd']
+    """
+    arrayExpr = Forward()
+    def countFieldParseAction(s,l,t):
+        n = t[0]
+        arrayExpr << (n and Group(And([expr]*n)) or Group(empty))
+        return []
+    if intExpr is None:
+        intExpr = Word(nums).setParseAction(lambda t:int(t[0]))
+    else:
+        intExpr = intExpr.copy()
+    intExpr.setName("arrayLen")
+    intExpr.addParseAction(countFieldParseAction, callDuringTry=True)
+    return ( intExpr + arrayExpr ).setName('(len) ' + _ustr(expr) + '...')
+
+def _flatten(L):
+    ret = []
+    for i in L:
+        if isinstance(i,list):
+            ret.extend(_flatten(i))
+        else:
+            ret.append(i)
+    return ret
+
+def matchPreviousLiteral(expr):
+    """
+    Helper to define an expression that is indirectly defined from
+    the tokens matched in a previous expression, that is, it looks
+    for a 'repeat' of a previous expression.  For example::
+        first = Word(nums)
+        second = matchPreviousLiteral(first)
+        matchExpr = first + ":" + second
+    will match C{"1:1"}, but not C{"1:2"}.  Because this matches a
+    previous literal, will also match the leading C{"1:1"} in C{"1:10"}.
+    If this is not desired, use C{matchPreviousExpr}.
+    Do I{not} use with packrat parsing enabled.
+    """
+    rep = Forward()
+    def copyTokenToRepeater(s,l,t):
+        if t:
+            if len(t) == 1:
+                rep << t[0]
+            else:
+                # flatten t tokens
+                tflat = _flatten(t.asList())
+                rep << And(Literal(tt) for tt in tflat)
+        else:
+            rep << Empty()
+    expr.addParseAction(copyTokenToRepeater, callDuringTry=True)
+    rep.setName('(prev) ' + _ustr(expr))
+    return rep
+
+def matchPreviousExpr(expr):
+    """
+    Helper to define an expression that is indirectly defined from
+    the tokens matched in a previous expression, that is, it looks
+    for a 'repeat' of a previous expression.  For example::
+        first = Word(nums)
+        second = matchPreviousExpr(first)
+        matchExpr = first + ":" + second
+    will match C{"1:1"}, but not C{"1:2"}.  Because this matches by
+    expressions, will I{not} match the leading C{"1:1"} in C{"1:10"};
+    the expressions are evaluated first, and then compared, so
+    C{"1"} is compared with C{"10"}.
+    Do I{not} use with packrat parsing enabled.
+    """
+    rep = Forward()
+    e2 = expr.copy()
+    rep <<= e2
+    def copyTokenToRepeater(s,l,t):
+        matchTokens = _flatten(t.asList())
+        def mustMatchTheseTokens(s,l,t):
+            theseTokens = _flatten(t.asList())
+            if  theseTokens != matchTokens:
+                raise ParseException("",0,"")
+        rep.setParseAction( mustMatchTheseTokens, callDuringTry=True )
+    expr.addParseAction(copyTokenToRepeater, callDuringTry=True)
+    rep.setName('(prev) ' + _ustr(expr))
+    return rep
+
+def _escapeRegexRangeChars(s):
+    #~  escape these chars: ^-]
+    for c in r"\^-]":
+        s = s.replace(c,_bslash+c)
+    s = s.replace("\n",r"\n")
+    s = s.replace("\t",r"\t")
+    return _ustr(s)
+
+def oneOf( strs, caseless=False, useRegex=True ):
+    """
+    Helper to quickly define a set of alternative Literals, and makes sure to do
+    longest-first testing when there is a conflict, regardless of the input order,
+    but returns a C{L{MatchFirst}} for best performance.
+
+    Parameters:
+     - strs - a string of space-delimited literals, or a collection of string literals
+     - caseless - (default=C{False}) - treat all literals as caseless
+     - useRegex - (default=C{True}) - as an optimization, will generate a Regex
+          object; otherwise, will generate a C{MatchFirst} object (if C{caseless=True}, or
+          if creating a C{Regex} raises an exception)
+
+    Example::
+        comp_oper = oneOf("< = > <= >= !=")
+        var = Word(alphas)
+        number = Word(nums)
+        term = var | number
+        comparison_expr = term + comp_oper + term
+        print(comparison_expr.searchString("B = 12  AA=23 B<=AA AA>12"))
+    prints::
+        [['B', '=', '12'], ['AA', '=', '23'], ['B', '<=', 'AA'], ['AA', '>', '12']]
+    """
+    if caseless:
+        isequal = ( lambda a,b: a.upper() == b.upper() )
+        masks = ( lambda a,b: b.upper().startswith(a.upper()) )
+        parseElementClass = CaselessLiteral
+    else:
+        isequal = ( lambda a,b: a == b )
+        masks = ( lambda a,b: b.startswith(a) )
+        parseElementClass = Literal
+
+    symbols = []
+    if isinstance(strs,basestring):
+        symbols = strs.split()
+    elif isinstance(strs, Iterable):
+        symbols = list(strs)
+    else:
+        warnings.warn("Invalid argument to oneOf, expected string or iterable",
+                SyntaxWarning, stacklevel=2)
+    if not symbols:
+        return NoMatch()
+
+    i = 0
+    while i < len(symbols)-1:
+        cur = symbols[i]
+        for j,other in enumerate(symbols[i+1:]):
+            if ( isequal(other, cur) ):
+                del symbols[i+j+1]
+                break
+            elif ( masks(cur, other) ):
+                del symbols[i+j+1]
+                symbols.insert(i,other)
+                cur = other
+                break
+        else:
+            i += 1
+
+    if not caseless and useRegex:
+        #~ print (strs,"->", "|".join( [ _escapeRegexChars(sym) for sym in symbols] ))
+        try:
+            if len(symbols)==len("".join(symbols)):
+                return Regex( "[%s]" % "".join(_escapeRegexRangeChars(sym) for sym in symbols) ).setName(' | '.join(symbols))
+            else:
+                return Regex( "|".join(re.escape(sym) for sym in symbols) ).setName(' | '.join(symbols))
+        except Exception:
+            warnings.warn("Exception creating Regex for oneOf, building MatchFirst",
+                    SyntaxWarning, stacklevel=2)
+
+
+    # last resort, just use MatchFirst
+    return MatchFirst(parseElementClass(sym) for sym in symbols).setName(' | '.join(symbols))
+
+def dictOf( key, value ):
+    """
+    Helper to easily and clearly define a dictionary by specifying the respective patterns
+    for the key and value.  Takes care of defining the C{L{Dict}}, C{L{ZeroOrMore}}, and C{L{Group}} tokens
+    in the proper order.  The key pattern can include delimiting markers or punctuation,
+    as long as they are suppressed, thereby leaving the significant key text.  The value
+    pattern can include named results, so that the C{Dict} results can include named token
+    fields.
+
+    Example::
+        text = "shape: SQUARE posn: upper left color: light blue texture: burlap"
+        attr_expr = (label + Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join))
+        print(OneOrMore(attr_expr).parseString(text).dump())
+        
+        attr_label = label
+        attr_value = Suppress(':') + OneOrMore(data_word, stopOn=label).setParseAction(' '.join)
+
+        # similar to Dict, but simpler call format
+        result = dictOf(attr_label, attr_value).parseString(text)
+        print(result.dump())
+        print(result['shape'])
+        print(result.shape)  # object attribute access works too
+        print(result.asDict())
+    prints::
+        [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'light blue'], ['texture', 'burlap']]
+        - color: light blue
+        - posn: upper left
+        - shape: SQUARE
+        - texture: burlap
+        SQUARE
+        SQUARE
+        {'color': 'light blue', 'shape': 'SQUARE', 'posn': 'upper left', 'texture': 'burlap'}
+    """
+    return Dict( ZeroOrMore( Group ( key + value ) ) )
+
+def originalTextFor(expr, asString=True):
+    """
+    Helper to return the original, untokenized text for a given expression.  Useful to
+    restore the parsed fields of an HTML start tag into the raw tag text itself, or to
+    revert separate tokens with intervening whitespace back to the original matching
+    input text. By default, returns astring containing the original parsed text.  
+       
+    If the optional C{asString} argument is passed as C{False}, then the return value is a 
+    C{L{ParseResults}} containing any results names that were originally matched, and a 
+    single token containing the original matched text from the input string.  So if 
+    the expression passed to C{L{originalTextFor}} contains expressions with defined
+    results names, you must set C{asString} to C{False} if you want to preserve those
+    results name values.
+
+    Example::
+        src = "this is test  bold text  normal text "
+        for tag in ("b","i"):
+            opener,closer = makeHTMLTags(tag)
+            patt = originalTextFor(opener + SkipTo(closer) + closer)
+            print(patt.searchString(src)[0])
+    prints::
+        [' bold text ']
+        ['text']
+    """
+    locMarker = Empty().setParseAction(lambda s,loc,t: loc)
+    endlocMarker = locMarker.copy()
+    endlocMarker.callPreparse = False
+    matchExpr = locMarker("_original_start") + expr + endlocMarker("_original_end")
+    if asString:
+        extractText = lambda s,l,t: s[t._original_start:t._original_end]
+    else:
+        def extractText(s,l,t):
+            t[:] = [s[t.pop('_original_start'):t.pop('_original_end')]]
+    matchExpr.setParseAction(extractText)
+    matchExpr.ignoreExprs = expr.ignoreExprs
+    return matchExpr
+
+def ungroup(expr): 
+    """
+    Helper to undo pyparsing's default grouping of And expressions, even
+    if all but one are non-empty.
+    """
+    return TokenConverter(expr).setParseAction(lambda t:t[0])
+
+def locatedExpr(expr):
+    """
+    Helper to decorate a returned token with its starting and ending locations in the input string.
+    This helper adds the following results names:
+     - locn_start = location where matched expression begins
+     - locn_end = location where matched expression ends
+     - value = the actual parsed results
+
+    Be careful if the input text contains C{} characters, you may want to call
+    C{L{ParserElement.parseWithTabs}}
+
+    Example::
+        wd = Word(alphas)
+        for match in locatedExpr(wd).searchString("ljsdf123lksdjjf123lkkjj1222"):
+            print(match)
+    prints::
+        [[0, 'ljsdf', 5]]
+        [[8, 'lksdjjf', 15]]
+        [[18, 'lkkjj', 23]]
+    """
+    locator = Empty().setParseAction(lambda s,l,t: l)
+    return Group(locator("locn_start") + expr("value") + locator.copy().leaveWhitespace()("locn_end"))
+
+
+# convenience constants for positional expressions
+empty       = Empty().setName("empty")
+lineStart   = LineStart().setName("lineStart")
+lineEnd     = LineEnd().setName("lineEnd")
+stringStart = StringStart().setName("stringStart")
+stringEnd   = StringEnd().setName("stringEnd")
+
+_escapedPunc = Word( _bslash, r"\[]-*.$+^?()~ ", exact=2 ).setParseAction(lambda s,l,t:t[0][1])
+_escapedHexChar = Regex(r"\\0?[xX][0-9a-fA-F]+").setParseAction(lambda s,l,t:unichr(int(t[0].lstrip(r'\0x'),16)))
+_escapedOctChar = Regex(r"\\0[0-7]+").setParseAction(lambda s,l,t:unichr(int(t[0][1:],8)))
+_singleChar = _escapedPunc | _escapedHexChar | _escapedOctChar | CharsNotIn(r'\]', exact=1)
+_charRange = Group(_singleChar + Suppress("-") + _singleChar)
+_reBracketExpr = Literal("[") + Optional("^").setResultsName("negate") + Group( OneOrMore( _charRange | _singleChar ) ).setResultsName("body") + "]"
+
+def srange(s):
+    r"""
+    Helper to easily define string ranges for use in Word construction.  Borrows
+    syntax from regexp '[]' string range definitions::
+        srange("[0-9]")   -> "0123456789"
+        srange("[a-z]")   -> "abcdefghijklmnopqrstuvwxyz"
+        srange("[a-z$_]") -> "abcdefghijklmnopqrstuvwxyz$_"
+    The input string must be enclosed in []'s, and the returned string is the expanded
+    character set joined into a single string.
+    The values enclosed in the []'s may be:
+     - a single character
+     - an escaped character with a leading backslash (such as C{\-} or C{\]})
+     - an escaped hex character with a leading C{'\x'} (C{\x21}, which is a C{'!'} character) 
+         (C{\0x##} is also supported for backwards compatibility) 
+     - an escaped octal character with a leading C{'\0'} (C{\041}, which is a C{'!'} character)
+     - a range of any of the above, separated by a dash (C{'a-z'}, etc.)
+     - any combination of the above (C{'aeiouy'}, C{'a-zA-Z0-9_$'}, etc.)
+    """
+    _expanded = lambda p: p if not isinstance(p,ParseResults) else ''.join(unichr(c) for c in range(ord(p[0]),ord(p[1])+1))
+    try:
+        return "".join(_expanded(part) for part in _reBracketExpr.parseString(s).body)
+    except Exception:
+        return ""
+
+def matchOnlyAtCol(n):
+    """
+    Helper method for defining parse actions that require matching at a specific
+    column in the input text.
+    """
+    def verifyCol(strg,locn,toks):
+        if col(locn,strg) != n:
+            raise ParseException(strg,locn,"matched token not at column %d" % n)
+    return verifyCol
+
+def replaceWith(replStr):
+    """
+    Helper method for common parse actions that simply return a literal value.  Especially
+    useful when used with C{L{transformString}()}.
+
+    Example::
+        num = Word(nums).setParseAction(lambda toks: int(toks[0]))
+        na = oneOf("N/A NA").setParseAction(replaceWith(math.nan))
+        term = na | num
+        
+        OneOrMore(term).parseString("324 234 N/A 234") # -> [324, 234, nan, 234]
+    """
+    return lambda s,l,t: [replStr]
+
+def removeQuotes(s,l,t):
+    """
+    Helper parse action for removing quotation marks from parsed quoted strings.
+
+    Example::
+        # by default, quotation marks are included in parsed results
+        quotedString.parseString("'Now is the Winter of our Discontent'") # -> ["'Now is the Winter of our Discontent'"]
+
+        # use removeQuotes to strip quotation marks from parsed results
+        quotedString.setParseAction(removeQuotes)
+        quotedString.parseString("'Now is the Winter of our Discontent'") # -> ["Now is the Winter of our Discontent"]
+    """
+    return t[0][1:-1]
+
+def tokenMap(func, *args):
+    """
+    Helper to define a parse action by mapping a function to all elements of a ParseResults list.If any additional 
+    args are passed, they are forwarded to the given function as additional arguments after
+    the token, as in C{hex_integer = Word(hexnums).setParseAction(tokenMap(int, 16))}, which will convert the
+    parsed data to an integer using base 16.
+
+    Example (compare the last to example in L{ParserElement.transformString}::
+        hex_ints = OneOrMore(Word(hexnums)).setParseAction(tokenMap(int, 16))
+        hex_ints.runTests('''
+            00 11 22 aa FF 0a 0d 1a
+            ''')
+        
+        upperword = Word(alphas).setParseAction(tokenMap(str.upper))
+        OneOrMore(upperword).runTests('''
+            my kingdom for a horse
+            ''')
+
+        wd = Word(alphas).setParseAction(tokenMap(str.title))
+        OneOrMore(wd).setParseAction(' '.join).runTests('''
+            now is the winter of our discontent made glorious summer by this sun of york
+            ''')
+    prints::
+        00 11 22 aa FF 0a 0d 1a
+        [0, 17, 34, 170, 255, 10, 13, 26]
+
+        my kingdom for a horse
+        ['MY', 'KINGDOM', 'FOR', 'A', 'HORSE']
+
+        now is the winter of our discontent made glorious summer by this sun of york
+        ['Now Is The Winter Of Our Discontent Made Glorious Summer By This Sun Of York']
+    """
+    def pa(s,l,t):
+        return [func(tokn, *args) for tokn in t]
+
+    try:
+        func_name = getattr(func, '__name__', 
+                            getattr(func, '__class__').__name__)
+    except Exception:
+        func_name = str(func)
+    pa.__name__ = func_name
+
+    return pa
+
+upcaseTokens = tokenMap(lambda t: _ustr(t).upper())
+"""(Deprecated) Helper parse action to convert tokens to upper case. Deprecated in favor of L{pyparsing_common.upcaseTokens}"""
+
+downcaseTokens = tokenMap(lambda t: _ustr(t).lower())
+"""(Deprecated) Helper parse action to convert tokens to lower case. Deprecated in favor of L{pyparsing_common.downcaseTokens}"""
+    
+def _makeTags(tagStr, xml):
+    """Internal helper to construct opening and closing tag expressions, given a tag name"""
+    if isinstance(tagStr,basestring):
+        resname = tagStr
+        tagStr = Keyword(tagStr, caseless=not xml)
+    else:
+        resname = tagStr.name
+
+    tagAttrName = Word(alphas,alphanums+"_-:")
+    if (xml):
+        tagAttrValue = dblQuotedString.copy().setParseAction( removeQuotes )
+        openTag = Suppress("<") + tagStr("tag") + \
+                Dict(ZeroOrMore(Group( tagAttrName + Suppress("=") + tagAttrValue ))) + \
+                Optional("/",default=[False]).setResultsName("empty").setParseAction(lambda s,l,t:t[0]=='/') + Suppress(">")
+    else:
+        printablesLessRAbrack = "".join(c for c in printables if c not in ">")
+        tagAttrValue = quotedString.copy().setParseAction( removeQuotes ) | Word(printablesLessRAbrack)
+        openTag = Suppress("<") + tagStr("tag") + \
+                Dict(ZeroOrMore(Group( tagAttrName.setParseAction(downcaseTokens) + \
+                Optional( Suppress("=") + tagAttrValue ) ))) + \
+                Optional("/",default=[False]).setResultsName("empty").setParseAction(lambda s,l,t:t[0]=='/') + Suppress(">")
+    closeTag = Combine(_L("")
+
+    openTag = openTag.setResultsName("start"+"".join(resname.replace(":"," ").title().split())).setName("<%s>" % resname)
+    closeTag = closeTag.setResultsName("end"+"".join(resname.replace(":"," ").title().split())).setName("" % resname)
+    openTag.tag = resname
+    closeTag.tag = resname
+    return openTag, closeTag
+
+def makeHTMLTags(tagStr):
+    """
+    Helper to construct opening and closing tag expressions for HTML, given a tag name. Matches
+    tags in either upper or lower case, attributes with namespaces and with quoted or unquoted values.
+
+    Example::
+        text = 'More info at the pyparsing wiki page'
+        # makeHTMLTags returns pyparsing expressions for the opening and closing tags as a 2-tuple
+        a,a_end = makeHTMLTags("A")
+        link_expr = a + SkipTo(a_end)("link_text") + a_end
+        
+        for link in link_expr.searchString(text):
+            # attributes in the  tag (like "href" shown here) are also accessible as named results
+            print(link.link_text, '->', link.href)
+    prints::
+        pyparsing -> http://pyparsing.wikispaces.com
+    """
+    return _makeTags( tagStr, False )
+
+def makeXMLTags(tagStr):
+    """
+    Helper to construct opening and closing tag expressions for XML, given a tag name. Matches
+    tags only in the given upper/lower case.
+
+    Example: similar to L{makeHTMLTags}
+    """
+    return _makeTags( tagStr, True )
+
+def withAttribute(*args,**attrDict):
+    """
+    Helper to create a validating parse action to be used with start tags created
+    with C{L{makeXMLTags}} or C{L{makeHTMLTags}}. Use C{withAttribute} to qualify a starting tag
+    with a required attribute value, to avoid false matches on common tags such as
+    C{} or C{
}. + + Call C{withAttribute} with a series of attribute names and values. Specify the list + of filter attributes names and values as: + - keyword arguments, as in C{(align="right")}, or + - as an explicit dict with C{**} operator, when an attribute name is also a Python + reserved word, as in C{**{"class":"Customer", "align":"right"}} + - a list of name-value tuples, as in ( ("ns1:class", "Customer"), ("ns2:align","right") ) + For attribute names with a namespace prefix, you must use the second form. Attribute + names are matched insensitive to upper/lower case. + + If just testing for C{class} (with or without a namespace), use C{L{withClass}}. + + To verify that the attribute exists, but without specifying a value, pass + C{withAttribute.ANY_VALUE} as the value. + + Example:: + html = ''' +
+ Some text +
1 4 0 1 0
+
1,3 2,3 1,1
+
this has no type
+
+ + ''' + div,div_end = makeHTMLTags("div") + + # only match div tag having a type attribute with value "grid" + div_grid = div().setParseAction(withAttribute(type="grid")) + grid_expr = div_grid + SkipTo(div | div_end)("body") + for grid_header in grid_expr.searchString(html): + print(grid_header.body) + + # construct a match with any div tag having a type attribute, regardless of the value + div_any_type = div().setParseAction(withAttribute(type=withAttribute.ANY_VALUE)) + div_expr = div_any_type + SkipTo(div | div_end)("body") + for div_header in div_expr.searchString(html): + print(div_header.body) + prints:: + 1 4 0 1 0 + + 1 4 0 1 0 + 1,3 2,3 1,1 + """ + if args: + attrs = args[:] + else: + attrs = attrDict.items() + attrs = [(k,v) for k,v in attrs] + def pa(s,l,tokens): + for attrName,attrValue in attrs: + if attrName not in tokens: + raise ParseException(s,l,"no matching attribute " + attrName) + if attrValue != withAttribute.ANY_VALUE and tokens[attrName] != attrValue: + raise ParseException(s,l,"attribute '%s' has value '%s', must be '%s'" % + (attrName, tokens[attrName], attrValue)) + return pa +withAttribute.ANY_VALUE = object() + +def withClass(classname, namespace=''): + """ + Simplified version of C{L{withAttribute}} when matching on a div class - made + difficult because C{class} is a reserved word in Python. + + Example:: + html = ''' +
+ Some text +
1 4 0 1 0
+
1,3 2,3 1,1
+
this <div> has no class
+
+ + ''' + div,div_end = makeHTMLTags("div") + div_grid = div().setParseAction(withClass("grid")) + + grid_expr = div_grid + SkipTo(div | div_end)("body") + for grid_header in grid_expr.searchString(html): + print(grid_header.body) + + div_any_type = div().setParseAction(withClass(withAttribute.ANY_VALUE)) + div_expr = div_any_type + SkipTo(div | div_end)("body") + for div_header in div_expr.searchString(html): + print(div_header.body) + prints:: + 1 4 0 1 0 + + 1 4 0 1 0 + 1,3 2,3 1,1 + """ + classattr = "%s:class" % namespace if namespace else "class" + return withAttribute(**{classattr : classname}) + +opAssoc = _Constants() +opAssoc.LEFT = object() +opAssoc.RIGHT = object() + +def infixNotation( baseExpr, opList, lpar=Suppress('('), rpar=Suppress(')') ): + """ + Helper method for constructing grammars of expressions made up of + operators working in a precedence hierarchy. Operators may be unary or + binary, left- or right-associative. Parse actions can also be attached + to operator expressions. The generated parser will also recognize the use + of parentheses to override operator precedences (see example below). + + Note: if you define a deep operator list, you may see performance issues + when using infixNotation. See L{ParserElement.enablePackrat} for a + mechanism to potentially improve your parser performance. + + Parameters: + - baseExpr - expression representing the most basic element for the nested + - opList - list of tuples, one for each operator precedence level in the + expression grammar; each tuple is of the form + (opExpr, numTerms, rightLeftAssoc, parseAction), where: + - opExpr is the pyparsing expression for the operator; + may also be a string, which will be converted to a Literal; + if numTerms is 3, opExpr is a tuple of two expressions, for the + two operators separating the 3 terms + - numTerms is the number of terms for this operator (must + be 1, 2, or 3) + - rightLeftAssoc is the indicator whether the operator is + right or left associative, using the pyparsing-defined + constants C{opAssoc.RIGHT} and C{opAssoc.LEFT}. + - parseAction is the parse action to be associated with + expressions matching this operator expression (the + parse action tuple member may be omitted); if the parse action + is passed a tuple or list of functions, this is equivalent to + calling C{setParseAction(*fn)} (L{ParserElement.setParseAction}) + - lpar - expression for matching left-parentheses (default=C{Suppress('(')}) + - rpar - expression for matching right-parentheses (default=C{Suppress(')')}) + + Example:: + # simple example of four-function arithmetic with ints and variable names + integer = pyparsing_common.signed_integer + varname = pyparsing_common.identifier + + arith_expr = infixNotation(integer | varname, + [ + ('-', 1, opAssoc.RIGHT), + (oneOf('* /'), 2, opAssoc.LEFT), + (oneOf('+ -'), 2, opAssoc.LEFT), + ]) + + arith_expr.runTests(''' + 5+3*6 + (5+3)*6 + -2--11 + ''', fullDump=False) + prints:: + 5+3*6 + [[5, '+', [3, '*', 6]]] + + (5+3)*6 + [[[5, '+', 3], '*', 6]] + + -2--11 + [[['-', 2], '-', ['-', 11]]] + """ + ret = Forward() + lastExpr = baseExpr | ( lpar + ret + rpar ) + for i,operDef in enumerate(opList): + opExpr,arity,rightLeftAssoc,pa = (operDef + (None,))[:4] + termName = "%s term" % opExpr if arity < 3 else "%s%s term" % opExpr + if arity == 3: + if opExpr is None or len(opExpr) != 2: + raise ValueError("if numterms=3, opExpr must be a tuple or list of two expressions") + opExpr1, opExpr2 = opExpr + thisExpr = Forward().setName(termName) + if rightLeftAssoc == opAssoc.LEFT: + if arity == 1: + matchExpr = FollowedBy(lastExpr + opExpr) + Group( lastExpr + OneOrMore( opExpr ) ) + elif arity == 2: + if opExpr is not None: + matchExpr = FollowedBy(lastExpr + opExpr + lastExpr) + Group( lastExpr + OneOrMore( opExpr + lastExpr ) ) + else: + matchExpr = FollowedBy(lastExpr+lastExpr) + Group( lastExpr + OneOrMore(lastExpr) ) + elif arity == 3: + matchExpr = FollowedBy(lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr) + \ + Group( lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr ) + else: + raise ValueError("operator must be unary (1), binary (2), or ternary (3)") + elif rightLeftAssoc == opAssoc.RIGHT: + if arity == 1: + # try to avoid LR with this extra test + if not isinstance(opExpr, Optional): + opExpr = Optional(opExpr) + matchExpr = FollowedBy(opExpr.expr + thisExpr) + Group( opExpr + thisExpr ) + elif arity == 2: + if opExpr is not None: + matchExpr = FollowedBy(lastExpr + opExpr + thisExpr) + Group( lastExpr + OneOrMore( opExpr + thisExpr ) ) + else: + matchExpr = FollowedBy(lastExpr + thisExpr) + Group( lastExpr + OneOrMore( thisExpr ) ) + elif arity == 3: + matchExpr = FollowedBy(lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr) + \ + Group( lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr ) + else: + raise ValueError("operator must be unary (1), binary (2), or ternary (3)") + else: + raise ValueError("operator must indicate right or left associativity") + if pa: + if isinstance(pa, (tuple, list)): + matchExpr.setParseAction(*pa) + else: + matchExpr.setParseAction(pa) + thisExpr <<= ( matchExpr.setName(termName) | lastExpr ) + lastExpr = thisExpr + ret <<= lastExpr + return ret + +operatorPrecedence = infixNotation +"""(Deprecated) Former name of C{L{infixNotation}}, will be dropped in a future release.""" + +dblQuotedString = Combine(Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*')+'"').setName("string enclosed in double quotes") +sglQuotedString = Combine(Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*")+"'").setName("string enclosed in single quotes") +quotedString = Combine(Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*')+'"'| + Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*")+"'").setName("quotedString using single or double quotes") +unicodeString = Combine(_L('u') + quotedString.copy()).setName("unicode string literal") + +def nestedExpr(opener="(", closer=")", content=None, ignoreExpr=quotedString.copy()): + """ + Helper method for defining nested lists enclosed in opening and closing + delimiters ("(" and ")" are the default). + + Parameters: + - opener - opening character for a nested list (default=C{"("}); can also be a pyparsing expression + - closer - closing character for a nested list (default=C{")"}); can also be a pyparsing expression + - content - expression for items within the nested lists (default=C{None}) + - ignoreExpr - expression for ignoring opening and closing delimiters (default=C{quotedString}) + + If an expression is not provided for the content argument, the nested + expression will capture all whitespace-delimited content between delimiters + as a list of separate values. + + Use the C{ignoreExpr} argument to define expressions that may contain + opening or closing characters that should not be treated as opening + or closing characters for nesting, such as quotedString or a comment + expression. Specify multiple expressions using an C{L{Or}} or C{L{MatchFirst}}. + The default is L{quotedString}, but if no expressions are to be ignored, + then pass C{None} for this argument. + + Example:: + data_type = oneOf("void int short long char float double") + decl_data_type = Combine(data_type + Optional(Word('*'))) + ident = Word(alphas+'_', alphanums+'_') + number = pyparsing_common.number + arg = Group(decl_data_type + ident) + LPAR,RPAR = map(Suppress, "()") + + code_body = nestedExpr('{', '}', ignoreExpr=(quotedString | cStyleComment)) + + c_function = (decl_data_type("type") + + ident("name") + + LPAR + Optional(delimitedList(arg), [])("args") + RPAR + + code_body("body")) + c_function.ignore(cStyleComment) + + source_code = ''' + int is_odd(int x) { + return (x%2); + } + + int dec_to_hex(char hchar) { + if (hchar >= '0' && hchar <= '9') { + return (ord(hchar)-ord('0')); + } else { + return (10+ord(hchar)-ord('A')); + } + } + ''' + for func in c_function.searchString(source_code): + print("%(name)s (%(type)s) args: %(args)s" % func) + + prints:: + is_odd (int) args: [['int', 'x']] + dec_to_hex (int) args: [['char', 'hchar']] + """ + if opener == closer: + raise ValueError("opening and closing strings cannot be the same") + if content is None: + if isinstance(opener,basestring) and isinstance(closer,basestring): + if len(opener) == 1 and len(closer)==1: + if ignoreExpr is not None: + content = (Combine(OneOrMore(~ignoreExpr + + CharsNotIn(opener+closer+ParserElement.DEFAULT_WHITE_CHARS,exact=1)) + ).setParseAction(lambda t:t[0].strip())) + else: + content = (empty.copy()+CharsNotIn(opener+closer+ParserElement.DEFAULT_WHITE_CHARS + ).setParseAction(lambda t:t[0].strip())) + else: + if ignoreExpr is not None: + content = (Combine(OneOrMore(~ignoreExpr + + ~Literal(opener) + ~Literal(closer) + + CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS,exact=1)) + ).setParseAction(lambda t:t[0].strip())) + else: + content = (Combine(OneOrMore(~Literal(opener) + ~Literal(closer) + + CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS,exact=1)) + ).setParseAction(lambda t:t[0].strip())) + else: + raise ValueError("opening and closing arguments must be strings if no content expression is given") + ret = Forward() + if ignoreExpr is not None: + ret <<= Group( Suppress(opener) + ZeroOrMore( ignoreExpr | ret | content ) + Suppress(closer) ) + else: + ret <<= Group( Suppress(opener) + ZeroOrMore( ret | content ) + Suppress(closer) ) + ret.setName('nested %s%s expression' % (opener,closer)) + return ret + +def indentedBlock(blockStatementExpr, indentStack, indent=True): + """ + Helper method for defining space-delimited indentation blocks, such as + those used to define block statements in Python source code. + + Parameters: + - blockStatementExpr - expression defining syntax of statement that + is repeated within the indented block + - indentStack - list created by caller to manage indentation stack + (multiple statementWithIndentedBlock expressions within a single grammar + should share a common indentStack) + - indent - boolean indicating whether block must be indented beyond the + the current level; set to False for block of left-most statements + (default=C{True}) + + A valid block must contain at least one C{blockStatement}. + + Example:: + data = ''' + def A(z): + A1 + B = 100 + G = A2 + A2 + A3 + B + def BB(a,b,c): + BB1 + def BBA(): + bba1 + bba2 + bba3 + C + D + def spam(x,y): + def eggs(z): + pass + ''' + + + indentStack = [1] + stmt = Forward() + + identifier = Word(alphas, alphanums) + funcDecl = ("def" + identifier + Group( "(" + Optional( delimitedList(identifier) ) + ")" ) + ":") + func_body = indentedBlock(stmt, indentStack) + funcDef = Group( funcDecl + func_body ) + + rvalue = Forward() + funcCall = Group(identifier + "(" + Optional(delimitedList(rvalue)) + ")") + rvalue << (funcCall | identifier | Word(nums)) + assignment = Group(identifier + "=" + rvalue) + stmt << ( funcDef | assignment | identifier ) + + module_body = OneOrMore(stmt) + + parseTree = module_body.parseString(data) + parseTree.pprint() + prints:: + [['def', + 'A', + ['(', 'z', ')'], + ':', + [['A1'], [['B', '=', '100']], [['G', '=', 'A2']], ['A2'], ['A3']]], + 'B', + ['def', + 'BB', + ['(', 'a', 'b', 'c', ')'], + ':', + [['BB1'], [['def', 'BBA', ['(', ')'], ':', [['bba1'], ['bba2'], ['bba3']]]]]], + 'C', + 'D', + ['def', + 'spam', + ['(', 'x', 'y', ')'], + ':', + [[['def', 'eggs', ['(', 'z', ')'], ':', [['pass']]]]]]] + """ + def checkPeerIndent(s,l,t): + if l >= len(s): return + curCol = col(l,s) + if curCol != indentStack[-1]: + if curCol > indentStack[-1]: + raise ParseFatalException(s,l,"illegal nesting") + raise ParseException(s,l,"not a peer entry") + + def checkSubIndent(s,l,t): + curCol = col(l,s) + if curCol > indentStack[-1]: + indentStack.append( curCol ) + else: + raise ParseException(s,l,"not a subentry") + + def checkUnindent(s,l,t): + if l >= len(s): return + curCol = col(l,s) + if not(indentStack and curCol < indentStack[-1] and curCol <= indentStack[-2]): + raise ParseException(s,l,"not an unindent") + indentStack.pop() + + NL = OneOrMore(LineEnd().setWhitespaceChars("\t ").suppress()) + INDENT = (Empty() + Empty().setParseAction(checkSubIndent)).setName('INDENT') + PEER = Empty().setParseAction(checkPeerIndent).setName('') + UNDENT = Empty().setParseAction(checkUnindent).setName('UNINDENT') + if indent: + smExpr = Group( Optional(NL) + + #~ FollowedBy(blockStatementExpr) + + INDENT + (OneOrMore( PEER + Group(blockStatementExpr) + Optional(NL) )) + UNDENT) + else: + smExpr = Group( Optional(NL) + + (OneOrMore( PEER + Group(blockStatementExpr) + Optional(NL) )) ) + blockStatementExpr.ignore(_bslash + LineEnd()) + return smExpr.setName('indented block') + +alphas8bit = srange(r"[\0xc0-\0xd6\0xd8-\0xf6\0xf8-\0xff]") +punc8bit = srange(r"[\0xa1-\0xbf\0xd7\0xf7]") + +anyOpenTag,anyCloseTag = makeHTMLTags(Word(alphas,alphanums+"_:").setName('any tag')) +_htmlEntityMap = dict(zip("gt lt amp nbsp quot apos".split(),'><& "\'')) +commonHTMLEntity = Regex('&(?P' + '|'.join(_htmlEntityMap.keys()) +");").setName("common HTML entity") +def replaceHTMLEntity(t): + """Helper parser action to replace common HTML entities with their special characters""" + return _htmlEntityMap.get(t.entity) + +# it's easy to get these comment structures wrong - they're very common, so may as well make them available +cStyleComment = Combine(Regex(r"/\*(?:[^*]|\*(?!/))*") + '*/').setName("C style comment") +"Comment of the form C{/* ... */}" + +htmlComment = Regex(r"").setName("HTML comment") +"Comment of the form C{}" + +restOfLine = Regex(r".*").leaveWhitespace().setName("rest of line") +dblSlashComment = Regex(r"//(?:\\\n|[^\n])*").setName("// comment") +"Comment of the form C{// ... (to end of line)}" + +cppStyleComment = Combine(Regex(r"/\*(?:[^*]|\*(?!/))*") + '*/'| dblSlashComment).setName("C++ style comment") +"Comment of either form C{L{cStyleComment}} or C{L{dblSlashComment}}" + +javaStyleComment = cppStyleComment +"Same as C{L{cppStyleComment}}" + +pythonStyleComment = Regex(r"#.*").setName("Python style comment") +"Comment of the form C{# ... (to end of line)}" + +_commasepitem = Combine(OneOrMore(Word(printables, excludeChars=',') + + Optional( Word(" \t") + + ~Literal(",") + ~LineEnd() ) ) ).streamline().setName("commaItem") +commaSeparatedList = delimitedList( Optional( quotedString.copy() | _commasepitem, default="") ).setName("commaSeparatedList") +"""(Deprecated) Predefined expression of 1 or more printable words or quoted strings, separated by commas. + This expression is deprecated in favor of L{pyparsing_common.comma_separated_list}.""" + +# some other useful expressions - using lower-case class name since we are really using this as a namespace +class pyparsing_common: + """ + Here are some common low-level expressions that may be useful in jump-starting parser development: + - numeric forms (L{integers}, L{reals}, L{scientific notation}) + - common L{programming identifiers} + - network addresses (L{MAC}, L{IPv4}, L{IPv6}) + - ISO8601 L{dates} and L{datetime} + - L{UUID} + - L{comma-separated list} + Parse actions: + - C{L{convertToInteger}} + - C{L{convertToFloat}} + - C{L{convertToDate}} + - C{L{convertToDatetime}} + - C{L{stripHTMLTags}} + - C{L{upcaseTokens}} + - C{L{downcaseTokens}} + + Example:: + pyparsing_common.number.runTests(''' + # any int or real number, returned as the appropriate type + 100 + -100 + +100 + 3.14159 + 6.02e23 + 1e-12 + ''') + + pyparsing_common.fnumber.runTests(''' + # any int or real number, returned as float + 100 + -100 + +100 + 3.14159 + 6.02e23 + 1e-12 + ''') + + pyparsing_common.hex_integer.runTests(''' + # hex numbers + 100 + FF + ''') + + pyparsing_common.fraction.runTests(''' + # fractions + 1/2 + -3/4 + ''') + + pyparsing_common.mixed_integer.runTests(''' + # mixed fractions + 1 + 1/2 + -3/4 + 1-3/4 + ''') + + import uuid + pyparsing_common.uuid.setParseAction(tokenMap(uuid.UUID)) + pyparsing_common.uuid.runTests(''' + # uuid + 12345678-1234-5678-1234-567812345678 + ''') + prints:: + # any int or real number, returned as the appropriate type + 100 + [100] + + -100 + [-100] + + +100 + [100] + + 3.14159 + [3.14159] + + 6.02e23 + [6.02e+23] + + 1e-12 + [1e-12] + + # any int or real number, returned as float + 100 + [100.0] + + -100 + [-100.0] + + +100 + [100.0] + + 3.14159 + [3.14159] + + 6.02e23 + [6.02e+23] + + 1e-12 + [1e-12] + + # hex numbers + 100 + [256] + + FF + [255] + + # fractions + 1/2 + [0.5] + + -3/4 + [-0.75] + + # mixed fractions + 1 + [1] + + 1/2 + [0.5] + + -3/4 + [-0.75] + + 1-3/4 + [1.75] + + # uuid + 12345678-1234-5678-1234-567812345678 + [UUID('12345678-1234-5678-1234-567812345678')] + """ + + convertToInteger = tokenMap(int) + """ + Parse action for converting parsed integers to Python int + """ + + convertToFloat = tokenMap(float) + """ + Parse action for converting parsed numbers to Python float + """ + + integer = Word(nums).setName("integer").setParseAction(convertToInteger) + """expression that parses an unsigned integer, returns an int""" + + hex_integer = Word(hexnums).setName("hex integer").setParseAction(tokenMap(int,16)) + """expression that parses a hexadecimal integer, returns an int""" + + signed_integer = Regex(r'[+-]?\d+').setName("signed integer").setParseAction(convertToInteger) + """expression that parses an integer with optional leading sign, returns an int""" + + fraction = (signed_integer().setParseAction(convertToFloat) + '/' + signed_integer().setParseAction(convertToFloat)).setName("fraction") + """fractional expression of an integer divided by an integer, returns a float""" + fraction.addParseAction(lambda t: t[0]/t[-1]) + + mixed_integer = (fraction | signed_integer + Optional(Optional('-').suppress() + fraction)).setName("fraction or mixed integer-fraction") + """mixed integer of the form 'integer - fraction', with optional leading integer, returns float""" + mixed_integer.addParseAction(sum) + + real = Regex(r'[+-]?\d+\.\d*').setName("real number").setParseAction(convertToFloat) + """expression that parses a floating point number and returns a float""" + + sci_real = Regex(r'[+-]?\d+([eE][+-]?\d+|\.\d*([eE][+-]?\d+)?)').setName("real number with scientific notation").setParseAction(convertToFloat) + """expression that parses a floating point number with optional scientific notation and returns a float""" + + # streamlining this expression makes the docs nicer-looking + number = (sci_real | real | signed_integer).streamline() + """any numeric expression, returns the corresponding Python type""" + + fnumber = Regex(r'[+-]?\d+\.?\d*([eE][+-]?\d+)?').setName("fnumber").setParseAction(convertToFloat) + """any int or real number, returned as float""" + + identifier = Word(alphas+'_', alphanums+'_').setName("identifier") + """typical code identifier (leading alpha or '_', followed by 0 or more alphas, nums, or '_')""" + + ipv4_address = Regex(r'(25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})(\.(25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})){3}').setName("IPv4 address") + "IPv4 address (C{0.0.0.0 - 255.255.255.255})" + + _ipv6_part = Regex(r'[0-9a-fA-F]{1,4}').setName("hex_integer") + _full_ipv6_address = (_ipv6_part + (':' + _ipv6_part)*7).setName("full IPv6 address") + _short_ipv6_address = (Optional(_ipv6_part + (':' + _ipv6_part)*(0,6)) + "::" + Optional(_ipv6_part + (':' + _ipv6_part)*(0,6))).setName("short IPv6 address") + _short_ipv6_address.addCondition(lambda t: sum(1 for tt in t if pyparsing_common._ipv6_part.matches(tt)) < 8) + _mixed_ipv6_address = ("::ffff:" + ipv4_address).setName("mixed IPv6 address") + ipv6_address = Combine((_full_ipv6_address | _mixed_ipv6_address | _short_ipv6_address).setName("IPv6 address")).setName("IPv6 address") + "IPv6 address (long, short, or mixed form)" + + mac_address = Regex(r'[0-9a-fA-F]{2}([:.-])[0-9a-fA-F]{2}(?:\1[0-9a-fA-F]{2}){4}').setName("MAC address") + "MAC address xx:xx:xx:xx:xx (may also have '-' or '.' delimiters)" + + @staticmethod + def convertToDate(fmt="%Y-%m-%d"): + """ + Helper to create a parse action for converting parsed date string to Python datetime.date + + Params - + - fmt - format to be passed to datetime.strptime (default=C{"%Y-%m-%d"}) + + Example:: + date_expr = pyparsing_common.iso8601_date.copy() + date_expr.setParseAction(pyparsing_common.convertToDate()) + print(date_expr.parseString("1999-12-31")) + prints:: + [datetime.date(1999, 12, 31)] + """ + def cvt_fn(s,l,t): + try: + return datetime.strptime(t[0], fmt).date() + except ValueError as ve: + raise ParseException(s, l, str(ve)) + return cvt_fn + + @staticmethod + def convertToDatetime(fmt="%Y-%m-%dT%H:%M:%S.%f"): + """ + Helper to create a parse action for converting parsed datetime string to Python datetime.datetime + + Params - + - fmt - format to be passed to datetime.strptime (default=C{"%Y-%m-%dT%H:%M:%S.%f"}) + + Example:: + dt_expr = pyparsing_common.iso8601_datetime.copy() + dt_expr.setParseAction(pyparsing_common.convertToDatetime()) + print(dt_expr.parseString("1999-12-31T23:59:59.999")) + prints:: + [datetime.datetime(1999, 12, 31, 23, 59, 59, 999000)] + """ + def cvt_fn(s,l,t): + try: + return datetime.strptime(t[0], fmt) + except ValueError as ve: + raise ParseException(s, l, str(ve)) + return cvt_fn + + iso8601_date = Regex(r'(?P\d{4})(?:-(?P\d\d)(?:-(?P\d\d))?)?').setName("ISO8601 date") + "ISO8601 date (C{yyyy-mm-dd})" + + iso8601_datetime = Regex(r'(?P\d{4})-(?P\d\d)-(?P\d\d)[T ](?P\d\d):(?P\d\d)(:(?P\d\d(\.\d*)?)?)?(?PZ|[+-]\d\d:?\d\d)?').setName("ISO8601 datetime") + "ISO8601 datetime (C{yyyy-mm-ddThh:mm:ss.s(Z|+-00:00)}) - trailing seconds, milliseconds, and timezone optional; accepts separating C{'T'} or C{' '}" + + uuid = Regex(r'[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}').setName("UUID") + "UUID (C{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx})" + + _html_stripper = anyOpenTag.suppress() | anyCloseTag.suppress() + @staticmethod + def stripHTMLTags(s, l, tokens): + """ + Parse action to remove HTML tags from web page HTML source + + Example:: + # strip HTML links from normal text + text = 'More info at the
pyparsing wiki page' + td,td_end = makeHTMLTags("TD") + table_text = td + SkipTo(td_end).setParseAction(pyparsing_common.stripHTMLTags)("body") + td_end + + print(table_text.parseString(text).body) # -> 'More info at the pyparsing wiki page' + """ + return pyparsing_common._html_stripper.transformString(tokens[0]) + + _commasepitem = Combine(OneOrMore(~Literal(",") + ~LineEnd() + Word(printables, excludeChars=',') + + Optional( White(" \t") ) ) ).streamline().setName("commaItem") + comma_separated_list = delimitedList( Optional( quotedString.copy() | _commasepitem, default="") ).setName("comma separated list") + """Predefined expression of 1 or more printable words or quoted strings, separated by commas.""" + + upcaseTokens = staticmethod(tokenMap(lambda t: _ustr(t).upper())) + """Parse action to convert tokens to upper case.""" + + downcaseTokens = staticmethod(tokenMap(lambda t: _ustr(t).lower())) + """Parse action to convert tokens to lower case.""" + + +if __name__ == "__main__": + + selectToken = CaselessLiteral("select") + fromToken = CaselessLiteral("from") + + ident = Word(alphas, alphanums + "_$") + + columnName = delimitedList(ident, ".", combine=True).setParseAction(upcaseTokens) + columnNameList = Group(delimitedList(columnName)).setName("columns") + columnSpec = ('*' | columnNameList) + + tableName = delimitedList(ident, ".", combine=True).setParseAction(upcaseTokens) + tableNameList = Group(delimitedList(tableName)).setName("tables") + + simpleSQL = selectToken("command") + columnSpec("columns") + fromToken + tableNameList("tables") + + # demo runTests method, including embedded comments in test string + simpleSQL.runTests(""" + # '*' as column list and dotted table name + select * from SYS.XYZZY + + # caseless match on "SELECT", and casts back to "select" + SELECT * from XYZZY, ABC + + # list of column names, and mixed case SELECT keyword + Select AA,BB,CC from Sys.dual + + # multiple tables + Select A, B, C from Sys.dual, Table2 + + # invalid SELECT keyword - should fail + Xelect A, B, C from Sys.dual + + # incomplete command - should fail + Select + + # invalid column name - should fail + Select ^^^ frox Sys.dual + + """) + + pyparsing_common.number.runTests(""" + 100 + -100 + +100 + 3.14159 + 6.02e23 + 1e-12 + """) + + # any int or real number, returned as float + pyparsing_common.fnumber.runTests(""" + 100 + -100 + +100 + 3.14159 + 6.02e23 + 1e-12 + """) + + pyparsing_common.hex_integer.runTests(""" + 100 + FF + """) + + import uuid + pyparsing_common.uuid.setParseAction(tokenMap(uuid.UUID)) + pyparsing_common.uuid.runTests(""" + 12345678-1234-5678-1234-567812345678 + """) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/six.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/six.py new file mode 100644 index 0000000000..190c0239cd --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/_vendor/six.py @@ -0,0 +1,868 @@ +"""Utilities for writing code that runs on Python 2 and 3""" + +# Copyright (c) 2010-2015 Benjamin Peterson +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from __future__ import absolute_import + +import functools +import itertools +import operator +import sys +import types + +__author__ = "Benjamin Peterson " +__version__ = "1.10.0" + + +# Useful for very coarse version differentiation. +PY2 = sys.version_info[0] == 2 +PY3 = sys.version_info[0] == 3 +PY34 = sys.version_info[0:2] >= (3, 4) + +if PY3: + string_types = str, + integer_types = int, + class_types = type, + text_type = str + binary_type = bytes + + MAXSIZE = sys.maxsize +else: + string_types = basestring, + integer_types = (int, long) + class_types = (type, types.ClassType) + text_type = unicode + binary_type = str + + if sys.platform.startswith("java"): + # Jython always uses 32 bits. + MAXSIZE = int((1 << 31) - 1) + else: + # It's possible to have sizeof(long) != sizeof(Py_ssize_t). + class X(object): + + def __len__(self): + return 1 << 31 + try: + len(X()) + except OverflowError: + # 32-bit + MAXSIZE = int((1 << 31) - 1) + else: + # 64-bit + MAXSIZE = int((1 << 63) - 1) + del X + + +def _add_doc(func, doc): + """Add documentation to a function.""" + func.__doc__ = doc + + +def _import_module(name): + """Import module, returning the module after the last dot.""" + __import__(name) + return sys.modules[name] + + +class _LazyDescr(object): + + def __init__(self, name): + self.name = name + + def __get__(self, obj, tp): + result = self._resolve() + setattr(obj, self.name, result) # Invokes __set__. + try: + # This is a bit ugly, but it avoids running this again by + # removing this descriptor. + delattr(obj.__class__, self.name) + except AttributeError: + pass + return result + + +class MovedModule(_LazyDescr): + + def __init__(self, name, old, new=None): + super(MovedModule, self).__init__(name) + if PY3: + if new is None: + new = name + self.mod = new + else: + self.mod = old + + def _resolve(self): + return _import_module(self.mod) + + def __getattr__(self, attr): + _module = self._resolve() + value = getattr(_module, attr) + setattr(self, attr, value) + return value + + +class _LazyModule(types.ModuleType): + + def __init__(self, name): + super(_LazyModule, self).__init__(name) + self.__doc__ = self.__class__.__doc__ + + def __dir__(self): + attrs = ["__doc__", "__name__"] + attrs += [attr.name for attr in self._moved_attributes] + return attrs + + # Subclasses should override this + _moved_attributes = [] + + +class MovedAttribute(_LazyDescr): + + def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): + super(MovedAttribute, self).__init__(name) + if PY3: + if new_mod is None: + new_mod = name + self.mod = new_mod + if new_attr is None: + if old_attr is None: + new_attr = name + else: + new_attr = old_attr + self.attr = new_attr + else: + self.mod = old_mod + if old_attr is None: + old_attr = name + self.attr = old_attr + + def _resolve(self): + module = _import_module(self.mod) + return getattr(module, self.attr) + + +class _SixMetaPathImporter(object): + + """ + A meta path importer to import six.moves and its submodules. + + This class implements a PEP302 finder and loader. It should be compatible + with Python 2.5 and all existing versions of Python3 + """ + + def __init__(self, six_module_name): + self.name = six_module_name + self.known_modules = {} + + def _add_module(self, mod, *fullnames): + for fullname in fullnames: + self.known_modules[self.name + "." + fullname] = mod + + def _get_module(self, fullname): + return self.known_modules[self.name + "." + fullname] + + def find_module(self, fullname, path=None): + if fullname in self.known_modules: + return self + return None + + def __get_module(self, fullname): + try: + return self.known_modules[fullname] + except KeyError: + raise ImportError("This loader does not know module " + fullname) + + def load_module(self, fullname): + try: + # in case of a reload + return sys.modules[fullname] + except KeyError: + pass + mod = self.__get_module(fullname) + if isinstance(mod, MovedModule): + mod = mod._resolve() + else: + mod.__loader__ = self + sys.modules[fullname] = mod + return mod + + def is_package(self, fullname): + """ + Return true, if the named module is a package. + + We need this method to get correct spec objects with + Python 3.4 (see PEP451) + """ + return hasattr(self.__get_module(fullname), "__path__") + + def get_code(self, fullname): + """Return None + + Required, if is_package is implemented""" + self.__get_module(fullname) # eventually raises ImportError + return None + get_source = get_code # same as get_code + +_importer = _SixMetaPathImporter(__name__) + + +class _MovedItems(_LazyModule): + + """Lazy loading of moved objects""" + __path__ = [] # mark as package + + +_moved_attributes = [ + MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), + MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), + MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), + MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), + MovedAttribute("intern", "__builtin__", "sys"), + MovedAttribute("map", "itertools", "builtins", "imap", "map"), + MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"), + MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"), + MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), + MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"), + MovedAttribute("reduce", "__builtin__", "functools"), + MovedAttribute("shlex_quote", "pipes", "shlex", "quote"), + MovedAttribute("StringIO", "StringIO", "io"), + MovedAttribute("UserDict", "UserDict", "collections"), + MovedAttribute("UserList", "UserList", "collections"), + MovedAttribute("UserString", "UserString", "collections"), + MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), + MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), + MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), + MovedModule("builtins", "__builtin__"), + MovedModule("configparser", "ConfigParser"), + MovedModule("copyreg", "copy_reg"), + MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), + MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"), + MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), + MovedModule("http_cookies", "Cookie", "http.cookies"), + MovedModule("html_entities", "htmlentitydefs", "html.entities"), + MovedModule("html_parser", "HTMLParser", "html.parser"), + MovedModule("http_client", "httplib", "http.client"), + MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), + MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"), + MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), + MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), + MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), + MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), + MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), + MovedModule("cPickle", "cPickle", "pickle"), + MovedModule("queue", "Queue"), + MovedModule("reprlib", "repr"), + MovedModule("socketserver", "SocketServer"), + MovedModule("_thread", "thread", "_thread"), + MovedModule("tkinter", "Tkinter"), + MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"), + MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"), + MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"), + MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"), + MovedModule("tkinter_tix", "Tix", "tkinter.tix"), + MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"), + MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), + MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), + MovedModule("tkinter_colorchooser", "tkColorChooser", + "tkinter.colorchooser"), + MovedModule("tkinter_commondialog", "tkCommonDialog", + "tkinter.commondialog"), + MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), + MovedModule("tkinter_font", "tkFont", "tkinter.font"), + MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), + MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", + "tkinter.simpledialog"), + MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"), + MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"), + MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), + MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), + MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"), + MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"), +] +# Add windows specific modules. +if sys.platform == "win32": + _moved_attributes += [ + MovedModule("winreg", "_winreg"), + ] + +for attr in _moved_attributes: + setattr(_MovedItems, attr.name, attr) + if isinstance(attr, MovedModule): + _importer._add_module(attr, "moves." + attr.name) +del attr + +_MovedItems._moved_attributes = _moved_attributes + +moves = _MovedItems(__name__ + ".moves") +_importer._add_module(moves, "moves") + + +class Module_six_moves_urllib_parse(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_parse""" + + +_urllib_parse_moved_attributes = [ + MovedAttribute("ParseResult", "urlparse", "urllib.parse"), + MovedAttribute("SplitResult", "urlparse", "urllib.parse"), + MovedAttribute("parse_qs", "urlparse", "urllib.parse"), + MovedAttribute("parse_qsl", "urlparse", "urllib.parse"), + MovedAttribute("urldefrag", "urlparse", "urllib.parse"), + MovedAttribute("urljoin", "urlparse", "urllib.parse"), + MovedAttribute("urlparse", "urlparse", "urllib.parse"), + MovedAttribute("urlsplit", "urlparse", "urllib.parse"), + MovedAttribute("urlunparse", "urlparse", "urllib.parse"), + MovedAttribute("urlunsplit", "urlparse", "urllib.parse"), + MovedAttribute("quote", "urllib", "urllib.parse"), + MovedAttribute("quote_plus", "urllib", "urllib.parse"), + MovedAttribute("unquote", "urllib", "urllib.parse"), + MovedAttribute("unquote_plus", "urllib", "urllib.parse"), + MovedAttribute("urlencode", "urllib", "urllib.parse"), + MovedAttribute("splitquery", "urllib", "urllib.parse"), + MovedAttribute("splittag", "urllib", "urllib.parse"), + MovedAttribute("splituser", "urllib", "urllib.parse"), + MovedAttribute("uses_fragment", "urlparse", "urllib.parse"), + MovedAttribute("uses_netloc", "urlparse", "urllib.parse"), + MovedAttribute("uses_params", "urlparse", "urllib.parse"), + MovedAttribute("uses_query", "urlparse", "urllib.parse"), + MovedAttribute("uses_relative", "urlparse", "urllib.parse"), +] +for attr in _urllib_parse_moved_attributes: + setattr(Module_six_moves_urllib_parse, attr.name, attr) +del attr + +Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes + +_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), + "moves.urllib_parse", "moves.urllib.parse") + + +class Module_six_moves_urllib_error(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_error""" + + +_urllib_error_moved_attributes = [ + MovedAttribute("URLError", "urllib2", "urllib.error"), + MovedAttribute("HTTPError", "urllib2", "urllib.error"), + MovedAttribute("ContentTooShortError", "urllib", "urllib.error"), +] +for attr in _urllib_error_moved_attributes: + setattr(Module_six_moves_urllib_error, attr.name, attr) +del attr + +Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes + +_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), + "moves.urllib_error", "moves.urllib.error") + + +class Module_six_moves_urllib_request(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_request""" + + +_urllib_request_moved_attributes = [ + MovedAttribute("urlopen", "urllib2", "urllib.request"), + MovedAttribute("install_opener", "urllib2", "urllib.request"), + MovedAttribute("build_opener", "urllib2", "urllib.request"), + MovedAttribute("pathname2url", "urllib", "urllib.request"), + MovedAttribute("url2pathname", "urllib", "urllib.request"), + MovedAttribute("getproxies", "urllib", "urllib.request"), + MovedAttribute("Request", "urllib2", "urllib.request"), + MovedAttribute("OpenerDirector", "urllib2", "urllib.request"), + MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"), + MovedAttribute("ProxyHandler", "urllib2", "urllib.request"), + MovedAttribute("BaseHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"), + MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"), + MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"), + MovedAttribute("FileHandler", "urllib2", "urllib.request"), + MovedAttribute("FTPHandler", "urllib2", "urllib.request"), + MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"), + MovedAttribute("UnknownHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"), + MovedAttribute("urlretrieve", "urllib", "urllib.request"), + MovedAttribute("urlcleanup", "urllib", "urllib.request"), + MovedAttribute("URLopener", "urllib", "urllib.request"), + MovedAttribute("FancyURLopener", "urllib", "urllib.request"), + MovedAttribute("proxy_bypass", "urllib", "urllib.request"), +] +for attr in _urllib_request_moved_attributes: + setattr(Module_six_moves_urllib_request, attr.name, attr) +del attr + +Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes + +_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), + "moves.urllib_request", "moves.urllib.request") + + +class Module_six_moves_urllib_response(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_response""" + + +_urllib_response_moved_attributes = [ + MovedAttribute("addbase", "urllib", "urllib.response"), + MovedAttribute("addclosehook", "urllib", "urllib.response"), + MovedAttribute("addinfo", "urllib", "urllib.response"), + MovedAttribute("addinfourl", "urllib", "urllib.response"), +] +for attr in _urllib_response_moved_attributes: + setattr(Module_six_moves_urllib_response, attr.name, attr) +del attr + +Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes + +_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), + "moves.urllib_response", "moves.urllib.response") + + +class Module_six_moves_urllib_robotparser(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_robotparser""" + + +_urllib_robotparser_moved_attributes = [ + MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"), +] +for attr in _urllib_robotparser_moved_attributes: + setattr(Module_six_moves_urllib_robotparser, attr.name, attr) +del attr + +Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes + +_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), + "moves.urllib_robotparser", "moves.urllib.robotparser") + + +class Module_six_moves_urllib(types.ModuleType): + + """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" + __path__ = [] # mark as package + parse = _importer._get_module("moves.urllib_parse") + error = _importer._get_module("moves.urllib_error") + request = _importer._get_module("moves.urllib_request") + response = _importer._get_module("moves.urllib_response") + robotparser = _importer._get_module("moves.urllib_robotparser") + + def __dir__(self): + return ['parse', 'error', 'request', 'response', 'robotparser'] + +_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"), + "moves.urllib") + + +def add_move(move): + """Add an item to six.moves.""" + setattr(_MovedItems, move.name, move) + + +def remove_move(name): + """Remove item from six.moves.""" + try: + delattr(_MovedItems, name) + except AttributeError: + try: + del moves.__dict__[name] + except KeyError: + raise AttributeError("no such move, %r" % (name,)) + + +if PY3: + _meth_func = "__func__" + _meth_self = "__self__" + + _func_closure = "__closure__" + _func_code = "__code__" + _func_defaults = "__defaults__" + _func_globals = "__globals__" +else: + _meth_func = "im_func" + _meth_self = "im_self" + + _func_closure = "func_closure" + _func_code = "func_code" + _func_defaults = "func_defaults" + _func_globals = "func_globals" + + +try: + advance_iterator = next +except NameError: + def advance_iterator(it): + return it.next() +next = advance_iterator + + +try: + callable = callable +except NameError: + def callable(obj): + return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) + + +if PY3: + def get_unbound_function(unbound): + return unbound + + create_bound_method = types.MethodType + + def create_unbound_method(func, cls): + return func + + Iterator = object +else: + def get_unbound_function(unbound): + return unbound.im_func + + def create_bound_method(func, obj): + return types.MethodType(func, obj, obj.__class__) + + def create_unbound_method(func, cls): + return types.MethodType(func, None, cls) + + class Iterator(object): + + def next(self): + return type(self).__next__(self) + + callable = callable +_add_doc(get_unbound_function, + """Get the function out of a possibly unbound function""") + + +get_method_function = operator.attrgetter(_meth_func) +get_method_self = operator.attrgetter(_meth_self) +get_function_closure = operator.attrgetter(_func_closure) +get_function_code = operator.attrgetter(_func_code) +get_function_defaults = operator.attrgetter(_func_defaults) +get_function_globals = operator.attrgetter(_func_globals) + + +if PY3: + def iterkeys(d, **kw): + return iter(d.keys(**kw)) + + def itervalues(d, **kw): + return iter(d.values(**kw)) + + def iteritems(d, **kw): + return iter(d.items(**kw)) + + def iterlists(d, **kw): + return iter(d.lists(**kw)) + + viewkeys = operator.methodcaller("keys") + + viewvalues = operator.methodcaller("values") + + viewitems = operator.methodcaller("items") +else: + def iterkeys(d, **kw): + return d.iterkeys(**kw) + + def itervalues(d, **kw): + return d.itervalues(**kw) + + def iteritems(d, **kw): + return d.iteritems(**kw) + + def iterlists(d, **kw): + return d.iterlists(**kw) + + viewkeys = operator.methodcaller("viewkeys") + + viewvalues = operator.methodcaller("viewvalues") + + viewitems = operator.methodcaller("viewitems") + +_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.") +_add_doc(itervalues, "Return an iterator over the values of a dictionary.") +_add_doc(iteritems, + "Return an iterator over the (key, value) pairs of a dictionary.") +_add_doc(iterlists, + "Return an iterator over the (key, [values]) pairs of a dictionary.") + + +if PY3: + def b(s): + return s.encode("latin-1") + + def u(s): + return s + unichr = chr + import struct + int2byte = struct.Struct(">B").pack + del struct + byte2int = operator.itemgetter(0) + indexbytes = operator.getitem + iterbytes = iter + import io + StringIO = io.StringIO + BytesIO = io.BytesIO + _assertCountEqual = "assertCountEqual" + if sys.version_info[1] <= 1: + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" + else: + _assertRaisesRegex = "assertRaisesRegex" + _assertRegex = "assertRegex" +else: + def b(s): + return s + # Workaround for standalone backslash + + def u(s): + return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape") + unichr = unichr + int2byte = chr + + def byte2int(bs): + return ord(bs[0]) + + def indexbytes(buf, i): + return ord(buf[i]) + iterbytes = functools.partial(itertools.imap, ord) + import StringIO + StringIO = BytesIO = StringIO.StringIO + _assertCountEqual = "assertItemsEqual" + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" +_add_doc(b, """Byte literal""") +_add_doc(u, """Text literal""") + + +def assertCountEqual(self, *args, **kwargs): + return getattr(self, _assertCountEqual)(*args, **kwargs) + + +def assertRaisesRegex(self, *args, **kwargs): + return getattr(self, _assertRaisesRegex)(*args, **kwargs) + + +def assertRegex(self, *args, **kwargs): + return getattr(self, _assertRegex)(*args, **kwargs) + + +if PY3: + exec_ = getattr(moves.builtins, "exec") + + def reraise(tp, value, tb=None): + if value is None: + value = tp() + if value.__traceback__ is not tb: + raise value.with_traceback(tb) + raise value + +else: + def exec_(_code_, _globs_=None, _locs_=None): + """Execute code in a namespace.""" + if _globs_ is None: + frame = sys._getframe(1) + _globs_ = frame.f_globals + if _locs_ is None: + _locs_ = frame.f_locals + del frame + elif _locs_ is None: + _locs_ = _globs_ + exec("""exec _code_ in _globs_, _locs_""") + + exec_("""def reraise(tp, value, tb=None): + raise tp, value, tb +""") + + +if sys.version_info[:2] == (3, 2): + exec_("""def raise_from(value, from_value): + if from_value is None: + raise value + raise value from from_value +""") +elif sys.version_info[:2] > (3, 2): + exec_("""def raise_from(value, from_value): + raise value from from_value +""") +else: + def raise_from(value, from_value): + raise value + + +print_ = getattr(moves.builtins, "print", None) +if print_ is None: + def print_(*args, **kwargs): + """The new-style print function for Python 2.4 and 2.5.""" + fp = kwargs.pop("file", sys.stdout) + if fp is None: + return + + def write(data): + if not isinstance(data, basestring): + data = str(data) + # If the file has an encoding, encode unicode with it. + if (isinstance(fp, file) and + isinstance(data, unicode) and + fp.encoding is not None): + errors = getattr(fp, "errors", None) + if errors is None: + errors = "strict" + data = data.encode(fp.encoding, errors) + fp.write(data) + want_unicode = False + sep = kwargs.pop("sep", None) + if sep is not None: + if isinstance(sep, unicode): + want_unicode = True + elif not isinstance(sep, str): + raise TypeError("sep must be None or a string") + end = kwargs.pop("end", None) + if end is not None: + if isinstance(end, unicode): + want_unicode = True + elif not isinstance(end, str): + raise TypeError("end must be None or a string") + if kwargs: + raise TypeError("invalid keyword arguments to print()") + if not want_unicode: + for arg in args: + if isinstance(arg, unicode): + want_unicode = True + break + if want_unicode: + newline = unicode("\n") + space = unicode(" ") + else: + newline = "\n" + space = " " + if sep is None: + sep = space + if end is None: + end = newline + for i, arg in enumerate(args): + if i: + write(sep) + write(arg) + write(end) +if sys.version_info[:2] < (3, 3): + _print = print_ + + def print_(*args, **kwargs): + fp = kwargs.get("file", sys.stdout) + flush = kwargs.pop("flush", False) + _print(*args, **kwargs) + if flush and fp is not None: + fp.flush() + +_add_doc(reraise, """Reraise an exception.""") + +if sys.version_info[0:2] < (3, 4): + def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, + updated=functools.WRAPPER_UPDATES): + def wrapper(f): + f = functools.wraps(wrapped, assigned, updated)(f) + f.__wrapped__ = wrapped + return f + return wrapper +else: + wraps = functools.wraps + + +def with_metaclass(meta, *bases): + """Create a base class with a metaclass.""" + # This requires a bit of explanation: the basic idea is to make a dummy + # metaclass for one level of class instantiation that replaces itself with + # the actual metaclass. + class metaclass(meta): + + def __new__(cls, name, this_bases, d): + return meta(name, bases, d) + return type.__new__(metaclass, 'temporary_class', (), {}) + + +def add_metaclass(metaclass): + """Class decorator for creating a class with a metaclass.""" + def wrapper(cls): + orig_vars = cls.__dict__.copy() + slots = orig_vars.get('__slots__') + if slots is not None: + if isinstance(slots, str): + slots = [slots] + for slots_var in slots: + orig_vars.pop(slots_var) + orig_vars.pop('__dict__', None) + orig_vars.pop('__weakref__', None) + return metaclass(cls.__name__, cls.__bases__, orig_vars) + return wrapper + + +def python_2_unicode_compatible(klass): + """ + A decorator that defines __unicode__ and __str__ methods under Python 2. + Under Python 3 it does nothing. + + To support Python 2 and 3 with a single code base, define a __str__ method + returning text and apply this decorator to the class. + """ + if PY2: + if '__str__' not in klass.__dict__: + raise ValueError("@python_2_unicode_compatible cannot be applied " + "to %s because it doesn't define __str__()." % + klass.__name__) + klass.__unicode__ = klass.__str__ + klass.__str__ = lambda self: self.__unicode__().encode('utf-8') + return klass + + +# Complete the moves implementation. +# This code is at the end of this module to speed up module loading. +# Turn this module into a package. +__path__ = [] # required for PEP 302 and PEP 451 +__package__ = __name__ # see PEP 366 @ReservedAssignment +if globals().get("__spec__") is not None: + __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable +# Remove other six meta path importers, since they cause problems. This can +# happen if six is removed from sys.modules and then reloaded. (Setuptools does +# this for some reason.) +if sys.meta_path: + for i, importer in enumerate(sys.meta_path): + # Here's some real nastiness: Another "instance" of the six module might + # be floating around. Therefore, we can't use isinstance() to check for + # the six meta path importer, since the other six instance will have + # inserted an importer with different class. + if (type(importer).__name__ == "_SixMetaPathImporter" and + importer.name == __name__): + del sys.meta_path[i] + break + del i, importer +# Finally, add the importer to the meta path import hook. +sys.meta_path.append(_importer) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/archive_util.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/archive_util.py new file mode 100644 index 0000000000..81436044d9 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/archive_util.py @@ -0,0 +1,173 @@ +"""Utilities for extracting common archive formats""" + +import zipfile +import tarfile +import os +import shutil +import posixpath +import contextlib +from distutils.errors import DistutilsError + +from pkg_resources import ensure_directory + +__all__ = [ + "unpack_archive", "unpack_zipfile", "unpack_tarfile", "default_filter", + "UnrecognizedFormat", "extraction_drivers", "unpack_directory", +] + + +class UnrecognizedFormat(DistutilsError): + """Couldn't recognize the archive type""" + + +def default_filter(src, dst): + """The default progress/filter callback; returns True for all files""" + return dst + + +def unpack_archive(filename, extract_dir, progress_filter=default_filter, + drivers=None): + """Unpack `filename` to `extract_dir`, or raise ``UnrecognizedFormat`` + + `progress_filter` is a function taking two arguments: a source path + internal to the archive ('/'-separated), and a filesystem path where it + will be extracted. The callback must return the desired extract path + (which may be the same as the one passed in), or else ``None`` to skip + that file or directory. The callback can thus be used to report on the + progress of the extraction, as well as to filter the items extracted or + alter their extraction paths. + + `drivers`, if supplied, must be a non-empty sequence of functions with the + same signature as this function (minus the `drivers` argument), that raise + ``UnrecognizedFormat`` if they do not support extracting the designated + archive type. The `drivers` are tried in sequence until one is found that + does not raise an error, or until all are exhausted (in which case + ``UnrecognizedFormat`` is raised). If you do not supply a sequence of + drivers, the module's ``extraction_drivers`` constant will be used, which + means that ``unpack_zipfile`` and ``unpack_tarfile`` will be tried, in that + order. + """ + for driver in drivers or extraction_drivers: + try: + driver(filename, extract_dir, progress_filter) + except UnrecognizedFormat: + continue + else: + return + else: + raise UnrecognizedFormat( + "Not a recognized archive type: %s" % filename + ) + + +def unpack_directory(filename, extract_dir, progress_filter=default_filter): + """"Unpack" a directory, using the same interface as for archives + + Raises ``UnrecognizedFormat`` if `filename` is not a directory + """ + if not os.path.isdir(filename): + raise UnrecognizedFormat("%s is not a directory" % filename) + + paths = { + filename: ('', extract_dir), + } + for base, dirs, files in os.walk(filename): + src, dst = paths[base] + for d in dirs: + paths[os.path.join(base, d)] = src + d + '/', os.path.join(dst, d) + for f in files: + target = os.path.join(dst, f) + target = progress_filter(src + f, target) + if not target: + # skip non-files + continue + ensure_directory(target) + f = os.path.join(base, f) + shutil.copyfile(f, target) + shutil.copystat(f, target) + + +def unpack_zipfile(filename, extract_dir, progress_filter=default_filter): + """Unpack zip `filename` to `extract_dir` + + Raises ``UnrecognizedFormat`` if `filename` is not a zipfile (as determined + by ``zipfile.is_zipfile()``). See ``unpack_archive()`` for an explanation + of the `progress_filter` argument. + """ + + if not zipfile.is_zipfile(filename): + raise UnrecognizedFormat("%s is not a zip file" % (filename,)) + + with zipfile.ZipFile(filename) as z: + for info in z.infolist(): + name = info.filename + + # don't extract absolute paths or ones with .. in them + if name.startswith('/') or '..' in name.split('/'): + continue + + target = os.path.join(extract_dir, *name.split('/')) + target = progress_filter(name, target) + if not target: + continue + if name.endswith('/'): + # directory + ensure_directory(target) + else: + # file + ensure_directory(target) + data = z.read(info.filename) + with open(target, 'wb') as f: + f.write(data) + unix_attributes = info.external_attr >> 16 + if unix_attributes: + os.chmod(target, unix_attributes) + + +def unpack_tarfile(filename, extract_dir, progress_filter=default_filter): + """Unpack tar/tar.gz/tar.bz2 `filename` to `extract_dir` + + Raises ``UnrecognizedFormat`` if `filename` is not a tarfile (as determined + by ``tarfile.open()``). See ``unpack_archive()`` for an explanation + of the `progress_filter` argument. + """ + try: + tarobj = tarfile.open(filename) + except tarfile.TarError: + raise UnrecognizedFormat( + "%s is not a compressed or uncompressed tar file" % (filename,) + ) + with contextlib.closing(tarobj): + # don't do any chowning! + tarobj.chown = lambda *args: None + for member in tarobj: + name = member.name + # don't extract absolute paths or ones with .. in them + if not name.startswith('/') and '..' not in name.split('/'): + prelim_dst = os.path.join(extract_dir, *name.split('/')) + + # resolve any links and to extract the link targets as normal + # files + while member is not None and (member.islnk() or member.issym()): + linkpath = member.linkname + if member.issym(): + base = posixpath.dirname(member.name) + linkpath = posixpath.join(base, linkpath) + linkpath = posixpath.normpath(linkpath) + member = tarobj._getmember(linkpath) + + if member is not None and (member.isfile() or member.isdir()): + final_dst = progress_filter(name, prelim_dst) + if final_dst: + if final_dst.endswith(os.sep): + final_dst = final_dst[:-1] + try: + # XXX Ugh + tarobj._extract_member(member, final_dst) + except tarfile.ExtractError: + # chown/chmod/mkfifo/mknode/makedev failed + pass + return True + + +extraction_drivers = unpack_directory, unpack_zipfile, unpack_tarfile diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/__init__.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/__init__.py new file mode 100644 index 0000000000..743f5588fa --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/__init__.py @@ -0,0 +1,17 @@ +__all__ = [ + 'alias', 'bdist_egg', 'bdist_rpm', 'build_ext', 'build_py', 'develop', + 'easy_install', 'egg_info', 'install', 'install_lib', 'rotate', 'saveopts', + 'sdist', 'setopt', 'test', 'install_egg_info', 'install_scripts', + 'bdist_wininst', 'upload_docs', 'build_clib', 'dist_info', +] + +from distutils.command.bdist import bdist +import sys + +from setuptools.command import install_scripts + +if 'egg' not in bdist.format_commands: + bdist.format_command['egg'] = ('bdist_egg', "Python .egg file") + bdist.format_commands.append('egg') + +del bdist, sys diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/alias.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/alias.py new file mode 100644 index 0000000000..4532b1cc0d --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/alias.py @@ -0,0 +1,80 @@ +from distutils.errors import DistutilsOptionError + +from setuptools.extern.six.moves import map + +from setuptools.command.setopt import edit_config, option_base, config_file + + +def shquote(arg): + """Quote an argument for later parsing by shlex.split()""" + for c in '"', "'", "\\", "#": + if c in arg: + return repr(arg) + if arg.split() != [arg]: + return repr(arg) + return arg + + +class alias(option_base): + """Define a shortcut that invokes one or more commands""" + + description = "define a shortcut to invoke one or more commands" + command_consumes_arguments = True + + user_options = [ + ('remove', 'r', 'remove (unset) the alias'), + ] + option_base.user_options + + boolean_options = option_base.boolean_options + ['remove'] + + def initialize_options(self): + option_base.initialize_options(self) + self.args = None + self.remove = None + + def finalize_options(self): + option_base.finalize_options(self) + if self.remove and len(self.args) != 1: + raise DistutilsOptionError( + "Must specify exactly one argument (the alias name) when " + "using --remove" + ) + + def run(self): + aliases = self.distribution.get_option_dict('aliases') + + if not self.args: + print("Command Aliases") + print("---------------") + for alias in aliases: + print("setup.py alias", format_alias(alias, aliases)) + return + + elif len(self.args) == 1: + alias, = self.args + if self.remove: + command = None + elif alias in aliases: + print("setup.py alias", format_alias(alias, aliases)) + return + else: + print("No alias definition found for %r" % alias) + return + else: + alias = self.args[0] + command = ' '.join(map(shquote, self.args[1:])) + + edit_config(self.filename, {'aliases': {alias: command}}, self.dry_run) + + +def format_alias(name, aliases): + source, command = aliases[name] + if source == config_file('global'): + source = '--global-config ' + elif source == config_file('user'): + source = '--user-config ' + elif source == config_file('local'): + source = '' + else: + source = '--filename=%r' % source + return source + name + ' ' + command diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/bdist_egg.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/bdist_egg.py new file mode 100644 index 0000000000..98470f1715 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/bdist_egg.py @@ -0,0 +1,502 @@ +"""setuptools.command.bdist_egg + +Build .egg distributions""" + +from distutils.errors import DistutilsSetupError +from distutils.dir_util import remove_tree, mkpath +from distutils import log +from types import CodeType +import sys +import os +import re +import textwrap +import marshal + +from setuptools.extern import six + +from pkg_resources import get_build_platform, Distribution, ensure_directory +from pkg_resources import EntryPoint +from setuptools.extension import Library +from setuptools import Command + +try: + # Python 2.7 or >=3.2 + from sysconfig import get_path, get_python_version + + def _get_purelib(): + return get_path("purelib") +except ImportError: + from distutils.sysconfig import get_python_lib, get_python_version + + def _get_purelib(): + return get_python_lib(False) + + +def strip_module(filename): + if '.' in filename: + filename = os.path.splitext(filename)[0] + if filename.endswith('module'): + filename = filename[:-6] + return filename + + +def sorted_walk(dir): + """Do os.walk in a reproducible way, + independent of indeterministic filesystem readdir order + """ + for base, dirs, files in os.walk(dir): + dirs.sort() + files.sort() + yield base, dirs, files + + +def write_stub(resource, pyfile): + _stub_template = textwrap.dedent(""" + def __bootstrap__(): + global __bootstrap__, __loader__, __file__ + import sys, pkg_resources, imp + __file__ = pkg_resources.resource_filename(__name__, %r) + __loader__ = None; del __bootstrap__, __loader__ + imp.load_dynamic(__name__,__file__) + __bootstrap__() + """).lstrip() + with open(pyfile, 'w') as f: + f.write(_stub_template % resource) + + +class bdist_egg(Command): + description = "create an \"egg\" distribution" + + user_options = [ + ('bdist-dir=', 'b', + "temporary directory for creating the distribution"), + ('plat-name=', 'p', "platform name to embed in generated filenames " + "(default: %s)" % get_build_platform()), + ('exclude-source-files', None, + "remove all .py files from the generated egg"), + ('keep-temp', 'k', + "keep the pseudo-installation tree around after " + + "creating the distribution archive"), + ('dist-dir=', 'd', + "directory to put final built distributions in"), + ('skip-build', None, + "skip rebuilding everything (for testing/debugging)"), + ] + + boolean_options = [ + 'keep-temp', 'skip-build', 'exclude-source-files' + ] + + def initialize_options(self): + self.bdist_dir = None + self.plat_name = None + self.keep_temp = 0 + self.dist_dir = None + self.skip_build = 0 + self.egg_output = None + self.exclude_source_files = None + + def finalize_options(self): + ei_cmd = self.ei_cmd = self.get_finalized_command("egg_info") + self.egg_info = ei_cmd.egg_info + + if self.bdist_dir is None: + bdist_base = self.get_finalized_command('bdist').bdist_base + self.bdist_dir = os.path.join(bdist_base, 'egg') + + if self.plat_name is None: + self.plat_name = get_build_platform() + + self.set_undefined_options('bdist', ('dist_dir', 'dist_dir')) + + if self.egg_output is None: + + # Compute filename of the output egg + basename = Distribution( + None, None, ei_cmd.egg_name, ei_cmd.egg_version, + get_python_version(), + self.distribution.has_ext_modules() and self.plat_name + ).egg_name() + + self.egg_output = os.path.join(self.dist_dir, basename + '.egg') + + def do_install_data(self): + # Hack for packages that install data to install's --install-lib + self.get_finalized_command('install').install_lib = self.bdist_dir + + site_packages = os.path.normcase(os.path.realpath(_get_purelib())) + old, self.distribution.data_files = self.distribution.data_files, [] + + for item in old: + if isinstance(item, tuple) and len(item) == 2: + if os.path.isabs(item[0]): + realpath = os.path.realpath(item[0]) + normalized = os.path.normcase(realpath) + if normalized == site_packages or normalized.startswith( + site_packages + os.sep + ): + item = realpath[len(site_packages) + 1:], item[1] + # XXX else: raise ??? + self.distribution.data_files.append(item) + + try: + log.info("installing package data to %s", self.bdist_dir) + self.call_command('install_data', force=0, root=None) + finally: + self.distribution.data_files = old + + def get_outputs(self): + return [self.egg_output] + + def call_command(self, cmdname, **kw): + """Invoke reinitialized command `cmdname` with keyword args""" + for dirname in INSTALL_DIRECTORY_ATTRS: + kw.setdefault(dirname, self.bdist_dir) + kw.setdefault('skip_build', self.skip_build) + kw.setdefault('dry_run', self.dry_run) + cmd = self.reinitialize_command(cmdname, **kw) + self.run_command(cmdname) + return cmd + + def run(self): + # Generate metadata first + self.run_command("egg_info") + # We run install_lib before install_data, because some data hacks + # pull their data path from the install_lib command. + log.info("installing library code to %s", self.bdist_dir) + instcmd = self.get_finalized_command('install') + old_root = instcmd.root + instcmd.root = None + if self.distribution.has_c_libraries() and not self.skip_build: + self.run_command('build_clib') + cmd = self.call_command('install_lib', warn_dir=0) + instcmd.root = old_root + + all_outputs, ext_outputs = self.get_ext_outputs() + self.stubs = [] + to_compile = [] + for (p, ext_name) in enumerate(ext_outputs): + filename, ext = os.path.splitext(ext_name) + pyfile = os.path.join(self.bdist_dir, strip_module(filename) + + '.py') + self.stubs.append(pyfile) + log.info("creating stub loader for %s", ext_name) + if not self.dry_run: + write_stub(os.path.basename(ext_name), pyfile) + to_compile.append(pyfile) + ext_outputs[p] = ext_name.replace(os.sep, '/') + + if to_compile: + cmd.byte_compile(to_compile) + if self.distribution.data_files: + self.do_install_data() + + # Make the EGG-INFO directory + archive_root = self.bdist_dir + egg_info = os.path.join(archive_root, 'EGG-INFO') + self.mkpath(egg_info) + if self.distribution.scripts: + script_dir = os.path.join(egg_info, 'scripts') + log.info("installing scripts to %s", script_dir) + self.call_command('install_scripts', install_dir=script_dir, + no_ep=1) + + self.copy_metadata_to(egg_info) + native_libs = os.path.join(egg_info, "native_libs.txt") + if all_outputs: + log.info("writing %s", native_libs) + if not self.dry_run: + ensure_directory(native_libs) + libs_file = open(native_libs, 'wt') + libs_file.write('\n'.join(all_outputs)) + libs_file.write('\n') + libs_file.close() + elif os.path.isfile(native_libs): + log.info("removing %s", native_libs) + if not self.dry_run: + os.unlink(native_libs) + + write_safety_flag( + os.path.join(archive_root, 'EGG-INFO'), self.zip_safe() + ) + + if os.path.exists(os.path.join(self.egg_info, 'depends.txt')): + log.warn( + "WARNING: 'depends.txt' will not be used by setuptools 0.6!\n" + "Use the install_requires/extras_require setup() args instead." + ) + + if self.exclude_source_files: + self.zap_pyfiles() + + # Make the archive + make_zipfile(self.egg_output, archive_root, verbose=self.verbose, + dry_run=self.dry_run, mode=self.gen_header()) + if not self.keep_temp: + remove_tree(self.bdist_dir, dry_run=self.dry_run) + + # Add to 'Distribution.dist_files' so that the "upload" command works + getattr(self.distribution, 'dist_files', []).append( + ('bdist_egg', get_python_version(), self.egg_output)) + + def zap_pyfiles(self): + log.info("Removing .py files from temporary directory") + for base, dirs, files in walk_egg(self.bdist_dir): + for name in files: + path = os.path.join(base, name) + + if name.endswith('.py'): + log.debug("Deleting %s", path) + os.unlink(path) + + if base.endswith('__pycache__'): + path_old = path + + pattern = r'(?P.+)\.(?P[^.]+)\.pyc' + m = re.match(pattern, name) + path_new = os.path.join( + base, os.pardir, m.group('name') + '.pyc') + log.info( + "Renaming file from [%s] to [%s]" + % (path_old, path_new)) + try: + os.remove(path_new) + except OSError: + pass + os.rename(path_old, path_new) + + def zip_safe(self): + safe = getattr(self.distribution, 'zip_safe', None) + if safe is not None: + return safe + log.warn("zip_safe flag not set; analyzing archive contents...") + return analyze_egg(self.bdist_dir, self.stubs) + + def gen_header(self): + epm = EntryPoint.parse_map(self.distribution.entry_points or '') + ep = epm.get('setuptools.installation', {}).get('eggsecutable') + if ep is None: + return 'w' # not an eggsecutable, do it the usual way. + + if not ep.attrs or ep.extras: + raise DistutilsSetupError( + "eggsecutable entry point (%r) cannot have 'extras' " + "or refer to a module" % (ep,) + ) + + pyver = '{}.{}'.format(*sys.version_info) + pkg = ep.module_name + full = '.'.join(ep.attrs) + base = ep.attrs[0] + basename = os.path.basename(self.egg_output) + + header = ( + "#!/bin/sh\n" + 'if [ `basename $0` = "%(basename)s" ]\n' + 'then exec python%(pyver)s -c "' + "import sys, os; sys.path.insert(0, os.path.abspath('$0')); " + "from %(pkg)s import %(base)s; sys.exit(%(full)s())" + '" "$@"\n' + 'else\n' + ' echo $0 is not the correct name for this egg file.\n' + ' echo Please rename it back to %(basename)s and try again.\n' + ' exec false\n' + 'fi\n' + ) % locals() + + if not self.dry_run: + mkpath(os.path.dirname(self.egg_output), dry_run=self.dry_run) + f = open(self.egg_output, 'w') + f.write(header) + f.close() + return 'a' + + def copy_metadata_to(self, target_dir): + "Copy metadata (egg info) to the target_dir" + # normalize the path (so that a forward-slash in egg_info will + # match using startswith below) + norm_egg_info = os.path.normpath(self.egg_info) + prefix = os.path.join(norm_egg_info, '') + for path in self.ei_cmd.filelist.files: + if path.startswith(prefix): + target = os.path.join(target_dir, path[len(prefix):]) + ensure_directory(target) + self.copy_file(path, target) + + def get_ext_outputs(self): + """Get a list of relative paths to C extensions in the output distro""" + + all_outputs = [] + ext_outputs = [] + + paths = {self.bdist_dir: ''} + for base, dirs, files in sorted_walk(self.bdist_dir): + for filename in files: + if os.path.splitext(filename)[1].lower() in NATIVE_EXTENSIONS: + all_outputs.append(paths[base] + filename) + for filename in dirs: + paths[os.path.join(base, filename)] = (paths[base] + + filename + '/') + + if self.distribution.has_ext_modules(): + build_cmd = self.get_finalized_command('build_ext') + for ext in build_cmd.extensions: + if isinstance(ext, Library): + continue + fullname = build_cmd.get_ext_fullname(ext.name) + filename = build_cmd.get_ext_filename(fullname) + if not os.path.basename(filename).startswith('dl-'): + if os.path.exists(os.path.join(self.bdist_dir, filename)): + ext_outputs.append(filename) + + return all_outputs, ext_outputs + + +NATIVE_EXTENSIONS = dict.fromkeys('.dll .so .dylib .pyd'.split()) + + +def walk_egg(egg_dir): + """Walk an unpacked egg's contents, skipping the metadata directory""" + walker = sorted_walk(egg_dir) + base, dirs, files = next(walker) + if 'EGG-INFO' in dirs: + dirs.remove('EGG-INFO') + yield base, dirs, files + for bdf in walker: + yield bdf + + +def analyze_egg(egg_dir, stubs): + # check for existing flag in EGG-INFO + for flag, fn in safety_flags.items(): + if os.path.exists(os.path.join(egg_dir, 'EGG-INFO', fn)): + return flag + if not can_scan(): + return False + safe = True + for base, dirs, files in walk_egg(egg_dir): + for name in files: + if name.endswith('.py') or name.endswith('.pyw'): + continue + elif name.endswith('.pyc') or name.endswith('.pyo'): + # always scan, even if we already know we're not safe + safe = scan_module(egg_dir, base, name, stubs) and safe + return safe + + +def write_safety_flag(egg_dir, safe): + # Write or remove zip safety flag file(s) + for flag, fn in safety_flags.items(): + fn = os.path.join(egg_dir, fn) + if os.path.exists(fn): + if safe is None or bool(safe) != flag: + os.unlink(fn) + elif safe is not None and bool(safe) == flag: + f = open(fn, 'wt') + f.write('\n') + f.close() + + +safety_flags = { + True: 'zip-safe', + False: 'not-zip-safe', +} + + +def scan_module(egg_dir, base, name, stubs): + """Check whether module possibly uses unsafe-for-zipfile stuff""" + + filename = os.path.join(base, name) + if filename[:-1] in stubs: + return True # Extension module + pkg = base[len(egg_dir) + 1:].replace(os.sep, '.') + module = pkg + (pkg and '.' or '') + os.path.splitext(name)[0] + if six.PY2: + skip = 8 # skip magic & date + elif sys.version_info < (3, 7): + skip = 12 # skip magic & date & file size + else: + skip = 16 # skip magic & reserved? & date & file size + f = open(filename, 'rb') + f.read(skip) + code = marshal.load(f) + f.close() + safe = True + symbols = dict.fromkeys(iter_symbols(code)) + for bad in ['__file__', '__path__']: + if bad in symbols: + log.warn("%s: module references %s", module, bad) + safe = False + if 'inspect' in symbols: + for bad in [ + 'getsource', 'getabsfile', 'getsourcefile', 'getfile' + 'getsourcelines', 'findsource', 'getcomments', 'getframeinfo', + 'getinnerframes', 'getouterframes', 'stack', 'trace' + ]: + if bad in symbols: + log.warn("%s: module MAY be using inspect.%s", module, bad) + safe = False + return safe + + +def iter_symbols(code): + """Yield names and strings used by `code` and its nested code objects""" + for name in code.co_names: + yield name + for const in code.co_consts: + if isinstance(const, six.string_types): + yield const + elif isinstance(const, CodeType): + for name in iter_symbols(const): + yield name + + +def can_scan(): + if not sys.platform.startswith('java') and sys.platform != 'cli': + # CPython, PyPy, etc. + return True + log.warn("Unable to analyze compiled code on this platform.") + log.warn("Please ask the author to include a 'zip_safe'" + " setting (either True or False) in the package's setup.py") + + +# Attribute names of options for commands that might need to be convinced to +# install to the egg build directory + +INSTALL_DIRECTORY_ATTRS = [ + 'install_lib', 'install_dir', 'install_data', 'install_base' +] + + +def make_zipfile(zip_filename, base_dir, verbose=0, dry_run=0, compress=True, + mode='w'): + """Create a zip file from all the files under 'base_dir'. The output + zip file will be named 'base_dir' + ".zip". Uses either the "zipfile" + Python module (if available) or the InfoZIP "zip" utility (if installed + and found on the default search path). If neither tool is available, + raises DistutilsExecError. Returns the name of the output zip file. + """ + import zipfile + + mkpath(os.path.dirname(zip_filename), dry_run=dry_run) + log.info("creating '%s' and adding '%s' to it", zip_filename, base_dir) + + def visit(z, dirname, names): + for name in names: + path = os.path.normpath(os.path.join(dirname, name)) + if os.path.isfile(path): + p = path[len(base_dir) + 1:] + if not dry_run: + z.write(path, p) + log.debug("adding '%s'", p) + + compression = zipfile.ZIP_DEFLATED if compress else zipfile.ZIP_STORED + if not dry_run: + z = zipfile.ZipFile(zip_filename, mode, compression=compression) + for dirname, dirs, files in sorted_walk(base_dir): + visit(z, dirname, files) + z.close() + else: + for dirname, dirs, files in sorted_walk(base_dir): + visit(None, dirname, files) + return zip_filename diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/bdist_rpm.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/bdist_rpm.py new file mode 100644 index 0000000000..70730927ec --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/bdist_rpm.py @@ -0,0 +1,43 @@ +import distutils.command.bdist_rpm as orig + + +class bdist_rpm(orig.bdist_rpm): + """ + Override the default bdist_rpm behavior to do the following: + + 1. Run egg_info to ensure the name and version are properly calculated. + 2. Always run 'install' using --single-version-externally-managed to + disable eggs in RPM distributions. + 3. Replace dash with underscore in the version numbers for better RPM + compatibility. + """ + + def run(self): + # ensure distro name is up-to-date + self.run_command('egg_info') + + orig.bdist_rpm.run(self) + + def _make_spec_file(self): + version = self.distribution.get_version() + rpmversion = version.replace('-', '_') + spec = orig.bdist_rpm._make_spec_file(self) + line23 = '%define version ' + version + line24 = '%define version ' + rpmversion + spec = [ + line.replace( + "Source0: %{name}-%{version}.tar", + "Source0: %{name}-%{unmangled_version}.tar" + ).replace( + "setup.py install ", + "setup.py install --single-version-externally-managed " + ).replace( + "%setup", + "%setup -n %{name}-%{unmangled_version}" + ).replace(line23, line24) + for line in spec + ] + insert_loc = spec.index(line24) + 1 + unmangled_version = "%define unmangled_version " + version + spec.insert(insert_loc, unmangled_version) + return spec diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/bdist_wininst.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/bdist_wininst.py new file mode 100644 index 0000000000..073de97b46 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/bdist_wininst.py @@ -0,0 +1,21 @@ +import distutils.command.bdist_wininst as orig + + +class bdist_wininst(orig.bdist_wininst): + def reinitialize_command(self, command, reinit_subcommands=0): + """ + Supplement reinitialize_command to work around + http://bugs.python.org/issue20819 + """ + cmd = self.distribution.reinitialize_command( + command, reinit_subcommands) + if command in ('install', 'install_lib'): + cmd.install_lib = None + return cmd + + def run(self): + self._is_running = True + try: + orig.bdist_wininst.run(self) + finally: + self._is_running = False diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/develop.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/develop.py new file mode 100644 index 0000000000..b561924609 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/develop.py @@ -0,0 +1,221 @@ +from distutils.util import convert_path +from distutils import log +from distutils.errors import DistutilsError, DistutilsOptionError +import os +import glob +import io + +from setuptools.extern import six + +import pkg_resources +from setuptools.command.easy_install import easy_install +from setuptools import namespaces +import setuptools + +__metaclass__ = type + + +class develop(namespaces.DevelopInstaller, easy_install): + """Set up package for development""" + + description = "install package in 'development mode'" + + user_options = easy_install.user_options + [ + ("uninstall", "u", "Uninstall this source package"), + ("egg-path=", None, "Set the path to be used in the .egg-link file"), + ] + + boolean_options = easy_install.boolean_options + ['uninstall'] + + command_consumes_arguments = False # override base + + def run(self): + if self.uninstall: + self.multi_version = True + self.uninstall_link() + self.uninstall_namespaces() + else: + self.install_for_development() + self.warn_deprecated_options() + + def initialize_options(self): + self.uninstall = None + self.egg_path = None + easy_install.initialize_options(self) + self.setup_path = None + self.always_copy_from = '.' # always copy eggs installed in curdir + + def finalize_options(self): + ei = self.get_finalized_command("egg_info") + if ei.broken_egg_info: + template = "Please rename %r to %r before using 'develop'" + args = ei.egg_info, ei.broken_egg_info + raise DistutilsError(template % args) + self.args = [ei.egg_name] + + easy_install.finalize_options(self) + self.expand_basedirs() + self.expand_dirs() + # pick up setup-dir .egg files only: no .egg-info + self.package_index.scan(glob.glob('*.egg')) + + egg_link_fn = ei.egg_name + '.egg-link' + self.egg_link = os.path.join(self.install_dir, egg_link_fn) + self.egg_base = ei.egg_base + if self.egg_path is None: + self.egg_path = os.path.abspath(ei.egg_base) + + target = pkg_resources.normalize_path(self.egg_base) + egg_path = pkg_resources.normalize_path( + os.path.join(self.install_dir, self.egg_path)) + if egg_path != target: + raise DistutilsOptionError( + "--egg-path must be a relative path from the install" + " directory to " + target + ) + + # Make a distribution for the package's source + self.dist = pkg_resources.Distribution( + target, + pkg_resources.PathMetadata(target, os.path.abspath(ei.egg_info)), + project_name=ei.egg_name + ) + + self.setup_path = self._resolve_setup_path( + self.egg_base, + self.install_dir, + self.egg_path, + ) + + @staticmethod + def _resolve_setup_path(egg_base, install_dir, egg_path): + """ + Generate a path from egg_base back to '.' where the + setup script resides and ensure that path points to the + setup path from $install_dir/$egg_path. + """ + path_to_setup = egg_base.replace(os.sep, '/').rstrip('/') + if path_to_setup != os.curdir: + path_to_setup = '../' * (path_to_setup.count('/') + 1) + resolved = pkg_resources.normalize_path( + os.path.join(install_dir, egg_path, path_to_setup) + ) + if resolved != pkg_resources.normalize_path(os.curdir): + raise DistutilsOptionError( + "Can't get a consistent path to setup script from" + " installation directory", resolved, + pkg_resources.normalize_path(os.curdir)) + return path_to_setup + + def install_for_development(self): + if not six.PY2 and getattr(self.distribution, 'use_2to3', False): + # If we run 2to3 we can not do this inplace: + + # Ensure metadata is up-to-date + self.reinitialize_command('build_py', inplace=0) + self.run_command('build_py') + bpy_cmd = self.get_finalized_command("build_py") + build_path = pkg_resources.normalize_path(bpy_cmd.build_lib) + + # Build extensions + self.reinitialize_command('egg_info', egg_base=build_path) + self.run_command('egg_info') + + self.reinitialize_command('build_ext', inplace=0) + self.run_command('build_ext') + + # Fixup egg-link and easy-install.pth + ei_cmd = self.get_finalized_command("egg_info") + self.egg_path = build_path + self.dist.location = build_path + # XXX + self.dist._provider = pkg_resources.PathMetadata( + build_path, ei_cmd.egg_info) + else: + # Without 2to3 inplace works fine: + self.run_command('egg_info') + + # Build extensions in-place + self.reinitialize_command('build_ext', inplace=1) + self.run_command('build_ext') + + self.install_site_py() # ensure that target dir is site-safe + if setuptools.bootstrap_install_from: + self.easy_install(setuptools.bootstrap_install_from) + setuptools.bootstrap_install_from = None + + self.install_namespaces() + + # create an .egg-link in the installation dir, pointing to our egg + log.info("Creating %s (link to %s)", self.egg_link, self.egg_base) + if not self.dry_run: + with open(self.egg_link, "w") as f: + f.write(self.egg_path + "\n" + self.setup_path) + # postprocess the installed distro, fixing up .pth, installing scripts, + # and handling requirements + self.process_distribution(None, self.dist, not self.no_deps) + + def uninstall_link(self): + if os.path.exists(self.egg_link): + log.info("Removing %s (link to %s)", self.egg_link, self.egg_base) + egg_link_file = open(self.egg_link) + contents = [line.rstrip() for line in egg_link_file] + egg_link_file.close() + if contents not in ([self.egg_path], + [self.egg_path, self.setup_path]): + log.warn("Link points to %s: uninstall aborted", contents) + return + if not self.dry_run: + os.unlink(self.egg_link) + if not self.dry_run: + self.update_pth(self.dist) # remove any .pth link to us + if self.distribution.scripts: + # XXX should also check for entry point scripts! + log.warn("Note: you must uninstall or replace scripts manually!") + + def install_egg_scripts(self, dist): + if dist is not self.dist: + # Installing a dependency, so fall back to normal behavior + return easy_install.install_egg_scripts(self, dist) + + # create wrapper scripts in the script dir, pointing to dist.scripts + + # new-style... + self.install_wrapper_scripts(dist) + + # ...and old-style + for script_name in self.distribution.scripts or []: + script_path = os.path.abspath(convert_path(script_name)) + script_name = os.path.basename(script_path) + with io.open(script_path) as strm: + script_text = strm.read() + self.install_script(dist, script_name, script_text, script_path) + + def install_wrapper_scripts(self, dist): + dist = VersionlessRequirement(dist) + return easy_install.install_wrapper_scripts(self, dist) + + +class VersionlessRequirement: + """ + Adapt a pkg_resources.Distribution to simply return the project + name as the 'requirement' so that scripts will work across + multiple versions. + + >>> from pkg_resources import Distribution + >>> dist = Distribution(project_name='foo', version='1.0') + >>> str(dist.as_requirement()) + 'foo==1.0' + >>> adapted_dist = VersionlessRequirement(dist) + >>> str(adapted_dist.as_requirement()) + 'foo' + """ + + def __init__(self, dist): + self.__dist = dist + + def __getattr__(self, name): + return getattr(self.__dist, name) + + def as_requirement(self): + return self.project_name diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/dist_info.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/dist_info.py new file mode 100644 index 0000000000..c45258fa03 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/dist_info.py @@ -0,0 +1,36 @@ +""" +Create a dist_info directory +As defined in the wheel specification +""" + +import os + +from distutils.core import Command +from distutils import log + + +class dist_info(Command): + + description = 'create a .dist-info directory' + + user_options = [ + ('egg-base=', 'e', "directory containing .egg-info directories" + " (default: top of the source tree)"), + ] + + def initialize_options(self): + self.egg_base = None + + def finalize_options(self): + pass + + def run(self): + egg_info = self.get_finalized_command('egg_info') + egg_info.egg_base = self.egg_base + egg_info.finalize_options() + egg_info.run() + dist_info_dir = egg_info.egg_info[:-len('.egg-info')] + '.dist-info' + log.info("creating '{}'".format(os.path.abspath(dist_info_dir))) + + bdist_wheel = self.get_finalized_command('bdist_wheel') + bdist_wheel.egg2dist(egg_info.egg_info, dist_info_dir) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/easy_install.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/easy_install.py new file mode 100644 index 0000000000..5a8dbe0f4b --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/easy_install.py @@ -0,0 +1,2402 @@ +#!/usr/bin/env python +""" +Easy Install +------------ + +A tool for doing automatic download/extract/build of distutils-based Python +packages. For detailed documentation, see the accompanying EasyInstall.txt +file, or visit the `EasyInstall home page`__. + +__ https://setuptools.readthedocs.io/en/latest/easy_install.html + +""" + +from glob import glob +from distutils.util import get_platform +from distutils.util import convert_path, subst_vars +from distutils.errors import ( + DistutilsArgError, DistutilsOptionError, + DistutilsError, DistutilsPlatformError, +) +from distutils.command.install import INSTALL_SCHEMES, SCHEME_KEYS +from distutils import log, dir_util +from distutils.command.build_scripts import first_line_re +from distutils.spawn import find_executable +import sys +import os +import zipimport +import shutil +import tempfile +import zipfile +import re +import stat +import random +import textwrap +import warnings +import site +import struct +import contextlib +import subprocess +import shlex +import io + + +from sysconfig import get_config_vars, get_path + +from setuptools import SetuptoolsDeprecationWarning + +from setuptools.extern import six +from setuptools.extern.six.moves import configparser, map + +from setuptools import Command +from setuptools.sandbox import run_setup +from setuptools.py27compat import rmtree_safe +from setuptools.command import setopt +from setuptools.archive_util import unpack_archive +from setuptools.package_index import ( + PackageIndex, parse_requirement_arg, URL_SCHEME, +) +from setuptools.command import bdist_egg, egg_info +from setuptools.wheel import Wheel +from pkg_resources import ( + yield_lines, normalize_path, resource_string, ensure_directory, + get_distribution, find_distributions, Environment, Requirement, + Distribution, PathMetadata, EggMetadata, WorkingSet, DistributionNotFound, + VersionConflict, DEVELOP_DIST, +) +import pkg_resources.py31compat + +__metaclass__ = type + +# Turn on PEP440Warnings +warnings.filterwarnings("default", category=pkg_resources.PEP440Warning) + +__all__ = [ + 'samefile', 'easy_install', 'PthDistributions', 'extract_wininst_cfg', + 'main', 'get_exe_prefixes', +] + + +def is_64bit(): + return struct.calcsize("P") == 8 + + +def samefile(p1, p2): + """ + Determine if two paths reference the same file. + + Augments os.path.samefile to work on Windows and + suppresses errors if the path doesn't exist. + """ + both_exist = os.path.exists(p1) and os.path.exists(p2) + use_samefile = hasattr(os.path, 'samefile') and both_exist + if use_samefile: + return os.path.samefile(p1, p2) + norm_p1 = os.path.normpath(os.path.normcase(p1)) + norm_p2 = os.path.normpath(os.path.normcase(p2)) + return norm_p1 == norm_p2 + + +if six.PY2: + + def _to_bytes(s): + return s + + def isascii(s): + try: + six.text_type(s, 'ascii') + return True + except UnicodeError: + return False +else: + + def _to_bytes(s): + return s.encode('utf8') + + def isascii(s): + try: + s.encode('ascii') + return True + except UnicodeError: + return False + + +_one_liner = lambda text: textwrap.dedent(text).strip().replace('\n', '; ') + + +class easy_install(Command): + """Manage a download/build/install process""" + description = "Find/get/install Python packages" + command_consumes_arguments = True + + user_options = [ + ('prefix=', None, "installation prefix"), + ("zip-ok", "z", "install package as a zipfile"), + ("multi-version", "m", "make apps have to require() a version"), + ("upgrade", "U", "force upgrade (searches PyPI for latest versions)"), + ("install-dir=", "d", "install package to DIR"), + ("script-dir=", "s", "install scripts to DIR"), + ("exclude-scripts", "x", "Don't install scripts"), + ("always-copy", "a", "Copy all needed packages to install dir"), + ("index-url=", "i", "base URL of Python Package Index"), + ("find-links=", "f", "additional URL(s) to search for packages"), + ("build-directory=", "b", + "download/extract/build in DIR; keep the results"), + ('optimize=', 'O', + "also compile with optimization: -O1 for \"python -O\", " + "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"), + ('record=', None, + "filename in which to record list of installed files"), + ('always-unzip', 'Z', "don't install as a zipfile, no matter what"), + ('site-dirs=', 'S', "list of directories where .pth files work"), + ('editable', 'e', "Install specified packages in editable form"), + ('no-deps', 'N', "don't install dependencies"), + ('allow-hosts=', 'H', "pattern(s) that hostnames must match"), + ('local-snapshots-ok', 'l', + "allow building eggs from local checkouts"), + ('version', None, "print version information and exit"), + ('install-layout=', None, "installation layout to choose (known values: deb)"), + ('force-installation-into-system-dir', '0', "force installation into /usr"), + ('no-find-links', None, + "Don't load find-links defined in packages being installed") + ] + boolean_options = [ + 'zip-ok', 'multi-version', 'exclude-scripts', 'upgrade', 'always-copy', + 'editable', + 'no-deps', 'local-snapshots-ok', 'version', 'force-installation-into-system-dir' + ] + + if site.ENABLE_USER_SITE: + help_msg = "install in user site-package '%s'" % site.USER_SITE + user_options.append(('user', None, help_msg)) + boolean_options.append('user') + + negative_opt = {'always-unzip': 'zip-ok'} + create_index = PackageIndex + + def initialize_options(self): + # the --user option seems to be an opt-in one, + # so the default should be False. + self.user = 0 + self.zip_ok = self.local_snapshots_ok = None + self.install_dir = self.script_dir = self.exclude_scripts = None + self.index_url = None + self.find_links = None + self.build_directory = None + self.args = None + self.optimize = self.record = None + self.upgrade = self.always_copy = self.multi_version = None + self.editable = self.no_deps = self.allow_hosts = None + self.root = self.prefix = self.no_report = None + self.version = None + self.install_purelib = None # for pure module distributions + self.install_platlib = None # non-pure (dists w/ extensions) + self.install_headers = None # for C/C++ headers + self.install_lib = None # set to either purelib or platlib + self.install_scripts = None + self.install_data = None + self.install_base = None + self.install_platbase = None + if site.ENABLE_USER_SITE: + self.install_userbase = site.USER_BASE + self.install_usersite = site.USER_SITE + else: + self.install_userbase = None + self.install_usersite = None + self.no_find_links = None + + # Options not specifiable via command line + self.package_index = None + self.pth_file = self.always_copy_from = None + self.site_dirs = None + self.installed_projects = {} + self.sitepy_installed = False + # enable custom installation, known values: deb + self.install_layout = None + self.force_installation_into_system_dir = None + self.multiarch = None + + # Always read easy_install options, even if we are subclassed, or have + # an independent instance created. This ensures that defaults will + # always come from the standard configuration file(s)' "easy_install" + # section, even if this is a "develop" or "install" command, or some + # other embedding. + self._dry_run = None + self.verbose = self.distribution.verbose + self.distribution._set_command_options( + self, self.distribution.get_option_dict('easy_install') + ) + + def delete_blockers(self, blockers): + extant_blockers = ( + filename for filename in blockers + if os.path.exists(filename) or os.path.islink(filename) + ) + list(map(self._delete_path, extant_blockers)) + + def _delete_path(self, path): + log.info("Deleting %s", path) + if self.dry_run: + return + + is_tree = os.path.isdir(path) and not os.path.islink(path) + remover = rmtree if is_tree else os.unlink + remover(path) + + @staticmethod + def _render_version(): + """ + Render the Setuptools version and installation details, then exit. + """ + ver = '{}.{}'.format(*sys.version_info) + dist = get_distribution('setuptools') + tmpl = 'setuptools {dist.version} from {dist.location} (Python {ver})' + print(tmpl.format(**locals())) + raise SystemExit() + + def finalize_options(self): + self.version and self._render_version() + + py_version = sys.version.split()[0] + prefix, exec_prefix = get_config_vars('prefix', 'exec_prefix') + + self.config_vars = { + 'dist_name': self.distribution.get_name(), + 'dist_version': self.distribution.get_version(), + 'dist_fullname': self.distribution.get_fullname(), + 'py_version': py_version, + 'py_version_short': py_version[0:3], + 'py_version_nodot': py_version[0] + py_version[2], + 'sys_prefix': prefix, + 'prefix': prefix, + 'sys_exec_prefix': exec_prefix, + 'exec_prefix': exec_prefix, + # Only python 3.2+ has abiflags + 'abiflags': getattr(sys, 'abiflags', ''), + } + + if site.ENABLE_USER_SITE: + self.config_vars['userbase'] = self.install_userbase + self.config_vars['usersite'] = self.install_usersite + + self._fix_install_dir_for_user_site() + + self.expand_basedirs() + self.expand_dirs() + + if self.install_layout: + if not self.install_layout.lower() in ['deb']: + raise DistutilsOptionError("unknown value for --install-layout") + self.install_layout = self.install_layout.lower() + + import sysconfig + if sys.version_info[:2] >= (3, 3): + self.multiarch = sysconfig.get_config_var('MULTIARCH') + + self._expand( + 'install_dir', 'script_dir', 'build_directory', + 'site_dirs', + ) + # If a non-default installation directory was specified, default the + # script directory to match it. + if self.script_dir is None: + self.script_dir = self.install_dir + + if self.no_find_links is None: + self.no_find_links = False + + # Let install_dir get set by install_lib command, which in turn + # gets its info from the install command, and takes into account + # --prefix and --home and all that other crud. + self.set_undefined_options( + 'install_lib', ('install_dir', 'install_dir') + ) + # Likewise, set default script_dir from 'install_scripts.install_dir' + self.set_undefined_options( + 'install_scripts', ('install_dir', 'script_dir') + ) + + if self.user and self.install_purelib: + self.install_dir = self.install_purelib + self.script_dir = self.install_scripts + + if self.prefix == '/usr' and not self.force_installation_into_system_dir: + raise DistutilsOptionError("""installation into /usr + +Trying to install into the system managed parts of the file system. Please +consider to install to another location, or use the option +--force-installation-into-system-dir to overwrite this warning. +""") + + # default --record from the install command + self.set_undefined_options('install', ('record', 'record')) + # Should this be moved to the if statement below? It's not used + # elsewhere + normpath = map(normalize_path, sys.path) + self.all_site_dirs = get_site_dirs() + if self.site_dirs is not None: + site_dirs = [ + os.path.expanduser(s.strip()) for s in + self.site_dirs.split(',') + ] + for d in site_dirs: + if not os.path.isdir(d): + log.warn("%s (in --site-dirs) does not exist", d) + elif normalize_path(d) not in normpath: + raise DistutilsOptionError( + d + " (in --site-dirs) is not on sys.path" + ) + else: + self.all_site_dirs.append(normalize_path(d)) + if not self.editable: + self.check_site_dir() + self.index_url = self.index_url or "https://pypi.org/simple/" + self.shadow_path = self.all_site_dirs[:] + for path_item in self.install_dir, normalize_path(self.script_dir): + if path_item not in self.shadow_path: + self.shadow_path.insert(0, path_item) + + if self.allow_hosts is not None: + hosts = [s.strip() for s in self.allow_hosts.split(',')] + else: + hosts = ['*'] + if self.package_index is None: + self.package_index = self.create_index( + self.index_url, search_path=self.shadow_path, hosts=hosts, + ) + self.local_index = Environment(self.shadow_path + sys.path) + + if self.find_links is not None: + if isinstance(self.find_links, six.string_types): + self.find_links = self.find_links.split() + else: + self.find_links = [] + if self.local_snapshots_ok: + self.package_index.scan_egg_links(self.shadow_path + sys.path) + if not self.no_find_links: + self.package_index.add_find_links(self.find_links) + self.set_undefined_options('install_lib', ('optimize', 'optimize')) + if not isinstance(self.optimize, int): + try: + self.optimize = int(self.optimize) + if not (0 <= self.optimize <= 2): + raise ValueError + except ValueError: + raise DistutilsOptionError("--optimize must be 0, 1, or 2") + + if self.editable and not self.build_directory: + raise DistutilsArgError( + "Must specify a build directory (-b) when using --editable" + ) + if not self.args: + raise DistutilsArgError( + "No urls, filenames, or requirements specified (see --help)") + + self.outputs = [] + + def _fix_install_dir_for_user_site(self): + """ + Fix the install_dir if "--user" was used. + """ + if not self.user or not site.ENABLE_USER_SITE: + return + + self.create_home_path() + if self.install_userbase is None: + msg = "User base directory is not specified" + raise DistutilsPlatformError(msg) + self.install_base = self.install_platbase = self.install_userbase + scheme_name = os.name.replace('posix', 'unix') + '_user' + self.select_scheme(scheme_name) + + def _expand_attrs(self, attrs): + for attr in attrs: + val = getattr(self, attr) + if val is not None: + if os.name == 'posix' or os.name == 'nt': + val = os.path.expanduser(val) + val = subst_vars(val, self.config_vars) + setattr(self, attr, val) + + def expand_basedirs(self): + """Calls `os.path.expanduser` on install_base, install_platbase and + root.""" + self._expand_attrs(['install_base', 'install_platbase', 'root']) + + def expand_dirs(self): + """Calls `os.path.expanduser` on install dirs.""" + dirs = [ + 'install_purelib', + 'install_platlib', + 'install_lib', + 'install_headers', + 'install_scripts', + 'install_data', + ] + self._expand_attrs(dirs) + + def run(self, show_deprecation=True): + if show_deprecation: + self.announce( + "WARNING: The easy_install command is deprecated " + "and will be removed in a future version." + , log.WARN, + ) + if self.verbose != self.distribution.verbose: + log.set_verbosity(self.verbose) + try: + for spec in self.args: + self.easy_install(spec, not self.no_deps) + if self.record: + outputs = list(sorted(self.outputs)) + if self.root: # strip any package prefix + root_len = len(self.root) + for counter in range(len(outputs)): + outputs[counter] = outputs[counter][root_len:] + from distutils import file_util + + self.execute( + file_util.write_file, (self.record, outputs), + "writing list of installed files to '%s'" % + self.record + ) + self.warn_deprecated_options() + finally: + log.set_verbosity(self.distribution.verbose) + + def pseudo_tempname(self): + """Return a pseudo-tempname base in the install directory. + This code is intentionally naive; if a malicious party can write to + the target directory you're already in deep doodoo. + """ + try: + pid = os.getpid() + except Exception: + pid = random.randint(0, sys.maxsize) + return os.path.join(self.install_dir, "test-easy-install-%s" % pid) + + def warn_deprecated_options(self): + pass + + def check_site_dir(self): + """Verify that self.install_dir is .pth-capable dir, if needed""" + + instdir = normalize_path(self.install_dir) + pth_file = os.path.join(instdir, 'easy-install.pth') + + # Is it a configured, PYTHONPATH, implicit, or explicit site dir? + is_site_dir = instdir in self.all_site_dirs + + if not is_site_dir and not self.multi_version: + # No? Then directly test whether it does .pth file processing + is_site_dir = self.check_pth_processing() + else: + # make sure we can write to target dir + testfile = self.pseudo_tempname() + '.write-test' + test_exists = os.path.exists(testfile) + try: + if test_exists: + os.unlink(testfile) + open(testfile, 'w').close() + os.unlink(testfile) + except (OSError, IOError): + self.cant_write_to_target() + + if not is_site_dir and not self.multi_version: + # Can't install non-multi to non-site dir + raise DistutilsError(self.no_default_version_msg()) + + if is_site_dir: + if self.pth_file is None: + self.pth_file = PthDistributions(pth_file, self.all_site_dirs) + else: + self.pth_file = None + + if instdir not in map(normalize_path, _pythonpath()): + # only PYTHONPATH dirs need a site.py, so pretend it's there + self.sitepy_installed = True + elif self.multi_version and not os.path.exists(pth_file): + self.sitepy_installed = True # don't need site.py in this case + self.pth_file = None # and don't create a .pth file + self.install_dir = instdir + + __cant_write_msg = textwrap.dedent(""" + can't create or remove files in install directory + + The following error occurred while trying to add or remove files in the + installation directory: + + %s + + The installation directory you specified (via --install-dir, --prefix, or + the distutils default setting) was: + + %s + """).lstrip() + + __not_exists_id = textwrap.dedent(""" + This directory does not currently exist. Please create it and try again, or + choose a different installation directory (using the -d or --install-dir + option). + """).lstrip() + + __access_msg = textwrap.dedent(""" + Perhaps your account does not have write access to this directory? If the + installation directory is a system-owned directory, you may need to sign in + as the administrator or "root" account. If you do not have administrative + access to this machine, you may wish to choose a different installation + directory, preferably one that is listed in your PYTHONPATH environment + variable. + + For information on other options, you may wish to consult the + documentation at: + + https://setuptools.readthedocs.io/en/latest/easy_install.html + + Please make the appropriate changes for your system and try again. + """).lstrip() + + def cant_write_to_target(self): + msg = self.__cant_write_msg % (sys.exc_info()[1], self.install_dir,) + + if not os.path.exists(self.install_dir): + msg += '\n' + self.__not_exists_id + else: + msg += '\n' + self.__access_msg + raise DistutilsError(msg) + + def check_pth_processing(self): + """Empirically verify whether .pth files are supported in inst. dir""" + instdir = self.install_dir + log.info("Checking .pth file support in %s", instdir) + pth_file = self.pseudo_tempname() + ".pth" + ok_file = pth_file + '.ok' + ok_exists = os.path.exists(ok_file) + tmpl = _one_liner(""" + import os + f = open({ok_file!r}, 'w') + f.write('OK') + f.close() + """) + '\n' + try: + if ok_exists: + os.unlink(ok_file) + dirname = os.path.dirname(ok_file) + pkg_resources.py31compat.makedirs(dirname, exist_ok=True) + f = open(pth_file, 'w') + except (OSError, IOError): + self.cant_write_to_target() + else: + try: + f.write(tmpl.format(**locals())) + f.close() + f = None + executable = sys.executable + if os.name == 'nt': + dirname, basename = os.path.split(executable) + alt = os.path.join(dirname, 'pythonw.exe') + use_alt = ( + basename.lower() == 'python.exe' and + os.path.exists(alt) + ) + if use_alt: + # use pythonw.exe to avoid opening a console window + executable = alt + + from distutils.spawn import spawn + + spawn([executable, '-E', '-c', 'pass'], 0) + + if os.path.exists(ok_file): + log.info( + "TEST PASSED: %s appears to support .pth files", + instdir + ) + return True + finally: + if f: + f.close() + if os.path.exists(ok_file): + os.unlink(ok_file) + if os.path.exists(pth_file): + os.unlink(pth_file) + if not self.multi_version: + log.warn("TEST FAILED: %s does NOT support .pth files", instdir) + return False + + def install_egg_scripts(self, dist): + """Write all the scripts for `dist`, unless scripts are excluded""" + if not self.exclude_scripts and dist.metadata_isdir('scripts'): + for script_name in dist.metadata_listdir('scripts'): + if dist.metadata_isdir('scripts/' + script_name): + # The "script" is a directory, likely a Python 3 + # __pycache__ directory, so skip it. + continue + self.install_script( + dist, script_name, + dist.get_metadata('scripts/' + script_name) + ) + self.install_wrapper_scripts(dist) + + def add_output(self, path): + if os.path.isdir(path): + for base, dirs, files in os.walk(path): + for filename in files: + self.outputs.append(os.path.join(base, filename)) + else: + self.outputs.append(path) + + def not_editable(self, spec): + if self.editable: + raise DistutilsArgError( + "Invalid argument %r: you can't use filenames or URLs " + "with --editable (except via the --find-links option)." + % (spec,) + ) + + def check_editable(self, spec): + if not self.editable: + return + + if os.path.exists(os.path.join(self.build_directory, spec.key)): + raise DistutilsArgError( + "%r already exists in %s; can't do a checkout there" % + (spec.key, self.build_directory) + ) + + @contextlib.contextmanager + def _tmpdir(self): + tmpdir = tempfile.mkdtemp(prefix=u"easy_install-") + try: + # cast to str as workaround for #709 and #710 and #712 + yield str(tmpdir) + finally: + os.path.exists(tmpdir) and rmtree(rmtree_safe(tmpdir)) + + def easy_install(self, spec, deps=False): + if not self.editable: + self.install_site_py() + + with self._tmpdir() as tmpdir: + if not isinstance(spec, Requirement): + if URL_SCHEME(spec): + # It's a url, download it to tmpdir and process + self.not_editable(spec) + dl = self.package_index.download(spec, tmpdir) + return self.install_item(None, dl, tmpdir, deps, True) + + elif os.path.exists(spec): + # Existing file or directory, just process it directly + self.not_editable(spec) + return self.install_item(None, spec, tmpdir, deps, True) + else: + spec = parse_requirement_arg(spec) + + self.check_editable(spec) + dist = self.package_index.fetch_distribution( + spec, tmpdir, self.upgrade, self.editable, + not self.always_copy, self.local_index + ) + if dist is None: + msg = "Could not find suitable distribution for %r" % spec + if self.always_copy: + msg += " (--always-copy skips system and development eggs)" + raise DistutilsError(msg) + elif dist.precedence == DEVELOP_DIST: + # .egg-info dists don't need installing, just process deps + self.process_distribution(spec, dist, deps, "Using") + return dist + else: + return self.install_item(spec, dist.location, tmpdir, deps) + + def install_item(self, spec, download, tmpdir, deps, install_needed=False): + + # Installation is also needed if file in tmpdir or is not an egg + install_needed = install_needed or self.always_copy + install_needed = install_needed or os.path.dirname(download) == tmpdir + install_needed = install_needed or not download.endswith('.egg') + install_needed = install_needed or ( + self.always_copy_from is not None and + os.path.dirname(normalize_path(download)) == + normalize_path(self.always_copy_from) + ) + + if spec and not install_needed: + # at this point, we know it's a local .egg, we just don't know if + # it's already installed. + for dist in self.local_index[spec.project_name]: + if dist.location == download: + break + else: + install_needed = True # it's not in the local index + + log.info("Processing %s", os.path.basename(download)) + + if install_needed: + dists = self.install_eggs(spec, download, tmpdir) + for dist in dists: + self.process_distribution(spec, dist, deps) + else: + dists = [self.egg_distribution(download)] + self.process_distribution(spec, dists[0], deps, "Using") + + if spec is not None: + for dist in dists: + if dist in spec: + return dist + + def select_scheme(self, name): + """Sets the install directories by applying the install schemes.""" + # it's the caller's problem if they supply a bad name! + scheme = INSTALL_SCHEMES[name] + for key in SCHEME_KEYS: + attrname = 'install_' + key + if getattr(self, attrname) is None: + setattr(self, attrname, scheme[key]) + + def process_distribution(self, requirement, dist, deps=True, *info): + self.update_pth(dist) + self.package_index.add(dist) + if dist in self.local_index[dist.key]: + self.local_index.remove(dist) + self.local_index.add(dist) + self.install_egg_scripts(dist) + self.installed_projects[dist.key] = dist + log.info(self.installation_report(requirement, dist, *info)) + if (dist.has_metadata('dependency_links.txt') and + not self.no_find_links): + self.package_index.add_find_links( + dist.get_metadata_lines('dependency_links.txt') + ) + if not deps and not self.always_copy: + return + elif requirement is not None and dist.key != requirement.key: + log.warn("Skipping dependencies for %s", dist) + return # XXX this is not the distribution we were looking for + elif requirement is None or dist not in requirement: + # if we wound up with a different version, resolve what we've got + distreq = dist.as_requirement() + requirement = Requirement(str(distreq)) + log.info("Processing dependencies for %s", requirement) + try: + distros = WorkingSet([]).resolve( + [requirement], self.local_index, self.easy_install + ) + except DistributionNotFound as e: + raise DistutilsError(str(e)) + except VersionConflict as e: + raise DistutilsError(e.report()) + if self.always_copy or self.always_copy_from: + # Force all the relevant distros to be copied or activated + for dist in distros: + if dist.key not in self.installed_projects: + self.easy_install(dist.as_requirement()) + log.info("Finished processing dependencies for %s", requirement) + + def should_unzip(self, dist): + if self.zip_ok is not None: + return not self.zip_ok + if dist.has_metadata('not-zip-safe'): + return True + if not dist.has_metadata('zip-safe'): + return True + return False + + def maybe_move(self, spec, dist_filename, setup_base): + dst = os.path.join(self.build_directory, spec.key) + if os.path.exists(dst): + msg = ( + "%r already exists in %s; build directory %s will not be kept" + ) + log.warn(msg, spec.key, self.build_directory, setup_base) + return setup_base + if os.path.isdir(dist_filename): + setup_base = dist_filename + else: + if os.path.dirname(dist_filename) == setup_base: + os.unlink(dist_filename) # get it out of the tmp dir + contents = os.listdir(setup_base) + if len(contents) == 1: + dist_filename = os.path.join(setup_base, contents[0]) + if os.path.isdir(dist_filename): + # if the only thing there is a directory, move it instead + setup_base = dist_filename + ensure_directory(dst) + shutil.move(setup_base, dst) + return dst + + def install_wrapper_scripts(self, dist): + if self.exclude_scripts: + return + for args in ScriptWriter.best().get_args(dist): + self.write_script(*args) + + def install_script(self, dist, script_name, script_text, dev_path=None): + """Generate a legacy script wrapper and install it""" + spec = str(dist.as_requirement()) + is_script = is_python_script(script_text, script_name) + + if is_script: + body = self._load_template(dev_path) % locals() + script_text = ScriptWriter.get_header(script_text) + body + self.write_script(script_name, _to_bytes(script_text), 'b') + + @staticmethod + def _load_template(dev_path): + """ + There are a couple of template scripts in the package. This + function loads one of them and prepares it for use. + """ + # See https://github.com/pypa/setuptools/issues/134 for info + # on script file naming and downstream issues with SVR4 + name = 'script.tmpl' + if dev_path: + name = name.replace('.tmpl', ' (dev).tmpl') + + raw_bytes = resource_string('setuptools', name) + return raw_bytes.decode('utf-8') + + def write_script(self, script_name, contents, mode="t", blockers=()): + """Write an executable file to the scripts directory""" + self.delete_blockers( # clean up old .py/.pyw w/o a script + [os.path.join(self.script_dir, x) for x in blockers] + ) + log.info("Installing %s script to %s", script_name, self.script_dir) + target = os.path.join(self.script_dir, script_name) + self.add_output(target) + + if self.dry_run: + return + + mask = current_umask() + ensure_directory(target) + if os.path.exists(target): + os.unlink(target) + with open(target, "w" + mode) as f: + f.write(contents) + chmod(target, 0o777 - mask) + + def install_eggs(self, spec, dist_filename, tmpdir): + # .egg dirs or files are already built, so just return them + if dist_filename.lower().endswith('.egg'): + return [self.install_egg(dist_filename, tmpdir)] + elif dist_filename.lower().endswith('.exe'): + return [self.install_exe(dist_filename, tmpdir)] + elif dist_filename.lower().endswith('.whl'): + return [self.install_wheel(dist_filename, tmpdir)] + + # Anything else, try to extract and build + setup_base = tmpdir + if os.path.isfile(dist_filename) and not dist_filename.endswith('.py'): + unpack_archive(dist_filename, tmpdir, self.unpack_progress) + elif os.path.isdir(dist_filename): + setup_base = os.path.abspath(dist_filename) + + if (setup_base.startswith(tmpdir) # something we downloaded + and self.build_directory and spec is not None): + setup_base = self.maybe_move(spec, dist_filename, setup_base) + + # Find the setup.py file + setup_script = os.path.join(setup_base, 'setup.py') + + if not os.path.exists(setup_script): + setups = glob(os.path.join(setup_base, '*', 'setup.py')) + if not setups: + raise DistutilsError( + "Couldn't find a setup script in %s" % + os.path.abspath(dist_filename) + ) + if len(setups) > 1: + raise DistutilsError( + "Multiple setup scripts in %s" % + os.path.abspath(dist_filename) + ) + setup_script = setups[0] + + # Now run it, and return the result + if self.editable: + log.info(self.report_editable(spec, setup_script)) + return [] + else: + return self.build_and_install(setup_script, setup_base) + + def egg_distribution(self, egg_path): + if os.path.isdir(egg_path): + metadata = PathMetadata(egg_path, os.path.join(egg_path, + 'EGG-INFO')) + else: + metadata = EggMetadata(zipimport.zipimporter(egg_path)) + return Distribution.from_filename(egg_path, metadata=metadata) + + def install_egg(self, egg_path, tmpdir): + destination = os.path.join( + self.install_dir, + os.path.basename(egg_path), + ) + destination = os.path.abspath(destination) + if not self.dry_run: + ensure_directory(destination) + + dist = self.egg_distribution(egg_path) + if not samefile(egg_path, destination): + if os.path.isdir(destination) and not os.path.islink(destination): + dir_util.remove_tree(destination, dry_run=self.dry_run) + elif os.path.exists(destination): + self.execute( + os.unlink, + (destination,), + "Removing " + destination, + ) + try: + new_dist_is_zipped = False + if os.path.isdir(egg_path): + if egg_path.startswith(tmpdir): + f, m = shutil.move, "Moving" + else: + f, m = shutil.copytree, "Copying" + elif self.should_unzip(dist): + self.mkpath(destination) + f, m = self.unpack_and_compile, "Extracting" + else: + new_dist_is_zipped = True + if egg_path.startswith(tmpdir): + f, m = shutil.move, "Moving" + else: + f, m = shutil.copy2, "Copying" + self.execute( + f, + (egg_path, destination), + (m + " %s to %s") % ( + os.path.basename(egg_path), + os.path.dirname(destination) + ), + ) + update_dist_caches( + destination, + fix_zipimporter_caches=new_dist_is_zipped, + ) + except Exception: + update_dist_caches(destination, fix_zipimporter_caches=False) + raise + + self.add_output(destination) + return self.egg_distribution(destination) + + def install_exe(self, dist_filename, tmpdir): + # See if it's valid, get data + cfg = extract_wininst_cfg(dist_filename) + if cfg is None: + raise DistutilsError( + "%s is not a valid distutils Windows .exe" % dist_filename + ) + # Create a dummy distribution object until we build the real distro + dist = Distribution( + None, + project_name=cfg.get('metadata', 'name'), + version=cfg.get('metadata', 'version'), platform=get_platform(), + ) + + # Convert the .exe to an unpacked egg + egg_path = os.path.join(tmpdir, dist.egg_name() + '.egg') + dist.location = egg_path + egg_tmp = egg_path + '.tmp' + _egg_info = os.path.join(egg_tmp, 'EGG-INFO') + pkg_inf = os.path.join(_egg_info, 'PKG-INFO') + ensure_directory(pkg_inf) # make sure EGG-INFO dir exists + dist._provider = PathMetadata(egg_tmp, _egg_info) # XXX + self.exe_to_egg(dist_filename, egg_tmp) + + # Write EGG-INFO/PKG-INFO + if not os.path.exists(pkg_inf): + f = open(pkg_inf, 'w') + f.write('Metadata-Version: 1.0\n') + for k, v in cfg.items('metadata'): + if k != 'target_version': + f.write('%s: %s\n' % (k.replace('_', '-').title(), v)) + f.close() + script_dir = os.path.join(_egg_info, 'scripts') + # delete entry-point scripts to avoid duping + self.delete_blockers([ + os.path.join(script_dir, args[0]) + for args in ScriptWriter.get_args(dist) + ]) + # Build .egg file from tmpdir + bdist_egg.make_zipfile( + egg_path, egg_tmp, verbose=self.verbose, dry_run=self.dry_run, + ) + # install the .egg + return self.install_egg(egg_path, tmpdir) + + def exe_to_egg(self, dist_filename, egg_tmp): + """Extract a bdist_wininst to the directories an egg would use""" + # Check for .pth file and set up prefix translations + prefixes = get_exe_prefixes(dist_filename) + to_compile = [] + native_libs = [] + top_level = {} + + def process(src, dst): + s = src.lower() + for old, new in prefixes: + if s.startswith(old): + src = new + src[len(old):] + parts = src.split('/') + dst = os.path.join(egg_tmp, *parts) + dl = dst.lower() + if dl.endswith('.pyd') or dl.endswith('.dll'): + parts[-1] = bdist_egg.strip_module(parts[-1]) + top_level[os.path.splitext(parts[0])[0]] = 1 + native_libs.append(src) + elif dl.endswith('.py') and old != 'SCRIPTS/': + top_level[os.path.splitext(parts[0])[0]] = 1 + to_compile.append(dst) + return dst + if not src.endswith('.pth'): + log.warn("WARNING: can't process %s", src) + return None + + # extract, tracking .pyd/.dll->native_libs and .py -> to_compile + unpack_archive(dist_filename, egg_tmp, process) + stubs = [] + for res in native_libs: + if res.lower().endswith('.pyd'): # create stubs for .pyd's + parts = res.split('/') + resource = parts[-1] + parts[-1] = bdist_egg.strip_module(parts[-1]) + '.py' + pyfile = os.path.join(egg_tmp, *parts) + to_compile.append(pyfile) + stubs.append(pyfile) + bdist_egg.write_stub(resource, pyfile) + self.byte_compile(to_compile) # compile .py's + bdist_egg.write_safety_flag( + os.path.join(egg_tmp, 'EGG-INFO'), + bdist_egg.analyze_egg(egg_tmp, stubs)) # write zip-safety flag + + for name in 'top_level', 'native_libs': + if locals()[name]: + txt = os.path.join(egg_tmp, 'EGG-INFO', name + '.txt') + if not os.path.exists(txt): + f = open(txt, 'w') + f.write('\n'.join(locals()[name]) + '\n') + f.close() + + def install_wheel(self, wheel_path, tmpdir): + wheel = Wheel(wheel_path) + assert wheel.is_compatible() + destination = os.path.join(self.install_dir, wheel.egg_name()) + destination = os.path.abspath(destination) + if not self.dry_run: + ensure_directory(destination) + if os.path.isdir(destination) and not os.path.islink(destination): + dir_util.remove_tree(destination, dry_run=self.dry_run) + elif os.path.exists(destination): + self.execute( + os.unlink, + (destination,), + "Removing " + destination, + ) + try: + self.execute( + wheel.install_as_egg, + (destination,), + ("Installing %s to %s") % ( + os.path.basename(wheel_path), + os.path.dirname(destination) + ), + ) + finally: + update_dist_caches(destination, fix_zipimporter_caches=False) + self.add_output(destination) + return self.egg_distribution(destination) + + __mv_warning = textwrap.dedent(""" + Because this distribution was installed --multi-version, before you can + import modules from this package in an application, you will need to + 'import pkg_resources' and then use a 'require()' call similar to one of + these examples, in order to select the desired version: + + pkg_resources.require("%(name)s") # latest installed version + pkg_resources.require("%(name)s==%(version)s") # this exact version + pkg_resources.require("%(name)s>=%(version)s") # this version or higher + """).lstrip() + + __id_warning = textwrap.dedent(""" + Note also that the installation directory must be on sys.path at runtime for + this to work. (e.g. by being the application's script directory, by being on + PYTHONPATH, or by being added to sys.path by your code.) + """) + + def installation_report(self, req, dist, what="Installed"): + """Helpful installation message for display to package users""" + msg = "\n%(what)s %(eggloc)s%(extras)s" + if self.multi_version and not self.no_report: + msg += '\n' + self.__mv_warning + if self.install_dir not in map(normalize_path, sys.path): + msg += '\n' + self.__id_warning + + eggloc = dist.location + name = dist.project_name + version = dist.version + extras = '' # TODO: self.report_extras(req, dist) + return msg % locals() + + __editable_msg = textwrap.dedent(""" + Extracted editable version of %(spec)s to %(dirname)s + + If it uses setuptools in its setup script, you can activate it in + "development" mode by going to that directory and running:: + + %(python)s setup.py develop + + See the setuptools documentation for the "develop" command for more info. + """).lstrip() + + def report_editable(self, spec, setup_script): + dirname = os.path.dirname(setup_script) + python = sys.executable + return '\n' + self.__editable_msg % locals() + + def run_setup(self, setup_script, setup_base, args): + sys.modules.setdefault('distutils.command.bdist_egg', bdist_egg) + sys.modules.setdefault('distutils.command.egg_info', egg_info) + + args = list(args) + if self.verbose > 2: + v = 'v' * (self.verbose - 1) + args.insert(0, '-' + v) + elif self.verbose < 2: + args.insert(0, '-q') + if self.dry_run: + args.insert(0, '-n') + log.info( + "Running %s %s", setup_script[len(setup_base) + 1:], ' '.join(args) + ) + try: + run_setup(setup_script, args) + except SystemExit as v: + raise DistutilsError("Setup script exited with %s" % (v.args[0],)) + + def build_and_install(self, setup_script, setup_base): + args = ['bdist_egg', '--dist-dir'] + + dist_dir = tempfile.mkdtemp( + prefix='egg-dist-tmp-', dir=os.path.dirname(setup_script) + ) + try: + self._set_fetcher_options(os.path.dirname(setup_script)) + args.append(dist_dir) + + self.run_setup(setup_script, setup_base, args) + all_eggs = Environment([dist_dir]) + eggs = [] + for key in all_eggs: + for dist in all_eggs[key]: + eggs.append(self.install_egg(dist.location, setup_base)) + if not eggs and not self.dry_run: + log.warn("No eggs found in %s (setup script problem?)", + dist_dir) + return eggs + finally: + rmtree(dist_dir) + log.set_verbosity(self.verbose) # restore our log verbosity + + def _set_fetcher_options(self, base): + """ + When easy_install is about to run bdist_egg on a source dist, that + source dist might have 'setup_requires' directives, requiring + additional fetching. Ensure the fetcher options given to easy_install + are available to that command as well. + """ + # find the fetch options from easy_install and write them out + # to the setup.cfg file. + ei_opts = self.distribution.get_option_dict('easy_install').copy() + fetch_directives = ( + 'find_links', 'site_dirs', 'index_url', 'optimize', 'allow_hosts', + ) + fetch_options = {} + for key, val in ei_opts.items(): + if key not in fetch_directives: + continue + fetch_options[key.replace('_', '-')] = val[1] + # create a settings dictionary suitable for `edit_config` + settings = dict(easy_install=fetch_options) + cfg_filename = os.path.join(base, 'setup.cfg') + setopt.edit_config(cfg_filename, settings) + + def update_pth(self, dist): + if self.pth_file is None: + return + + for d in self.pth_file[dist.key]: # drop old entries + if self.multi_version or d.location != dist.location: + log.info("Removing %s from easy-install.pth file", d) + self.pth_file.remove(d) + if d.location in self.shadow_path: + self.shadow_path.remove(d.location) + + if not self.multi_version: + if dist.location in self.pth_file.paths: + log.info( + "%s is already the active version in easy-install.pth", + dist, + ) + else: + log.info("Adding %s to easy-install.pth file", dist) + self.pth_file.add(dist) # add new entry + if dist.location not in self.shadow_path: + self.shadow_path.append(dist.location) + + if not self.dry_run: + + self.pth_file.save() + + if dist.key == 'setuptools': + # Ensure that setuptools itself never becomes unavailable! + # XXX should this check for latest version? + filename = os.path.join(self.install_dir, 'setuptools.pth') + if os.path.islink(filename): + os.unlink(filename) + f = open(filename, 'wt') + f.write(self.pth_file.make_relative(dist.location) + '\n') + f.close() + + def unpack_progress(self, src, dst): + # Progress filter for unpacking + log.debug("Unpacking %s to %s", src, dst) + return dst # only unpack-and-compile skips files for dry run + + def unpack_and_compile(self, egg_path, destination): + to_compile = [] + to_chmod = [] + + def pf(src, dst): + if dst.endswith('.py') and not src.startswith('EGG-INFO/'): + to_compile.append(dst) + elif dst.endswith('.dll') or dst.endswith('.so'): + to_chmod.append(dst) + self.unpack_progress(src, dst) + return not self.dry_run and dst or None + + unpack_archive(egg_path, destination, pf) + self.byte_compile(to_compile) + if not self.dry_run: + for f in to_chmod: + mode = ((os.stat(f)[stat.ST_MODE]) | 0o555) & 0o7755 + chmod(f, mode) + + def byte_compile(self, to_compile): + if sys.dont_write_bytecode: + return + + from distutils.util import byte_compile + + try: + # try to make the byte compile messages quieter + log.set_verbosity(self.verbose - 1) + + byte_compile(to_compile, optimize=0, force=1, dry_run=self.dry_run) + if self.optimize: + byte_compile( + to_compile, optimize=self.optimize, force=1, + dry_run=self.dry_run, + ) + finally: + log.set_verbosity(self.verbose) # restore original verbosity + + __no_default_msg = textwrap.dedent(""" + bad install directory or PYTHONPATH + + You are attempting to install a package to a directory that is not + on PYTHONPATH and which Python does not read ".pth" files from. The + installation directory you specified (via --install-dir, --prefix, or + the distutils default setting) was: + + %s + + and your PYTHONPATH environment variable currently contains: + + %r + + Here are some of your options for correcting the problem: + + * You can choose a different installation directory, i.e., one that is + on PYTHONPATH or supports .pth files + + * You can add the installation directory to the PYTHONPATH environment + variable. (It must then also be on PYTHONPATH whenever you run + Python and want to use the package(s) you are installing.) + + * You can set up the installation directory to support ".pth" files by + using one of the approaches described here: + + https://setuptools.readthedocs.io/en/latest/easy_install.html#custom-installation-locations + + + Please make the appropriate changes for your system and try again.""").lstrip() + + def no_default_version_msg(self): + template = self.__no_default_msg + return template % (self.install_dir, os.environ.get('PYTHONPATH', '')) + + def install_site_py(self): + """Make sure there's a site.py in the target dir, if needed""" + + if self.sitepy_installed: + return # already did it, or don't need to + + sitepy = os.path.join(self.install_dir, "site.py") + source = resource_string("setuptools", "site-patch.py") + source = source.decode('utf-8') + current = "" + + if os.path.exists(sitepy): + log.debug("Checking existing site.py in %s", self.install_dir) + with io.open(sitepy) as strm: + current = strm.read() + + if not current.startswith('def __boot():'): + raise DistutilsError( + "%s is not a setuptools-generated site.py; please" + " remove it." % sitepy + ) + + if current != source: + log.info("Creating %s", sitepy) + if not self.dry_run: + ensure_directory(sitepy) + with io.open(sitepy, 'w', encoding='utf-8') as strm: + strm.write(source) + self.byte_compile([sitepy]) + + self.sitepy_installed = True + + def create_home_path(self): + """Create directories under ~.""" + if not self.user: + return + home = convert_path(os.path.expanduser("~")) + for name, path in six.iteritems(self.config_vars): + if path.startswith(home) and not os.path.isdir(path): + self.debug_print("os.makedirs('%s', 0o700)" % path) + os.makedirs(path, 0o700) + + if sys.version[:3] in ('2.3', '2.4', '2.5') or 'real_prefix' in sys.__dict__: + sitedir_name = 'site-packages' + else: + sitedir_name = 'dist-packages' + + INSTALL_SCHEMES = dict( + posix=dict( + install_dir='$base/lib/python$py_version_short/site-packages', + script_dir='$base/bin', + ), + unix_local = dict( + install_dir = '$base/local/lib/python$py_version_short/%s' % sitedir_name, + script_dir = '$base/local/bin', + ), + posix_local = dict( + install_dir = '$base/local/lib/python$py_version_short/%s' % sitedir_name, + script_dir = '$base/local/bin', + ), + deb_system = dict( + install_dir = '$base/lib/python3/%s' % sitedir_name, + script_dir = '$base/bin', + ), + ) + + DEFAULT_SCHEME = dict( + install_dir='$base/Lib/site-packages', + script_dir='$base/Scripts', + ) + + def _expand(self, *attrs): + config_vars = self.get_finalized_command('install').config_vars + + if self.prefix or self.install_layout: + if self.install_layout and self.install_layout in ['deb']: + scheme_name = "deb_system" + self.prefix = '/usr' + elif self.prefix or 'real_prefix' in sys.__dict__: + scheme_name = os.name + else: + scheme_name = "posix_local" + # Set default install_dir/scripts from --prefix + config_vars = config_vars.copy() + config_vars['base'] = self.prefix + scheme = self.INSTALL_SCHEMES.get(scheme_name,self.DEFAULT_SCHEME) + for attr, val in scheme.items(): + if getattr(self, attr, None) is None: + setattr(self, attr, val) + + from distutils.util import subst_vars + + for attr in attrs: + val = getattr(self, attr) + if val is not None: + val = subst_vars(val, config_vars) + if os.name == 'posix': + val = os.path.expanduser(val) + setattr(self, attr, val) + + +def _pythonpath(): + items = os.environ.get('PYTHONPATH', '').split(os.pathsep) + return filter(None, items) + + +def get_site_dirs(): + """ + Return a list of 'site' dirs + """ + + sitedirs = [] + + # start with PYTHONPATH + sitedirs.extend(_pythonpath()) + + prefixes = [sys.prefix] + if sys.exec_prefix != sys.prefix: + prefixes.append(sys.exec_prefix) + for prefix in prefixes: + if prefix: + if sys.platform in ('os2emx', 'riscos'): + sitedirs.append(os.path.join(prefix, "Lib", "site-packages")) + elif os.sep == '/': + sitedirs.extend([ + os.path.join( + prefix, + "local/lib", + "python" + sys.version[:3], + "dist-packages", + ), + os.path.join( + prefix, + "lib", + "python{}.{}".format(*sys.version_info), + "dist-packages", + ), + os.path.join(prefix, "lib", "site-python"), + ]) + else: + sitedirs.extend([ + prefix, + os.path.join(prefix, "lib", "site-packages"), + ]) + if sys.platform == 'darwin': + # for framework builds *only* we add the standard Apple + # locations. Currently only per-user, but /Library and + # /Network/Library could be added too + if 'Python.framework' in prefix: + home = os.environ.get('HOME') + if home: + home_sp = os.path.join( + home, + 'Library', + 'Python', + '{}.{}'.format(*sys.version_info), + 'site-packages', + ) + sitedirs.append(home_sp) + lib_paths = get_path('purelib'), get_path('platlib') + for site_lib in lib_paths: + if site_lib not in sitedirs: + sitedirs.append(site_lib) + + if site.ENABLE_USER_SITE: + sitedirs.append(site.USER_SITE) + + try: + sitedirs.extend(site.getsitepackages()) + except AttributeError: + pass + + sitedirs = list(map(normalize_path, sitedirs)) + + return sitedirs + + +def expand_paths(inputs): + """Yield sys.path directories that might contain "old-style" packages""" + + seen = {} + + for dirname in inputs: + dirname = normalize_path(dirname) + if dirname in seen: + continue + + seen[dirname] = 1 + if not os.path.isdir(dirname): + continue + + files = os.listdir(dirname) + yield dirname, files + + for name in files: + if not name.endswith('.pth'): + # We only care about the .pth files + continue + if name in ('easy-install.pth', 'setuptools.pth'): + # Ignore .pth files that we control + continue + + # Read the .pth file + f = open(os.path.join(dirname, name)) + lines = list(yield_lines(f)) + f.close() + + # Yield existing non-dupe, non-import directory lines from it + for line in lines: + if not line.startswith("import"): + line = normalize_path(line.rstrip()) + if line not in seen: + seen[line] = 1 + if not os.path.isdir(line): + continue + yield line, os.listdir(line) + + +def extract_wininst_cfg(dist_filename): + """Extract configuration data from a bdist_wininst .exe + + Returns a configparser.RawConfigParser, or None + """ + f = open(dist_filename, 'rb') + try: + endrec = zipfile._EndRecData(f) + if endrec is None: + return None + + prepended = (endrec[9] - endrec[5]) - endrec[6] + if prepended < 12: # no wininst data here + return None + f.seek(prepended - 12) + + tag, cfglen, bmlen = struct.unpack("egg path translations for a given .exe file""" + + prefixes = [ + ('PURELIB/', ''), + ('PLATLIB/pywin32_system32', ''), + ('PLATLIB/', ''), + ('SCRIPTS/', 'EGG-INFO/scripts/'), + ('DATA/lib/site-packages', ''), + ] + z = zipfile.ZipFile(exe_filename) + try: + for info in z.infolist(): + name = info.filename + parts = name.split('/') + if len(parts) == 3 and parts[2] == 'PKG-INFO': + if parts[1].endswith('.egg-info'): + prefixes.insert(0, ('/'.join(parts[:2]), 'EGG-INFO/')) + break + if len(parts) != 2 or not name.endswith('.pth'): + continue + if name.endswith('-nspkg.pth'): + continue + if parts[0].upper() in ('PURELIB', 'PLATLIB'): + contents = z.read(name) + if not six.PY2: + contents = contents.decode() + for pth in yield_lines(contents): + pth = pth.strip().replace('\\', '/') + if not pth.startswith('import'): + prefixes.append((('%s/%s/' % (parts[0], pth)), '')) + finally: + z.close() + prefixes = [(x.lower(), y) for x, y in prefixes] + prefixes.sort() + prefixes.reverse() + return prefixes + + +class PthDistributions(Environment): + """A .pth file with Distribution paths in it""" + + dirty = False + + def __init__(self, filename, sitedirs=()): + self.filename = filename + self.sitedirs = list(map(normalize_path, sitedirs)) + self.basedir = normalize_path(os.path.dirname(self.filename)) + self._load() + Environment.__init__(self, [], None, None) + for path in yield_lines(self.paths): + list(map(self.add, find_distributions(path, True))) + + def _load(self): + self.paths = [] + saw_import = False + seen = dict.fromkeys(self.sitedirs) + if os.path.isfile(self.filename): + f = open(self.filename, 'rt') + for line in f: + if line.startswith('import'): + saw_import = True + continue + path = line.rstrip() + self.paths.append(path) + if not path.strip() or path.strip().startswith('#'): + continue + # skip non-existent paths, in case somebody deleted a package + # manually, and duplicate paths as well + path = self.paths[-1] = normalize_path( + os.path.join(self.basedir, path) + ) + if not os.path.exists(path) or path in seen: + self.paths.pop() # skip it + self.dirty = True # we cleaned up, so we're dirty now :) + continue + seen[path] = 1 + f.close() + + if self.paths and not saw_import: + self.dirty = True # ensure anything we touch has import wrappers + while self.paths and not self.paths[-1].strip(): + self.paths.pop() + + def save(self): + """Write changed .pth file back to disk""" + if not self.dirty: + return + + rel_paths = list(map(self.make_relative, self.paths)) + if rel_paths: + log.debug("Saving %s", self.filename) + lines = self._wrap_lines(rel_paths) + data = '\n'.join(lines) + '\n' + + if os.path.islink(self.filename): + os.unlink(self.filename) + with open(self.filename, 'wt') as f: + f.write(data) + + elif os.path.exists(self.filename): + log.debug("Deleting empty %s", self.filename) + os.unlink(self.filename) + + self.dirty = False + + @staticmethod + def _wrap_lines(lines): + return lines + + def add(self, dist): + """Add `dist` to the distribution map""" + new_path = ( + dist.location not in self.paths and ( + dist.location not in self.sitedirs or + # account for '.' being in PYTHONPATH + dist.location == os.getcwd() + ) + ) + if new_path: + self.paths.append(dist.location) + self.dirty = True + Environment.add(self, dist) + + def remove(self, dist): + """Remove `dist` from the distribution map""" + while dist.location in self.paths: + self.paths.remove(dist.location) + self.dirty = True + Environment.remove(self, dist) + + def make_relative(self, path): + npath, last = os.path.split(normalize_path(path)) + baselen = len(self.basedir) + parts = [last] + sep = os.altsep == '/' and '/' or os.sep + while len(npath) >= baselen: + if npath == self.basedir: + parts.append(os.curdir) + parts.reverse() + return sep.join(parts) + npath, last = os.path.split(npath) + parts.append(last) + else: + return path + + +class RewritePthDistributions(PthDistributions): + @classmethod + def _wrap_lines(cls, lines): + yield cls.prelude + for line in lines: + yield line + yield cls.postlude + + prelude = _one_liner(""" + import sys + sys.__plen = len(sys.path) + """) + postlude = _one_liner(""" + import sys + new = sys.path[sys.__plen:] + del sys.path[sys.__plen:] + p = getattr(sys, '__egginsert', 0) + sys.path[p:p] = new + sys.__egginsert = p + len(new) + """) + + +if os.environ.get('SETUPTOOLS_SYS_PATH_TECHNIQUE', 'raw') == 'rewrite': + PthDistributions = RewritePthDistributions + + +def _first_line_re(): + """ + Return a regular expression based on first_line_re suitable for matching + strings. + """ + if isinstance(first_line_re.pattern, str): + return first_line_re + + # first_line_re in Python >=3.1.4 and >=3.2.1 is a bytes pattern. + return re.compile(first_line_re.pattern.decode()) + + +def auto_chmod(func, arg, exc): + if func in [os.unlink, os.remove] and os.name == 'nt': + chmod(arg, stat.S_IWRITE) + return func(arg) + et, ev, _ = sys.exc_info() + six.reraise(et, (ev[0], ev[1] + (" %s %s" % (func, arg)))) + + +def update_dist_caches(dist_path, fix_zipimporter_caches): + """ + Fix any globally cached `dist_path` related data + + `dist_path` should be a path of a newly installed egg distribution (zipped + or unzipped). + + sys.path_importer_cache contains finder objects that have been cached when + importing data from the original distribution. Any such finders need to be + cleared since the replacement distribution might be packaged differently, + e.g. a zipped egg distribution might get replaced with an unzipped egg + folder or vice versa. Having the old finders cached may then cause Python + to attempt loading modules from the replacement distribution using an + incorrect loader. + + zipimport.zipimporter objects are Python loaders charged with importing + data packaged inside zip archives. If stale loaders referencing the + original distribution, are left behind, they can fail to load modules from + the replacement distribution. E.g. if an old zipimport.zipimporter instance + is used to load data from a new zipped egg archive, it may cause the + operation to attempt to locate the requested data in the wrong location - + one indicated by the original distribution's zip archive directory + information. Such an operation may then fail outright, e.g. report having + read a 'bad local file header', or even worse, it may fail silently & + return invalid data. + + zipimport._zip_directory_cache contains cached zip archive directory + information for all existing zipimport.zipimporter instances and all such + instances connected to the same archive share the same cached directory + information. + + If asked, and the underlying Python implementation allows it, we can fix + all existing zipimport.zipimporter instances instead of having to track + them down and remove them one by one, by updating their shared cached zip + archive directory information. This, of course, assumes that the + replacement distribution is packaged as a zipped egg. + + If not asked to fix existing zipimport.zipimporter instances, we still do + our best to clear any remaining zipimport.zipimporter related cached data + that might somehow later get used when attempting to load data from the new + distribution and thus cause such load operations to fail. Note that when + tracking down such remaining stale data, we can not catch every conceivable + usage from here, and we clear only those that we know of and have found to + cause problems if left alive. Any remaining caches should be updated by + whomever is in charge of maintaining them, i.e. they should be ready to + handle us replacing their zip archives with new distributions at runtime. + + """ + # There are several other known sources of stale zipimport.zipimporter + # instances that we do not clear here, but might if ever given a reason to + # do so: + # * Global setuptools pkg_resources.working_set (a.k.a. 'master working + # set') may contain distributions which may in turn contain their + # zipimport.zipimporter loaders. + # * Several zipimport.zipimporter loaders held by local variables further + # up the function call stack when running the setuptools installation. + # * Already loaded modules may have their __loader__ attribute set to the + # exact loader instance used when importing them. Python 3.4 docs state + # that this information is intended mostly for introspection and so is + # not expected to cause us problems. + normalized_path = normalize_path(dist_path) + _uncache(normalized_path, sys.path_importer_cache) + if fix_zipimporter_caches: + _replace_zip_directory_cache_data(normalized_path) + else: + # Here, even though we do not want to fix existing and now stale + # zipimporter cache information, we still want to remove it. Related to + # Python's zip archive directory information cache, we clear each of + # its stale entries in two phases: + # 1. Clear the entry so attempting to access zip archive information + # via any existing stale zipimport.zipimporter instances fails. + # 2. Remove the entry from the cache so any newly constructed + # zipimport.zipimporter instances do not end up using old stale + # zip archive directory information. + # This whole stale data removal step does not seem strictly necessary, + # but has been left in because it was done before we started replacing + # the zip archive directory information cache content if possible, and + # there are no relevant unit tests that we can depend on to tell us if + # this is really needed. + _remove_and_clear_zip_directory_cache_data(normalized_path) + + +def _collect_zipimporter_cache_entries(normalized_path, cache): + """ + Return zipimporter cache entry keys related to a given normalized path. + + Alternative path spellings (e.g. those using different character case or + those using alternative path separators) related to the same path are + included. Any sub-path entries are included as well, i.e. those + corresponding to zip archives embedded in other zip archives. + + """ + result = [] + prefix_len = len(normalized_path) + for p in cache: + np = normalize_path(p) + if (np.startswith(normalized_path) and + np[prefix_len:prefix_len + 1] in (os.sep, '')): + result.append(p) + return result + + +def _update_zipimporter_cache(normalized_path, cache, updater=None): + """ + Update zipimporter cache data for a given normalized path. + + Any sub-path entries are processed as well, i.e. those corresponding to zip + archives embedded in other zip archives. + + Given updater is a callable taking a cache entry key and the original entry + (after already removing the entry from the cache), and expected to update + the entry and possibly return a new one to be inserted in its place. + Returning None indicates that the entry should not be replaced with a new + one. If no updater is given, the cache entries are simply removed without + any additional processing, the same as if the updater simply returned None. + + """ + for p in _collect_zipimporter_cache_entries(normalized_path, cache): + # N.B. pypy's custom zipimport._zip_directory_cache implementation does + # not support the complete dict interface: + # * Does not support item assignment, thus not allowing this function + # to be used only for removing existing cache entries. + # * Does not support the dict.pop() method, forcing us to use the + # get/del patterns instead. For more detailed information see the + # following links: + # https://github.com/pypa/setuptools/issues/202#issuecomment-202913420 + # http://bit.ly/2h9itJX + old_entry = cache[p] + del cache[p] + new_entry = updater and updater(p, old_entry) + if new_entry is not None: + cache[p] = new_entry + + +def _uncache(normalized_path, cache): + _update_zipimporter_cache(normalized_path, cache) + + +def _remove_and_clear_zip_directory_cache_data(normalized_path): + def clear_and_remove_cached_zip_archive_directory_data(path, old_entry): + old_entry.clear() + + _update_zipimporter_cache( + normalized_path, zipimport._zip_directory_cache, + updater=clear_and_remove_cached_zip_archive_directory_data) + + +# PyPy Python implementation does not allow directly writing to the +# zipimport._zip_directory_cache and so prevents us from attempting to correct +# its content. The best we can do there is clear the problematic cache content +# and have PyPy repopulate it as needed. The downside is that if there are any +# stale zipimport.zipimporter instances laying around, attempting to use them +# will fail due to not having its zip archive directory information available +# instead of being automatically corrected to use the new correct zip archive +# directory information. +if '__pypy__' in sys.builtin_module_names: + _replace_zip_directory_cache_data = \ + _remove_and_clear_zip_directory_cache_data +else: + + def _replace_zip_directory_cache_data(normalized_path): + def replace_cached_zip_archive_directory_data(path, old_entry): + # N.B. In theory, we could load the zip directory information just + # once for all updated path spellings, and then copy it locally and + # update its contained path strings to contain the correct + # spelling, but that seems like a way too invasive move (this cache + # structure is not officially documented anywhere and could in + # theory change with new Python releases) for no significant + # benefit. + old_entry.clear() + zipimport.zipimporter(path) + old_entry.update(zipimport._zip_directory_cache[path]) + return old_entry + + _update_zipimporter_cache( + normalized_path, zipimport._zip_directory_cache, + updater=replace_cached_zip_archive_directory_data) + + +def is_python(text, filename=''): + "Is this string a valid Python script?" + try: + compile(text, filename, 'exec') + except (SyntaxError, TypeError): + return False + else: + return True + + +def is_sh(executable): + """Determine if the specified executable is a .sh (contains a #! line)""" + try: + with io.open(executable, encoding='latin-1') as fp: + magic = fp.read(2) + except (OSError, IOError): + return executable + return magic == '#!' + + +def nt_quote_arg(arg): + """Quote a command line argument according to Windows parsing rules""" + return subprocess.list2cmdline([arg]) + + +def is_python_script(script_text, filename): + """Is this text, as a whole, a Python script? (as opposed to shell/bat/etc. + """ + if filename.endswith('.py') or filename.endswith('.pyw'): + return True # extension says it's Python + if is_python(script_text, filename): + return True # it's syntactically valid Python + if script_text.startswith('#!'): + # It begins with a '#!' line, so check if 'python' is in it somewhere + return 'python' in script_text.splitlines()[0].lower() + + return False # Not any Python I can recognize + + +try: + from os import chmod as _chmod +except ImportError: + # Jython compatibility + def _chmod(*args): + pass + + +def chmod(path, mode): + log.debug("changing mode of %s to %o", path, mode) + try: + _chmod(path, mode) + except os.error as e: + log.debug("chmod failed: %s", e) + + +class CommandSpec(list): + """ + A command spec for a #! header, specified as a list of arguments akin to + those passed to Popen. + """ + + options = [] + split_args = dict() + + @classmethod + def best(cls): + """ + Choose the best CommandSpec class based on environmental conditions. + """ + return cls + + @classmethod + def _sys_executable(cls): + _default = os.path.normpath(sys.executable) + return os.environ.get('__PYVENV_LAUNCHER__', _default) + + @classmethod + def from_param(cls, param): + """ + Construct a CommandSpec from a parameter to build_scripts, which may + be None. + """ + if isinstance(param, cls): + return param + if isinstance(param, list): + return cls(param) + if param is None: + return cls.from_environment() + # otherwise, assume it's a string. + return cls.from_string(param) + + @classmethod + def from_environment(cls): + return cls([cls._sys_executable()]) + + @classmethod + def from_string(cls, string): + """ + Construct a command spec from a simple string representing a command + line parseable by shlex.split. + """ + items = shlex.split(string, **cls.split_args) + return cls(items) + + def install_options(self, script_text): + self.options = shlex.split(self._extract_options(script_text)) + cmdline = subprocess.list2cmdline(self) + if not isascii(cmdline): + self.options[:0] = ['-x'] + + @staticmethod + def _extract_options(orig_script): + """ + Extract any options from the first line of the script. + """ + first = (orig_script + '\n').splitlines()[0] + match = _first_line_re().match(first) + options = match.group(1) or '' if match else '' + return options.strip() + + def as_header(self): + return self._render(self + list(self.options)) + + @staticmethod + def _strip_quotes(item): + _QUOTES = '"\'' + for q in _QUOTES: + if item.startswith(q) and item.endswith(q): + return item[1:-1] + return item + + @staticmethod + def _render(items): + cmdline = subprocess.list2cmdline( + CommandSpec._strip_quotes(item.strip()) for item in items) + return '#!' + cmdline + '\n' + + +# For pbr compat; will be removed in a future version. +sys_executable = CommandSpec._sys_executable() + + +class WindowsCommandSpec(CommandSpec): + split_args = dict(posix=False) + + +class ScriptWriter: + """ + Encapsulates behavior around writing entry point scripts for console and + gui apps. + """ + + template = textwrap.dedent(r""" + # EASY-INSTALL-ENTRY-SCRIPT: %(spec)r,%(group)r,%(name)r + __requires__ = %(spec)r + import re + import sys + from pkg_resources import load_entry_point + + if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit( + load_entry_point(%(spec)r, %(group)r, %(name)r)() + ) + """).lstrip() + + command_spec_class = CommandSpec + + @classmethod + def get_script_args(cls, dist, executable=None, wininst=False): + # for backward compatibility + warnings.warn("Use get_args", EasyInstallDeprecationWarning) + writer = (WindowsScriptWriter if wininst else ScriptWriter).best() + header = cls.get_script_header("", executable, wininst) + return writer.get_args(dist, header) + + @classmethod + def get_script_header(cls, script_text, executable=None, wininst=False): + # for backward compatibility + warnings.warn("Use get_header", EasyInstallDeprecationWarning, stacklevel=2) + if wininst: + executable = "python.exe" + return cls.get_header(script_text, executable) + + @classmethod + def get_args(cls, dist, header=None): + """ + Yield write_script() argument tuples for a distribution's + console_scripts and gui_scripts entry points. + """ + if header is None: + header = cls.get_header() + spec = str(dist.as_requirement()) + for type_ in 'console', 'gui': + group = type_ + '_scripts' + for name, ep in dist.get_entry_map(group).items(): + cls._ensure_safe_name(name) + script_text = cls.template % locals() + args = cls._get_script_args(type_, name, header, script_text) + for res in args: + yield res + + @staticmethod + def _ensure_safe_name(name): + """ + Prevent paths in *_scripts entry point names. + """ + has_path_sep = re.search(r'[\\/]', name) + if has_path_sep: + raise ValueError("Path separators not allowed in script names") + + @classmethod + def get_writer(cls, force_windows): + # for backward compatibility + warnings.warn("Use best", EasyInstallDeprecationWarning) + return WindowsScriptWriter.best() if force_windows else cls.best() + + @classmethod + def best(cls): + """ + Select the best ScriptWriter for this environment. + """ + if sys.platform == 'win32' or (os.name == 'java' and os._name == 'nt'): + return WindowsScriptWriter.best() + else: + return cls + + @classmethod + def _get_script_args(cls, type_, name, header, script_text): + # Simply write the stub with no extension. + yield (name, header + script_text) + + @classmethod + def get_header(cls, script_text="", executable=None): + """Create a #! line, getting options (if any) from script_text""" + cmd = cls.command_spec_class.best().from_param(executable) + cmd.install_options(script_text) + return cmd.as_header() + + +class WindowsScriptWriter(ScriptWriter): + command_spec_class = WindowsCommandSpec + + @classmethod + def get_writer(cls): + # for backward compatibility + warnings.warn("Use best", EasyInstallDeprecationWarning) + return cls.best() + + @classmethod + def best(cls): + """ + Select the best ScriptWriter suitable for Windows + """ + writer_lookup = dict( + executable=WindowsExecutableLauncherWriter, + natural=cls, + ) + # for compatibility, use the executable launcher by default + launcher = os.environ.get('SETUPTOOLS_LAUNCHER', 'executable') + return writer_lookup[launcher] + + @classmethod + def _get_script_args(cls, type_, name, header, script_text): + "For Windows, add a .py extension" + ext = dict(console='.pya', gui='.pyw')[type_] + if ext not in os.environ['PATHEXT'].lower().split(';'): + msg = ( + "{ext} not listed in PATHEXT; scripts will not be " + "recognized as executables." + ).format(**locals()) + warnings.warn(msg, UserWarning) + old = ['.pya', '.py', '-script.py', '.pyc', '.pyo', '.pyw', '.exe'] + old.remove(ext) + header = cls._adjust_header(type_, header) + blockers = [name + x for x in old] + yield name + ext, header + script_text, 't', blockers + + @classmethod + def _adjust_header(cls, type_, orig_header): + """ + Make sure 'pythonw' is used for gui and and 'python' is used for + console (regardless of what sys.executable is). + """ + pattern = 'pythonw.exe' + repl = 'python.exe' + if type_ == 'gui': + pattern, repl = repl, pattern + pattern_ob = re.compile(re.escape(pattern), re.IGNORECASE) + new_header = pattern_ob.sub(string=orig_header, repl=repl) + return new_header if cls._use_header(new_header) else orig_header + + @staticmethod + def _use_header(new_header): + """ + Should _adjust_header use the replaced header? + + On non-windows systems, always use. On + Windows systems, only use the replaced header if it resolves + to an executable on the system. + """ + clean_header = new_header[2:-1].strip('"') + return sys.platform != 'win32' or find_executable(clean_header) + + +class WindowsExecutableLauncherWriter(WindowsScriptWriter): + @classmethod + def _get_script_args(cls, type_, name, header, script_text): + """ + For Windows, add a .py extension and an .exe launcher + """ + if type_ == 'gui': + launcher_type = 'gui' + ext = '-script.pyw' + old = ['.pyw'] + else: + launcher_type = 'cli' + ext = '-script.py' + old = ['.py', '.pyc', '.pyo'] + hdr = cls._adjust_header(type_, header) + blockers = [name + x for x in old] + yield (name + ext, hdr + script_text, 't', blockers) + yield ( + name + '.exe', get_win_launcher(launcher_type), + 'b' # write in binary mode + ) + if not is_64bit(): + # install a manifest for the launcher to prevent Windows + # from detecting it as an installer (which it will for + # launchers like easy_install.exe). Consider only + # adding a manifest for launchers detected as installers. + # See Distribute #143 for details. + m_name = name + '.exe.manifest' + yield (m_name, load_launcher_manifest(name), 't') + + +# for backward-compatibility +get_script_args = ScriptWriter.get_script_args +get_script_header = ScriptWriter.get_script_header + + +def get_win_launcher(type): + """ + Load the Windows launcher (executable) suitable for launching a script. + + `type` should be either 'cli' or 'gui' + + Returns the executable as a byte string. + """ + launcher_fn = '%s.exe' % type + if is_64bit(): + launcher_fn = launcher_fn.replace(".", "-64.") + else: + launcher_fn = launcher_fn.replace(".", "-32.") + return resource_string('setuptools', launcher_fn) + + +def load_launcher_manifest(name): + manifest = pkg_resources.resource_string(__name__, 'launcher manifest.xml') + if six.PY2: + return manifest % vars() + else: + return manifest.decode('utf-8') % vars() + + +def rmtree(path, ignore_errors=False, onerror=auto_chmod): + return shutil.rmtree(path, ignore_errors, onerror) + + +def current_umask(): + tmp = os.umask(0o022) + os.umask(tmp) + return tmp + + +def bootstrap(): + # This function is called when setuptools*.egg is run using /bin/sh + import setuptools + + argv0 = os.path.dirname(setuptools.__path__[0]) + sys.argv[0] = argv0 + sys.argv.append(argv0) + main() + + +def main(argv=None, **kw): + from setuptools import setup + from setuptools.dist import Distribution + + class DistributionWithoutHelpCommands(Distribution): + common_usage = "" + + def _show_help(self, *args, **kw): + with _patch_usage(): + Distribution._show_help(self, *args, **kw) + + if argv is None: + argv = sys.argv[1:] + + with _patch_usage(): + setup( + script_args=['-q', 'easy_install', '-v'] + argv, + script_name=sys.argv[0] or 'easy_install', + distclass=DistributionWithoutHelpCommands, + **kw + ) + + +@contextlib.contextmanager +def _patch_usage(): + import distutils.core + USAGE = textwrap.dedent(""" + usage: %(script)s [options] requirement_or_url ... + or: %(script)s --help + """).lstrip() + + def gen_usage(script_name): + return USAGE % dict( + script=os.path.basename(script_name), + ) + + saved = distutils.core.gen_usage + distutils.core.gen_usage = gen_usage + try: + yield + finally: + distutils.core.gen_usage = saved + +class EasyInstallDeprecationWarning(SetuptoolsDeprecationWarning): + """Class for warning about deprecations in EasyInstall in SetupTools. Not ignored by default, unlike DeprecationWarning.""" + diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/egg_info.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/egg_info.py new file mode 100644 index 0000000000..13714d6b36 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/egg_info.py @@ -0,0 +1,717 @@ +"""setuptools.command.egg_info + +Create a distribution's .egg-info directory and contents""" + +from distutils.filelist import FileList as _FileList +from distutils.errors import DistutilsInternalError +from distutils.util import convert_path +from distutils import log +import distutils.errors +import distutils.filelist +import os +import re +import sys +import io +import warnings +import time +import collections + +from setuptools.extern import six +from setuptools.extern.six.moves import map + +from setuptools import Command +from setuptools.command.sdist import sdist +from setuptools.command.sdist import walk_revctrl +from setuptools.command.setopt import edit_config +from setuptools.command import bdist_egg +from pkg_resources import ( + parse_requirements, safe_name, parse_version, + safe_version, yield_lines, EntryPoint, iter_entry_points, to_filename) +import setuptools.unicode_utils as unicode_utils +from setuptools.glob import glob + +from setuptools.extern import packaging +from setuptools import SetuptoolsDeprecationWarning + +def translate_pattern(glob): + """ + Translate a file path glob like '*.txt' in to a regular expression. + This differs from fnmatch.translate which allows wildcards to match + directory separators. It also knows about '**/' which matches any number of + directories. + """ + pat = '' + + # This will split on '/' within [character classes]. This is deliberate. + chunks = glob.split(os.path.sep) + + sep = re.escape(os.sep) + valid_char = '[^%s]' % (sep,) + + for c, chunk in enumerate(chunks): + last_chunk = c == len(chunks) - 1 + + # Chunks that are a literal ** are globstars. They match anything. + if chunk == '**': + if last_chunk: + # Match anything if this is the last component + pat += '.*' + else: + # Match '(name/)*' + pat += '(?:%s+%s)*' % (valid_char, sep) + continue # Break here as the whole path component has been handled + + # Find any special characters in the remainder + i = 0 + chunk_len = len(chunk) + while i < chunk_len: + char = chunk[i] + if char == '*': + # Match any number of name characters + pat += valid_char + '*' + elif char == '?': + # Match a name character + pat += valid_char + elif char == '[': + # Character class + inner_i = i + 1 + # Skip initial !/] chars + if inner_i < chunk_len and chunk[inner_i] == '!': + inner_i = inner_i + 1 + if inner_i < chunk_len and chunk[inner_i] == ']': + inner_i = inner_i + 1 + + # Loop till the closing ] is found + while inner_i < chunk_len and chunk[inner_i] != ']': + inner_i = inner_i + 1 + + if inner_i >= chunk_len: + # Got to the end of the string without finding a closing ] + # Do not treat this as a matching group, but as a literal [ + pat += re.escape(char) + else: + # Grab the insides of the [brackets] + inner = chunk[i + 1:inner_i] + char_class = '' + + # Class negation + if inner[0] == '!': + char_class = '^' + inner = inner[1:] + + char_class += re.escape(inner) + pat += '[%s]' % (char_class,) + + # Skip to the end ] + i = inner_i + else: + pat += re.escape(char) + i += 1 + + # Join each chunk with the dir separator + if not last_chunk: + pat += sep + + pat += r'\Z' + return re.compile(pat, flags=re.MULTILINE|re.DOTALL) + + +class InfoCommon: + tag_build = None + tag_date = None + + @property + def name(self): + return safe_name(self.distribution.get_name()) + + def tagged_version(self): + version = self.distribution.get_version() + # egg_info may be called more than once for a distribution, + # in which case the version string already contains all tags. + if self.vtags and version.endswith(self.vtags): + return safe_version(version) + return safe_version(version + self.vtags) + + def tags(self): + version = '' + if self.tag_build: + version += self.tag_build + if self.tag_date: + version += time.strftime("-%Y%m%d") + return version + vtags = property(tags) + + +class egg_info(InfoCommon, Command): + description = "create a distribution's .egg-info directory" + + user_options = [ + ('egg-base=', 'e', "directory containing .egg-info directories" + " (default: top of the source tree)"), + ('tag-date', 'd', "Add date stamp (e.g. 20050528) to version number"), + ('tag-build=', 'b', "Specify explicit tag to add to version number"), + ('no-date', 'D', "Don't include date stamp [default]"), + ] + + boolean_options = ['tag-date'] + negative_opt = { + 'no-date': 'tag-date', + } + + def initialize_options(self): + self.egg_base = None + self.egg_name = None + self.egg_info = None + self.egg_version = None + self.broken_egg_info = False + + #################################### + # allow the 'tag_svn_revision' to be detected and + # set, supporting sdists built on older Setuptools. + @property + def tag_svn_revision(self): + pass + + @tag_svn_revision.setter + def tag_svn_revision(self, value): + pass + #################################### + + def save_version_info(self, filename): + """ + Materialize the value of date into the + build tag. Install build keys in a deterministic order + to avoid arbitrary reordering on subsequent builds. + """ + egg_info = collections.OrderedDict() + # follow the order these keys would have been added + # when PYTHONHASHSEED=0 + egg_info['tag_build'] = self.tags() + egg_info['tag_date'] = 0 + edit_config(filename, dict(egg_info=egg_info)) + + def finalize_options(self): + # Note: we need to capture the current value returned + # by `self.tagged_version()`, so we can later update + # `self.distribution.metadata.version` without + # repercussions. + self.egg_name = self.name + self.egg_version = self.tagged_version() + parsed_version = parse_version(self.egg_version) + + try: + is_version = isinstance(parsed_version, packaging.version.Version) + spec = ( + "%s==%s" if is_version else "%s===%s" + ) + list( + parse_requirements(spec % (self.egg_name, self.egg_version)) + ) + except ValueError: + raise distutils.errors.DistutilsOptionError( + "Invalid distribution name or version syntax: %s-%s" % + (self.egg_name, self.egg_version) + ) + + if self.egg_base is None: + dirs = self.distribution.package_dir + self.egg_base = (dirs or {}).get('', os.curdir) + + self.ensure_dirname('egg_base') + self.egg_info = to_filename(self.egg_name) + '.egg-info' + if self.egg_base != os.curdir: + self.egg_info = os.path.join(self.egg_base, self.egg_info) + if '-' in self.egg_name: + self.check_broken_egg_info() + + # Set package version for the benefit of dumber commands + # (e.g. sdist, bdist_wininst, etc.) + # + self.distribution.metadata.version = self.egg_version + + # If we bootstrapped around the lack of a PKG-INFO, as might be the + # case in a fresh checkout, make sure that any special tags get added + # to the version info + # + pd = self.distribution._patched_dist + if pd is not None and pd.key == self.egg_name.lower(): + pd._version = self.egg_version + pd._parsed_version = parse_version(self.egg_version) + self.distribution._patched_dist = None + + def write_or_delete_file(self, what, filename, data, force=False): + """Write `data` to `filename` or delete if empty + + If `data` is non-empty, this routine is the same as ``write_file()``. + If `data` is empty but not ``None``, this is the same as calling + ``delete_file(filename)`. If `data` is ``None``, then this is a no-op + unless `filename` exists, in which case a warning is issued about the + orphaned file (if `force` is false), or deleted (if `force` is true). + """ + if data: + self.write_file(what, filename, data) + elif os.path.exists(filename): + if data is None and not force: + log.warn( + "%s not set in setup(), but %s exists", what, filename + ) + return + else: + self.delete_file(filename) + + def write_file(self, what, filename, data): + """Write `data` to `filename` (if not a dry run) after announcing it + + `what` is used in a log message to identify what is being written + to the file. + """ + log.info("writing %s to %s", what, filename) + if not six.PY2: + data = data.encode("utf-8") + if not self.dry_run: + f = open(filename, 'wb') + f.write(data) + f.close() + + def delete_file(self, filename): + """Delete `filename` (if not a dry run) after announcing it""" + log.info("deleting %s", filename) + if not self.dry_run: + os.unlink(filename) + + def run(self): + self.mkpath(self.egg_info) + os.utime(self.egg_info, None) + installer = self.distribution.fetch_build_egg + for ep in iter_entry_points('egg_info.writers'): + ep.require(installer=installer) + writer = ep.resolve() + writer(self, ep.name, os.path.join(self.egg_info, ep.name)) + + # Get rid of native_libs.txt if it was put there by older bdist_egg + nl = os.path.join(self.egg_info, "native_libs.txt") + if os.path.exists(nl): + self.delete_file(nl) + + self.find_sources() + + def find_sources(self): + """Generate SOURCES.txt manifest file""" + manifest_filename = os.path.join(self.egg_info, "SOURCES.txt") + mm = manifest_maker(self.distribution) + mm.manifest = manifest_filename + mm.run() + self.filelist = mm.filelist + + def check_broken_egg_info(self): + bei = self.egg_name + '.egg-info' + if self.egg_base != os.curdir: + bei = os.path.join(self.egg_base, bei) + if os.path.exists(bei): + log.warn( + "-" * 78 + '\n' + "Note: Your current .egg-info directory has a '-' in its name;" + '\nthis will not work correctly with "setup.py develop".\n\n' + 'Please rename %s to %s to correct this problem.\n' + '-' * 78, + bei, self.egg_info + ) + self.broken_egg_info = self.egg_info + self.egg_info = bei # make it work for now + + +class FileList(_FileList): + # Implementations of the various MANIFEST.in commands + + def process_template_line(self, line): + # Parse the line: split it up, make sure the right number of words + # is there, and return the relevant words. 'action' is always + # defined: it's the first word of the line. Which of the other + # three are defined depends on the action; it'll be either + # patterns, (dir and patterns), or (dir_pattern). + (action, patterns, dir, dir_pattern) = self._parse_template_line(line) + + # OK, now we know that the action is valid and we have the + # right number of words on the line for that action -- so we + # can proceed with minimal error-checking. + if action == 'include': + self.debug_print("include " + ' '.join(patterns)) + for pattern in patterns: + if not self.include(pattern): + log.warn("warning: no files found matching '%s'", pattern) + + elif action == 'exclude': + self.debug_print("exclude " + ' '.join(patterns)) + for pattern in patterns: + if not self.exclude(pattern): + log.warn(("warning: no previously-included files " + "found matching '%s'"), pattern) + + elif action == 'global-include': + self.debug_print("global-include " + ' '.join(patterns)) + for pattern in patterns: + if not self.global_include(pattern): + log.warn(("warning: no files found matching '%s' " + "anywhere in distribution"), pattern) + + elif action == 'global-exclude': + self.debug_print("global-exclude " + ' '.join(patterns)) + for pattern in patterns: + if not self.global_exclude(pattern): + log.warn(("warning: no previously-included files matching " + "'%s' found anywhere in distribution"), + pattern) + + elif action == 'recursive-include': + self.debug_print("recursive-include %s %s" % + (dir, ' '.join(patterns))) + for pattern in patterns: + if not self.recursive_include(dir, pattern): + log.warn(("warning: no files found matching '%s' " + "under directory '%s'"), + pattern, dir) + + elif action == 'recursive-exclude': + self.debug_print("recursive-exclude %s %s" % + (dir, ' '.join(patterns))) + for pattern in patterns: + if not self.recursive_exclude(dir, pattern): + log.warn(("warning: no previously-included files matching " + "'%s' found under directory '%s'"), + pattern, dir) + + elif action == 'graft': + self.debug_print("graft " + dir_pattern) + if not self.graft(dir_pattern): + log.warn("warning: no directories found matching '%s'", + dir_pattern) + + elif action == 'prune': + self.debug_print("prune " + dir_pattern) + if not self.prune(dir_pattern): + log.warn(("no previously-included directories found " + "matching '%s'"), dir_pattern) + + else: + raise DistutilsInternalError( + "this cannot happen: invalid action '%s'" % action) + + def _remove_files(self, predicate): + """ + Remove all files from the file list that match the predicate. + Return True if any matching files were removed + """ + found = False + for i in range(len(self.files) - 1, -1, -1): + if predicate(self.files[i]): + self.debug_print(" removing " + self.files[i]) + del self.files[i] + found = True + return found + + def include(self, pattern): + """Include files that match 'pattern'.""" + found = [f for f in glob(pattern) if not os.path.isdir(f)] + self.extend(found) + return bool(found) + + def exclude(self, pattern): + """Exclude files that match 'pattern'.""" + match = translate_pattern(pattern) + return self._remove_files(match.match) + + def recursive_include(self, dir, pattern): + """ + Include all files anywhere in 'dir/' that match the pattern. + """ + full_pattern = os.path.join(dir, '**', pattern) + found = [f for f in glob(full_pattern, recursive=True) + if not os.path.isdir(f)] + self.extend(found) + return bool(found) + + def recursive_exclude(self, dir, pattern): + """ + Exclude any file anywhere in 'dir/' that match the pattern. + """ + match = translate_pattern(os.path.join(dir, '**', pattern)) + return self._remove_files(match.match) + + def graft(self, dir): + """Include all files from 'dir/'.""" + found = [ + item + for match_dir in glob(dir) + for item in distutils.filelist.findall(match_dir) + ] + self.extend(found) + return bool(found) + + def prune(self, dir): + """Filter out files from 'dir/'.""" + match = translate_pattern(os.path.join(dir, '**')) + return self._remove_files(match.match) + + def global_include(self, pattern): + """ + Include all files anywhere in the current directory that match the + pattern. This is very inefficient on large file trees. + """ + if self.allfiles is None: + self.findall() + match = translate_pattern(os.path.join('**', pattern)) + found = [f for f in self.allfiles if match.match(f)] + self.extend(found) + return bool(found) + + def global_exclude(self, pattern): + """ + Exclude all files anywhere that match the pattern. + """ + match = translate_pattern(os.path.join('**', pattern)) + return self._remove_files(match.match) + + def append(self, item): + if item.endswith('\r'): # Fix older sdists built on Windows + item = item[:-1] + path = convert_path(item) + + if self._safe_path(path): + self.files.append(path) + + def extend(self, paths): + self.files.extend(filter(self._safe_path, paths)) + + def _repair(self): + """ + Replace self.files with only safe paths + + Because some owners of FileList manipulate the underlying + ``files`` attribute directly, this method must be called to + repair those paths. + """ + self.files = list(filter(self._safe_path, self.files)) + + def _safe_path(self, path): + enc_warn = "'%s' not %s encodable -- skipping" + + # To avoid accidental trans-codings errors, first to unicode + u_path = unicode_utils.filesys_decode(path) + if u_path is None: + log.warn("'%s' in unexpected encoding -- skipping" % path) + return False + + # Must ensure utf-8 encodability + utf8_path = unicode_utils.try_encode(u_path, "utf-8") + if utf8_path is None: + log.warn(enc_warn, path, 'utf-8') + return False + + try: + # accept is either way checks out + if os.path.exists(u_path) or os.path.exists(utf8_path): + return True + # this will catch any encode errors decoding u_path + except UnicodeEncodeError: + log.warn(enc_warn, path, sys.getfilesystemencoding()) + + +class manifest_maker(sdist): + template = "MANIFEST.in" + + def initialize_options(self): + self.use_defaults = 1 + self.prune = 1 + self.manifest_only = 1 + self.force_manifest = 1 + + def finalize_options(self): + pass + + def run(self): + self.filelist = FileList() + if not os.path.exists(self.manifest): + self.write_manifest() # it must exist so it'll get in the list + self.add_defaults() + if os.path.exists(self.template): + self.read_template() + self.prune_file_list() + self.filelist.sort() + self.filelist.remove_duplicates() + self.write_manifest() + + def _manifest_normalize(self, path): + path = unicode_utils.filesys_decode(path) + return path.replace(os.sep, '/') + + def write_manifest(self): + """ + Write the file list in 'self.filelist' to the manifest file + named by 'self.manifest'. + """ + self.filelist._repair() + + # Now _repairs should encodability, but not unicode + files = [self._manifest_normalize(f) for f in self.filelist.files] + msg = "writing manifest file '%s'" % self.manifest + self.execute(write_file, (self.manifest, files), msg) + + def warn(self, msg): + if not self._should_suppress_warning(msg): + sdist.warn(self, msg) + + @staticmethod + def _should_suppress_warning(msg): + """ + suppress missing-file warnings from sdist + """ + return re.match(r"standard file .*not found", msg) + + def add_defaults(self): + sdist.add_defaults(self) + self.check_license() + self.filelist.append(self.template) + self.filelist.append(self.manifest) + rcfiles = list(walk_revctrl()) + if rcfiles: + self.filelist.extend(rcfiles) + elif os.path.exists(self.manifest): + self.read_manifest() + + if os.path.exists("setup.py"): + # setup.py should be included by default, even if it's not + # the script called to create the sdist + self.filelist.append("setup.py") + + ei_cmd = self.get_finalized_command('egg_info') + self.filelist.graft(ei_cmd.egg_info) + + def prune_file_list(self): + build = self.get_finalized_command('build') + base_dir = self.distribution.get_fullname() + self.filelist.prune(build.build_base) + self.filelist.prune(base_dir) + sep = re.escape(os.sep) + self.filelist.exclude_pattern(r'(^|' + sep + r')(RCS|CVS|\.svn)' + sep, + is_regex=1) + + +def write_file(filename, contents): + """Create a file with the specified name and write 'contents' (a + sequence of strings without line terminators) to it. + """ + contents = "\n".join(contents) + + # assuming the contents has been vetted for utf-8 encoding + contents = contents.encode("utf-8") + + with open(filename, "wb") as f: # always write POSIX-style manifest + f.write(contents) + + +def write_pkg_info(cmd, basename, filename): + log.info("writing %s", filename) + if not cmd.dry_run: + metadata = cmd.distribution.metadata + metadata.version, oldver = cmd.egg_version, metadata.version + metadata.name, oldname = cmd.egg_name, metadata.name + + try: + # write unescaped data to PKG-INFO, so older pkg_resources + # can still parse it + metadata.write_pkg_info(cmd.egg_info) + finally: + metadata.name, metadata.version = oldname, oldver + + safe = getattr(cmd.distribution, 'zip_safe', None) + + bdist_egg.write_safety_flag(cmd.egg_info, safe) + + +def warn_depends_obsolete(cmd, basename, filename): + if os.path.exists(filename): + log.warn( + "WARNING: 'depends.txt' is not used by setuptools 0.6!\n" + "Use the install_requires/extras_require setup() args instead." + ) + + +def _write_requirements(stream, reqs): + lines = yield_lines(reqs or ()) + append_cr = lambda line: line + '\n' + lines = map(append_cr, sorted(lines)) + stream.writelines(lines) + + +def write_requirements(cmd, basename, filename): + dist = cmd.distribution + data = six.StringIO() + _write_requirements(data, dist.install_requires) + extras_require = dist.extras_require or {} + for extra in sorted(extras_require): + data.write('\n[{extra}]\n'.format(**vars())) + _write_requirements(data, extras_require[extra]) + cmd.write_or_delete_file("requirements", filename, data.getvalue()) + + +def write_setup_requirements(cmd, basename, filename): + data = io.StringIO() + _write_requirements(data, cmd.distribution.setup_requires) + cmd.write_or_delete_file("setup-requirements", filename, data.getvalue()) + + +def write_toplevel_names(cmd, basename, filename): + pkgs = dict.fromkeys( + [ + k.split('.', 1)[0] + for k in cmd.distribution.iter_distribution_names() + ] + ) + cmd.write_file("top-level names", filename, '\n'.join(sorted(pkgs)) + '\n') + + +def overwrite_arg(cmd, basename, filename): + write_arg(cmd, basename, filename, True) + + +def write_arg(cmd, basename, filename, force=False): + argname = os.path.splitext(basename)[0] + value = getattr(cmd.distribution, argname, None) + if value is not None: + value = '\n'.join(value) + '\n' + cmd.write_or_delete_file(argname, filename, value, force) + + +def write_entries(cmd, basename, filename): + ep = cmd.distribution.entry_points + + if isinstance(ep, six.string_types) or ep is None: + data = ep + elif ep is not None: + data = [] + for section, contents in sorted(ep.items()): + if not isinstance(contents, six.string_types): + contents = EntryPoint.parse_group(section, contents) + contents = '\n'.join(sorted(map(str, contents.values()))) + data.append('[%s]\n%s\n\n' % (section, contents)) + data = ''.join(data) + + cmd.write_or_delete_file('entry points', filename, data, True) + + +def get_pkg_info_revision(): + """ + Get a -r### off of PKG-INFO Version in case this is an sdist of + a subversion revision. + """ + warnings.warn("get_pkg_info_revision is deprecated.", EggInfoDeprecationWarning) + if os.path.exists('PKG-INFO'): + with io.open('PKG-INFO') as f: + for line in f: + match = re.match(r"Version:.*-r(\d+)\s*$", line) + if match: + return int(match.group(1)) + return 0 + + +class EggInfoDeprecationWarning(SetuptoolsDeprecationWarning): + """Class for warning about deprecations in eggInfo in setupTools. Not ignored by default, unlike DeprecationWarning.""" diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/install.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/install.py new file mode 100644 index 0000000000..72b9a3e424 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/install.py @@ -0,0 +1,125 @@ +from distutils.errors import DistutilsArgError +import inspect +import glob +import warnings +import platform +import distutils.command.install as orig + +import setuptools + +# Prior to numpy 1.9, NumPy relies on the '_install' name, so provide it for +# now. See https://github.com/pypa/setuptools/issues/199/ +_install = orig.install + + +class install(orig.install): + """Use easy_install to install the package, w/dependencies""" + + user_options = orig.install.user_options + [ + ('old-and-unmanageable', None, "Try not to use this!"), + ('single-version-externally-managed', None, + "used by system package builders to create 'flat' eggs"), + ] + boolean_options = orig.install.boolean_options + [ + 'old-and-unmanageable', 'single-version-externally-managed', + ] + new_commands = [ + ('install_egg_info', lambda self: True), + ('install_scripts', lambda self: True), + ] + _nc = dict(new_commands) + + def initialize_options(self): + orig.install.initialize_options(self) + self.old_and_unmanageable = None + self.single_version_externally_managed = None + + def finalize_options(self): + orig.install.finalize_options(self) + if self.root: + self.single_version_externally_managed = True + elif self.single_version_externally_managed: + if not self.root and not self.record: + raise DistutilsArgError( + "You must specify --record or --root when building system" + " packages" + ) + + def handle_extra_path(self): + if self.root or self.single_version_externally_managed: + # explicit backward-compatibility mode, allow extra_path to work + return orig.install.handle_extra_path(self) + + # Ignore extra_path when installing an egg (or being run by another + # command without --root or --single-version-externally-managed + self.path_file = None + self.extra_dirs = '' + + def run(self): + # Explicit request for old-style install? Just do it + if self.old_and_unmanageable or self.single_version_externally_managed: + return orig.install.run(self) + + if not self._called_from_setup(inspect.currentframe()): + # Run in backward-compatibility mode to support bdist_* commands. + orig.install.run(self) + else: + self.do_egg_install() + + @staticmethod + def _called_from_setup(run_frame): + """ + Attempt to detect whether run() was called from setup() or by another + command. If called by setup(), the parent caller will be the + 'run_command' method in 'distutils.dist', and *its* caller will be + the 'run_commands' method. If called any other way, the + immediate caller *might* be 'run_command', but it won't have been + called by 'run_commands'. Return True in that case or if a call stack + is unavailable. Return False otherwise. + """ + if run_frame is None: + msg = "Call stack not available. bdist_* commands may fail." + warnings.warn(msg) + if platform.python_implementation() == 'IronPython': + msg = "For best results, pass -X:Frames to enable call stack." + warnings.warn(msg) + return True + res = inspect.getouterframes(run_frame)[2] + caller, = res[:1] + info = inspect.getframeinfo(caller) + caller_module = caller.f_globals.get('__name__', '') + return ( + caller_module == 'distutils.dist' + and info.function == 'run_commands' + ) + + def do_egg_install(self): + + easy_install = self.distribution.get_command_class('easy_install') + + cmd = easy_install( + self.distribution, args="x", root=self.root, record=self.record, + ) + cmd.ensure_finalized() # finalize before bdist_egg munges install cmd + cmd.always_copy_from = '.' # make sure local-dir eggs get installed + + # pick up setup-dir .egg files only: no .egg-info + cmd.package_index.scan(glob.glob('*.egg')) + + self.run_command('bdist_egg') + args = [self.distribution.get_command_obj('bdist_egg').egg_output] + + if setuptools.bootstrap_install_from: + # Bootstrap self-installation of setuptools + args.insert(0, setuptools.bootstrap_install_from) + + cmd.args = args + cmd.run(show_deprecation=False) + setuptools.bootstrap_install_from = None + + +# XXX Python 3.1 doesn't see _nc if this is inside the class +install.sub_commands = ( + [cmd for cmd in orig.install.sub_commands if cmd[0] not in install._nc] + + install.new_commands +) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/install_egg_info.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/install_egg_info.py new file mode 100644 index 0000000000..5f405bcad7 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/install_egg_info.py @@ -0,0 +1,82 @@ +from distutils import log, dir_util +import os, sys + +from setuptools import Command +from setuptools import namespaces +from setuptools.archive_util import unpack_archive +import pkg_resources + + +class install_egg_info(namespaces.Installer, Command): + """Install an .egg-info directory for the package""" + + description = "Install an .egg-info directory for the package" + + user_options = [ + ('install-dir=', 'd', "directory to install to"), + ] + + def initialize_options(self): + self.install_dir = None + self.install_layout = None + self.prefix_option = None + + def finalize_options(self): + self.set_undefined_options('install_lib', + ('install_dir', 'install_dir')) + self.set_undefined_options('install',('install_layout','install_layout')) + if sys.hexversion > 0x2060000: + self.set_undefined_options('install',('prefix_option','prefix_option')) + ei_cmd = self.get_finalized_command("egg_info") + basename = pkg_resources.Distribution( + None, None, ei_cmd.egg_name, ei_cmd.egg_version + ).egg_name() + '.egg-info' + + if self.install_layout: + if not self.install_layout.lower() in ['deb']: + raise DistutilsOptionError("unknown value for --install-layout") + self.install_layout = self.install_layout.lower() + basename = basename.replace('-py%s' % pkg_resources.PY_MAJOR, '') + elif self.prefix_option or 'real_prefix' in sys.__dict__: + # don't modify for virtualenv + pass + else: + basename = basename.replace('-py%s' % pkg_resources.PY_MAJOR, '') + + self.source = ei_cmd.egg_info + self.target = os.path.join(self.install_dir, basename) + self.outputs = [] + + def run(self): + self.run_command('egg_info') + if os.path.isdir(self.target) and not os.path.islink(self.target): + dir_util.remove_tree(self.target, dry_run=self.dry_run) + elif os.path.exists(self.target): + self.execute(os.unlink, (self.target,), "Removing " + self.target) + if not self.dry_run: + pkg_resources.ensure_directory(self.target) + self.execute( + self.copytree, (), "Copying %s to %s" % (self.source, self.target) + ) + self.install_namespaces() + + def get_outputs(self): + return self.outputs + + def copytree(self): + # Copy the .egg-info tree to site-packages + def skimmer(src, dst): + # filter out source-control directories; note that 'src' is always + # a '/'-separated path, regardless of platform. 'dst' is a + # platform-specific path. + for skip in '.svn/', 'CVS/': + if src.startswith(skip) or '/' + skip in src: + return None + if self.install_layout and self.install_layout in ['deb'] and src.startswith('SOURCES.txt'): + log.info("Skipping SOURCES.txt") + return None + self.outputs.append(dst) + log.debug("Copying %s to %s", src, dst) + return dst + + unpack_archive(self.source, self.target, skimmer) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/install_lib.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/install_lib.py new file mode 100644 index 0000000000..bf81519d98 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/install_lib.py @@ -0,0 +1,147 @@ +import os +import sys +from itertools import product, starmap +import distutils.command.install_lib as orig + + +class install_lib(orig.install_lib): + """Don't add compiled flags to filenames of non-Python files""" + + def initialize_options(self): + orig.install_lib.initialize_options(self) + self.multiarch = None + self.install_layout = None + + def finalize_options(self): + orig.install_lib.finalize_options(self) + self.set_undefined_options('install',('install_layout','install_layout')) + if self.install_layout == 'deb' and sys.version_info[:2] >= (3, 3): + import sysconfig + self.multiarch = sysconfig.get_config_var('MULTIARCH') + + def run(self): + self.build() + outfiles = self.install() + if outfiles is not None: + # always compile, in case we have any extension stubs to deal with + self.byte_compile(outfiles) + + def get_exclusions(self): + """ + Return a collections.Sized collections.Container of paths to be + excluded for single_version_externally_managed installations. + """ + all_packages = ( + pkg + for ns_pkg in self._get_SVEM_NSPs() + for pkg in self._all_packages(ns_pkg) + ) + + excl_specs = product(all_packages, self._gen_exclusion_paths()) + return set(starmap(self._exclude_pkg_path, excl_specs)) + + def _exclude_pkg_path(self, pkg, exclusion_path): + """ + Given a package name and exclusion path within that package, + compute the full exclusion path. + """ + parts = pkg.split('.') + [exclusion_path] + return os.path.join(self.install_dir, *parts) + + @staticmethod + def _all_packages(pkg_name): + """ + >>> list(install_lib._all_packages('foo.bar.baz')) + ['foo.bar.baz', 'foo.bar', 'foo'] + """ + while pkg_name: + yield pkg_name + pkg_name, sep, child = pkg_name.rpartition('.') + + def _get_SVEM_NSPs(self): + """ + Get namespace packages (list) but only for + single_version_externally_managed installations and empty otherwise. + """ + # TODO: is it necessary to short-circuit here? i.e. what's the cost + # if get_finalized_command is called even when namespace_packages is + # False? + if not self.distribution.namespace_packages: + return [] + + install_cmd = self.get_finalized_command('install') + svem = install_cmd.single_version_externally_managed + + return self.distribution.namespace_packages if svem else [] + + @staticmethod + def _gen_exclusion_paths(): + """ + Generate file paths to be excluded for namespace packages (bytecode + cache files). + """ + # always exclude the package module itself + yield '__init__.py' + + yield '__init__.pyc' + yield '__init__.pyo' + + if not hasattr(sys, 'implementation'): + return + + base = os.path.join('__pycache__', '__init__.' + sys.implementation.cache_tag) + yield base + '.pyc' + yield base + '.pyo' + yield base + '.opt-1.pyc' + yield base + '.opt-2.pyc' + + def copy_tree( + self, infile, outfile, + preserve_mode=1, preserve_times=1, preserve_symlinks=0, level=1 + ): + assert preserve_mode and preserve_times and not preserve_symlinks + exclude = self.get_exclusions() + + if not exclude: + import distutils.dir_util + distutils.dir_util._multiarch = self.multiarch + return orig.install_lib.copy_tree(self, infile, outfile) + + # Exclude namespace package __init__.py* files from the output + + from setuptools.archive_util import unpack_directory + from distutils import log + + outfiles = [] + + if self.multiarch: + import sysconfig + ext_suffix = sysconfig.get_config_var ('EXT_SUFFIX') + if ext_suffix.endswith(self.multiarch + ext_suffix[-3:]): + new_suffix = None + else: + new_suffix = "%s-%s%s" % (ext_suffix[:-3], self.multiarch, ext_suffix[-3:]) + + def pf(src, dst): + if dst in exclude: + log.warn("Skipping installation of %s (namespace package)", + dst) + return False + + if self.multiarch and new_suffix and dst.endswith(ext_suffix) and not dst.endswith(new_suffix): + dst = dst.replace(ext_suffix, new_suffix) + log.info("renaming extension to %s", os.path.basename(dst)) + + log.info("copying %s -> %s", src, os.path.dirname(dst)) + outfiles.append(dst) + return dst + + unpack_directory(infile, outfile, pf) + return outfiles + + def get_outputs(self): + outputs = orig.install_lib.get_outputs(self) + exclude = self.get_exclusions() + if exclude: + return [f for f in outputs if f not in exclude] + return outputs diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/install_scripts.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/install_scripts.py new file mode 100644 index 0000000000..16234273a2 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/install_scripts.py @@ -0,0 +1,65 @@ +from distutils import log +import distutils.command.install_scripts as orig +import os +import sys + +from pkg_resources import Distribution, PathMetadata, ensure_directory + + +class install_scripts(orig.install_scripts): + """Do normal script install, plus any egg_info wrapper scripts""" + + def initialize_options(self): + orig.install_scripts.initialize_options(self) + self.no_ep = False + + def run(self): + import setuptools.command.easy_install as ei + + self.run_command("egg_info") + if self.distribution.scripts: + orig.install_scripts.run(self) # run first to set up self.outfiles + else: + self.outfiles = [] + if self.no_ep: + # don't install entry point scripts into .egg file! + return + + ei_cmd = self.get_finalized_command("egg_info") + dist = Distribution( + ei_cmd.egg_base, PathMetadata(ei_cmd.egg_base, ei_cmd.egg_info), + ei_cmd.egg_name, ei_cmd.egg_version, + ) + bs_cmd = self.get_finalized_command('build_scripts') + exec_param = getattr(bs_cmd, 'executable', None) + bw_cmd = self.get_finalized_command("bdist_wininst") + is_wininst = getattr(bw_cmd, '_is_running', False) + writer = ei.ScriptWriter + if is_wininst: + exec_param = "python.exe" + writer = ei.WindowsScriptWriter + if exec_param == sys.executable: + # In case the path to the Python executable contains a space, wrap + # it so it's not split up. + exec_param = [exec_param] + # resolve the writer to the environment + writer = writer.best() + cmd = writer.command_spec_class.best().from_param(exec_param) + for args in writer.get_args(dist, cmd.as_header()): + self.write_script(*args) + + def write_script(self, script_name, contents, mode="t", *ignored): + """Write an executable file to the scripts directory""" + from setuptools.command.easy_install import chmod, current_umask + + log.info("Installing %s script to %s", script_name, self.install_dir) + target = os.path.join(self.install_dir, script_name) + self.outfiles.append(target) + + mask = current_umask() + if not self.dry_run: + ensure_directory(target) + f = open(target, "w" + mode) + f.write(contents) + f.close() + chmod(target, 0o777 - mask) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/launcher manifest.xml b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/launcher manifest.xml new file mode 100644 index 0000000000..5972a96d8d --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/launcher manifest.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/py36compat.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/py36compat.py new file mode 100644 index 0000000000..61063e7542 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/py36compat.py @@ -0,0 +1,136 @@ +import os +from glob import glob +from distutils.util import convert_path +from distutils.command import sdist + +from setuptools.extern.six.moves import filter + + +class sdist_add_defaults: + """ + Mix-in providing forward-compatibility for functionality as found in + distutils on Python 3.7. + + Do not edit the code in this class except to update functionality + as implemented in distutils. Instead, override in the subclass. + """ + + def add_defaults(self): + """Add all the default files to self.filelist: + - README or README.txt + - setup.py + - test/test*.py + - all pure Python modules mentioned in setup script + - all files pointed by package_data (build_py) + - all files defined in data_files. + - all files defined as scripts. + - all C sources listed as part of extensions or C libraries + in the setup script (doesn't catch C headers!) + Warns if (README or README.txt) or setup.py are missing; everything + else is optional. + """ + self._add_defaults_standards() + self._add_defaults_optional() + self._add_defaults_python() + self._add_defaults_data_files() + self._add_defaults_ext() + self._add_defaults_c_libs() + self._add_defaults_scripts() + + @staticmethod + def _cs_path_exists(fspath): + """ + Case-sensitive path existence check + + >>> sdist_add_defaults._cs_path_exists(__file__) + True + >>> sdist_add_defaults._cs_path_exists(__file__.upper()) + False + """ + if not os.path.exists(fspath): + return False + # make absolute so we always have a directory + abspath = os.path.abspath(fspath) + directory, filename = os.path.split(abspath) + return filename in os.listdir(directory) + + def _add_defaults_standards(self): + standards = [self.READMES, self.distribution.script_name] + for fn in standards: + if isinstance(fn, tuple): + alts = fn + got_it = False + for fn in alts: + if self._cs_path_exists(fn): + got_it = True + self.filelist.append(fn) + break + + if not got_it: + self.warn("standard file not found: should have one of " + + ', '.join(alts)) + else: + if self._cs_path_exists(fn): + self.filelist.append(fn) + else: + self.warn("standard file '%s' not found" % fn) + + def _add_defaults_optional(self): + optional = ['test/test*.py', 'setup.cfg'] + for pattern in optional: + files = filter(os.path.isfile, glob(pattern)) + self.filelist.extend(files) + + def _add_defaults_python(self): + # build_py is used to get: + # - python modules + # - files defined in package_data + build_py = self.get_finalized_command('build_py') + + # getting python files + if self.distribution.has_pure_modules(): + self.filelist.extend(build_py.get_source_files()) + + # getting package_data files + # (computed in build_py.data_files by build_py.finalize_options) + for pkg, src_dir, build_dir, filenames in build_py.data_files: + for filename in filenames: + self.filelist.append(os.path.join(src_dir, filename)) + + def _add_defaults_data_files(self): + # getting distribution.data_files + if self.distribution.has_data_files(): + for item in self.distribution.data_files: + if isinstance(item, str): + # plain file + item = convert_path(item) + if os.path.isfile(item): + self.filelist.append(item) + else: + # a (dirname, filenames) tuple + dirname, filenames = item + for f in filenames: + f = convert_path(f) + if os.path.isfile(f): + self.filelist.append(f) + + def _add_defaults_ext(self): + if self.distribution.has_ext_modules(): + build_ext = self.get_finalized_command('build_ext') + self.filelist.extend(build_ext.get_source_files()) + + def _add_defaults_c_libs(self): + if self.distribution.has_c_libraries(): + build_clib = self.get_finalized_command('build_clib') + self.filelist.extend(build_clib.get_source_files()) + + def _add_defaults_scripts(self): + if self.distribution.has_scripts(): + build_scripts = self.get_finalized_command('build_scripts') + self.filelist.extend(build_scripts.get_source_files()) + + +if hasattr(sdist.sdist, '_add_defaults_standards'): + # disable the functionality already available upstream + class sdist_add_defaults: + pass diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/register.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/register.py new file mode 100644 index 0000000000..b8266b9a60 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/register.py @@ -0,0 +1,18 @@ +from distutils import log +import distutils.command.register as orig + +from setuptools.errors import RemovedCommandError + + +class register(orig.register): + """Formerly used to register packages on PyPI.""" + + def run(self): + msg = ( + "The register command has been removed, use twine to upload " + + "instead (https://pypi.org/p/twine)" + ) + + self.announce("ERROR: " + msg, log.ERROR) + + raise RemovedCommandError(msg) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/rotate.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/rotate.py new file mode 100644 index 0000000000..b89353f529 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/rotate.py @@ -0,0 +1,66 @@ +from distutils.util import convert_path +from distutils import log +from distutils.errors import DistutilsOptionError +import os +import shutil + +from setuptools.extern import six + +from setuptools import Command + + +class rotate(Command): + """Delete older distributions""" + + description = "delete older distributions, keeping N newest files" + user_options = [ + ('match=', 'm', "patterns to match (required)"), + ('dist-dir=', 'd', "directory where the distributions are"), + ('keep=', 'k', "number of matching distributions to keep"), + ] + + boolean_options = [] + + def initialize_options(self): + self.match = None + self.dist_dir = None + self.keep = None + + def finalize_options(self): + if self.match is None: + raise DistutilsOptionError( + "Must specify one or more (comma-separated) match patterns " + "(e.g. '.zip' or '.egg')" + ) + if self.keep is None: + raise DistutilsOptionError("Must specify number of files to keep") + try: + self.keep = int(self.keep) + except ValueError: + raise DistutilsOptionError("--keep must be an integer") + if isinstance(self.match, six.string_types): + self.match = [ + convert_path(p.strip()) for p in self.match.split(',') + ] + self.set_undefined_options('bdist', ('dist_dir', 'dist_dir')) + + def run(self): + self.run_command("egg_info") + from glob import glob + + for pattern in self.match: + pattern = self.distribution.get_name() + '*' + pattern + files = glob(os.path.join(self.dist_dir, pattern)) + files = [(os.path.getmtime(f), f) for f in files] + files.sort() + files.reverse() + + log.info("%d file(s) matching %s", len(files), pattern) + files = files[self.keep:] + for (t, f) in files: + log.info("Deleting %s", f) + if not self.dry_run: + if os.path.isdir(f): + shutil.rmtree(f) + else: + os.unlink(f) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/saveopts.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/saveopts.py new file mode 100644 index 0000000000..611cec5528 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/saveopts.py @@ -0,0 +1,22 @@ +from setuptools.command.setopt import edit_config, option_base + + +class saveopts(option_base): + """Save command-line options to a file""" + + description = "save supplied options to setup.cfg or other config file" + + def run(self): + dist = self.distribution + settings = {} + + for cmd in dist.command_options: + + if cmd == 'saveopts': + continue # don't save our own options! + + for opt, (src, val) in dist.get_option_dict(cmd).items(): + if src == "command line": + settings.setdefault(cmd, {})[opt] = val + + edit_config(self.filename, settings, self.dry_run) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/sdist.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/sdist.py new file mode 100644 index 0000000000..8c3438eaa6 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/sdist.py @@ -0,0 +1,252 @@ +from distutils import log +import distutils.command.sdist as orig +import os +import sys +import io +import contextlib + +from setuptools.extern import six, ordered_set + +from .py36compat import sdist_add_defaults + +import pkg_resources + +_default_revctrl = list + + +def walk_revctrl(dirname=''): + """Find all files under revision control""" + for ep in pkg_resources.iter_entry_points('setuptools.file_finders'): + for item in ep.load()(dirname): + yield item + + +class sdist(sdist_add_defaults, orig.sdist): + """Smart sdist that finds anything supported by revision control""" + + user_options = [ + ('formats=', None, + "formats for source distribution (comma-separated list)"), + ('keep-temp', 'k', + "keep the distribution tree around after creating " + + "archive file(s)"), + ('dist-dir=', 'd', + "directory to put the source distribution archive(s) in " + "[default: dist]"), + ] + + negative_opt = {} + + README_EXTENSIONS = ['', '.rst', '.txt', '.md'] + READMES = tuple('README{0}'.format(ext) for ext in README_EXTENSIONS) + + def run(self): + self.run_command('egg_info') + ei_cmd = self.get_finalized_command('egg_info') + self.filelist = ei_cmd.filelist + self.filelist.append(os.path.join(ei_cmd.egg_info, 'SOURCES.txt')) + self.check_readme() + + # Run sub commands + for cmd_name in self.get_sub_commands(): + self.run_command(cmd_name) + + self.make_distribution() + + dist_files = getattr(self.distribution, 'dist_files', []) + for file in self.archive_files: + data = ('sdist', '', file) + if data not in dist_files: + dist_files.append(data) + + def initialize_options(self): + orig.sdist.initialize_options(self) + + self._default_to_gztar() + + def _default_to_gztar(self): + # only needed on Python prior to 3.6. + if sys.version_info >= (3, 6, 0, 'beta', 1): + return + self.formats = ['gztar'] + + def make_distribution(self): + """ + Workaround for #516 + """ + with self._remove_os_link(): + orig.sdist.make_distribution(self) + + @staticmethod + @contextlib.contextmanager + def _remove_os_link(): + """ + In a context, remove and restore os.link if it exists + """ + + class NoValue: + pass + + orig_val = getattr(os, 'link', NoValue) + try: + del os.link + except Exception: + pass + try: + yield + finally: + if orig_val is not NoValue: + setattr(os, 'link', orig_val) + + def __read_template_hack(self): + # This grody hack closes the template file (MANIFEST.in) if an + # exception occurs during read_template. + # Doing so prevents an error when easy_install attempts to delete the + # file. + try: + orig.sdist.read_template(self) + except Exception: + _, _, tb = sys.exc_info() + tb.tb_next.tb_frame.f_locals['template'].close() + raise + + # Beginning with Python 2.7.2, 3.1.4, and 3.2.1, this leaky file handle + # has been fixed, so only override the method if we're using an earlier + # Python. + has_leaky_handle = ( + sys.version_info < (2, 7, 2) + or (3, 0) <= sys.version_info < (3, 1, 4) + or (3, 2) <= sys.version_info < (3, 2, 1) + ) + if has_leaky_handle: + read_template = __read_template_hack + + def _add_defaults_optional(self): + if six.PY2: + sdist_add_defaults._add_defaults_optional(self) + else: + super()._add_defaults_optional() + if os.path.isfile('pyproject.toml'): + self.filelist.append('pyproject.toml') + + def _add_defaults_python(self): + """getting python files""" + if self.distribution.has_pure_modules(): + build_py = self.get_finalized_command('build_py') + self.filelist.extend(build_py.get_source_files()) + self._add_data_files(self._safe_data_files(build_py)) + + def _safe_data_files(self, build_py): + """ + Extracting data_files from build_py is known to cause + infinite recursion errors when `include_package_data` + is enabled, so suppress it in that case. + """ + if self.distribution.include_package_data: + return () + return build_py.data_files + + def _add_data_files(self, data_files): + """ + Add data files as found in build_py.data_files. + """ + self.filelist.extend( + os.path.join(src_dir, name) + for _, src_dir, _, filenames in data_files + for name in filenames + ) + + def _add_defaults_data_files(self): + try: + if six.PY2: + sdist_add_defaults._add_defaults_data_files(self) + else: + super()._add_defaults_data_files() + except TypeError: + log.warn("data_files contains unexpected objects") + + def check_readme(self): + for f in self.READMES: + if os.path.exists(f): + return + else: + self.warn( + "standard file not found: should have one of " + + ', '.join(self.READMES) + ) + + def make_release_tree(self, base_dir, files): + orig.sdist.make_release_tree(self, base_dir, files) + + # Save any egg_info command line options used to create this sdist + dest = os.path.join(base_dir, 'setup.cfg') + if hasattr(os, 'link') and os.path.exists(dest): + # unlink and re-copy, since it might be hard-linked, and + # we don't want to change the source version + os.unlink(dest) + self.copy_file('setup.cfg', dest) + + self.get_finalized_command('egg_info').save_version_info(dest) + + def _manifest_is_not_generated(self): + # check for special comment used in 2.7.1 and higher + if not os.path.isfile(self.manifest): + return False + + with io.open(self.manifest, 'rb') as fp: + first_line = fp.readline() + return (first_line != + '# file GENERATED by distutils, do NOT edit\n'.encode()) + + def read_manifest(self): + """Read the manifest file (named by 'self.manifest') and use it to + fill in 'self.filelist', the list of files to include in the source + distribution. + """ + log.info("reading manifest file '%s'", self.manifest) + manifest = open(self.manifest, 'rb') + for line in manifest: + # The manifest must contain UTF-8. See #303. + if not six.PY2: + try: + line = line.decode('UTF-8') + except UnicodeDecodeError: + log.warn("%r not UTF-8 decodable -- skipping" % line) + continue + # ignore comments and blank lines + line = line.strip() + if line.startswith('#') or not line: + continue + self.filelist.append(line) + manifest.close() + + def check_license(self): + """Checks if license_file' or 'license_files' is configured and adds any + valid paths to 'self.filelist'. + """ + + files = ordered_set.OrderedSet() + + opts = self.distribution.get_option_dict('metadata') + + # ignore the source of the value + _, license_file = opts.get('license_file', (None, None)) + + if license_file is None: + log.debug("'license_file' option was not specified") + else: + files.add(license_file) + + try: + files.update(self.distribution.metadata.license_files) + except TypeError: + log.warn("warning: 'license_files' option is malformed") + + for f in files: + if not os.path.exists(f): + log.warn( + "warning: Failed to find the configured license file '%s'", + f) + files.remove(f) + + self.filelist.extend(files) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/setopt.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/setopt.py new file mode 100644 index 0000000000..7e57cc0262 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/setopt.py @@ -0,0 +1,149 @@ +from distutils.util import convert_path +from distutils import log +from distutils.errors import DistutilsOptionError +import distutils +import os + +from setuptools.extern.six.moves import configparser + +from setuptools import Command + +__all__ = ['config_file', 'edit_config', 'option_base', 'setopt'] + + +def config_file(kind="local"): + """Get the filename of the distutils, local, global, or per-user config + + `kind` must be one of "local", "global", or "user" + """ + if kind == 'local': + return 'setup.cfg' + if kind == 'global': + return os.path.join( + os.path.dirname(distutils.__file__), 'distutils.cfg' + ) + if kind == 'user': + dot = os.name == 'posix' and '.' or '' + return os.path.expanduser(convert_path("~/%spydistutils.cfg" % dot)) + raise ValueError( + "config_file() type must be 'local', 'global', or 'user'", kind + ) + + +def edit_config(filename, settings, dry_run=False): + """Edit a configuration file to include `settings` + + `settings` is a dictionary of dictionaries or ``None`` values, keyed by + command/section name. A ``None`` value means to delete the entire section, + while a dictionary lists settings to be changed or deleted in that section. + A setting of ``None`` means to delete that setting. + """ + log.debug("Reading configuration from %s", filename) + opts = configparser.RawConfigParser() + opts.read([filename]) + for section, options in settings.items(): + if options is None: + log.info("Deleting section [%s] from %s", section, filename) + opts.remove_section(section) + else: + if not opts.has_section(section): + log.debug("Adding new section [%s] to %s", section, filename) + opts.add_section(section) + for option, value in options.items(): + if value is None: + log.debug( + "Deleting %s.%s from %s", + section, option, filename + ) + opts.remove_option(section, option) + if not opts.options(section): + log.info("Deleting empty [%s] section from %s", + section, filename) + opts.remove_section(section) + else: + log.debug( + "Setting %s.%s to %r in %s", + section, option, value, filename + ) + opts.set(section, option, value) + + log.info("Writing %s", filename) + if not dry_run: + with open(filename, 'w') as f: + opts.write(f) + + +class option_base(Command): + """Abstract base class for commands that mess with config files""" + + user_options = [ + ('global-config', 'g', + "save options to the site-wide distutils.cfg file"), + ('user-config', 'u', + "save options to the current user's pydistutils.cfg file"), + ('filename=', 'f', + "configuration file to use (default=setup.cfg)"), + ] + + boolean_options = [ + 'global-config', 'user-config', + ] + + def initialize_options(self): + self.global_config = None + self.user_config = None + self.filename = None + + def finalize_options(self): + filenames = [] + if self.global_config: + filenames.append(config_file('global')) + if self.user_config: + filenames.append(config_file('user')) + if self.filename is not None: + filenames.append(self.filename) + if not filenames: + filenames.append(config_file('local')) + if len(filenames) > 1: + raise DistutilsOptionError( + "Must specify only one configuration file option", + filenames + ) + self.filename, = filenames + + +class setopt(option_base): + """Save command-line options to a file""" + + description = "set an option in setup.cfg or another config file" + + user_options = [ + ('command=', 'c', 'command to set an option for'), + ('option=', 'o', 'option to set'), + ('set-value=', 's', 'value of the option'), + ('remove', 'r', 'remove (unset) the value'), + ] + option_base.user_options + + boolean_options = option_base.boolean_options + ['remove'] + + def initialize_options(self): + option_base.initialize_options(self) + self.command = None + self.option = None + self.set_value = None + self.remove = None + + def finalize_options(self): + option_base.finalize_options(self) + if self.command is None or self.option is None: + raise DistutilsOptionError("Must specify --command *and* --option") + if self.set_value is None and not self.remove: + raise DistutilsOptionError("Must specify --set-value or --remove") + + def run(self): + edit_config( + self.filename, { + self.command: {self.option.replace('-', '_'): self.set_value} + }, + self.dry_run + ) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/test.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/test.py new file mode 100644 index 0000000000..f6470e9c34 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/test.py @@ -0,0 +1,279 @@ +import os +import operator +import sys +import contextlib +import itertools +import unittest +from distutils.errors import DistutilsError, DistutilsOptionError +from distutils import log +from unittest import TestLoader + +from setuptools.extern import six +from setuptools.extern.six.moves import map, filter + +from pkg_resources import (resource_listdir, resource_exists, normalize_path, + working_set, _namespace_packages, evaluate_marker, + add_activation_listener, require, EntryPoint) +from setuptools import Command +from .build_py import _unique_everseen + +__metaclass__ = type + + +class ScanningLoader(TestLoader): + + def __init__(self): + TestLoader.__init__(self) + self._visited = set() + + def loadTestsFromModule(self, module, pattern=None): + """Return a suite of all tests cases contained in the given module + + If the module is a package, load tests from all the modules in it. + If the module has an ``additional_tests`` function, call it and add + the return value to the tests. + """ + if module in self._visited: + return None + self._visited.add(module) + + tests = [] + tests.append(TestLoader.loadTestsFromModule(self, module)) + + if hasattr(module, "additional_tests"): + tests.append(module.additional_tests()) + + if hasattr(module, '__path__'): + for file in resource_listdir(module.__name__, ''): + if file.endswith('.py') and file != '__init__.py': + submodule = module.__name__ + '.' + file[:-3] + else: + if resource_exists(module.__name__, file + '/__init__.py'): + submodule = module.__name__ + '.' + file + else: + continue + tests.append(self.loadTestsFromName(submodule)) + + if len(tests) != 1: + return self.suiteClass(tests) + else: + return tests[0] # don't create a nested suite for only one return + + +# adapted from jaraco.classes.properties:NonDataProperty +class NonDataProperty: + def __init__(self, fget): + self.fget = fget + + def __get__(self, obj, objtype=None): + if obj is None: + return self + return self.fget(obj) + + +class test(Command): + """Command to run unit tests after in-place build""" + + description = "run unit tests after in-place build (deprecated)" + + user_options = [ + ('test-module=', 'm', "Run 'test_suite' in specified module"), + ('test-suite=', 's', + "Run single test, case or suite (e.g. 'module.test_suite')"), + ('test-runner=', 'r', "Test runner to use"), + ] + + def initialize_options(self): + self.test_suite = None + self.test_module = None + self.test_loader = None + self.test_runner = None + + def finalize_options(self): + + if self.test_suite and self.test_module: + msg = "You may specify a module or a suite, but not both" + raise DistutilsOptionError(msg) + + if self.test_suite is None: + if self.test_module is None: + self.test_suite = self.distribution.test_suite + else: + self.test_suite = self.test_module + ".test_suite" + + if self.test_loader is None: + self.test_loader = getattr(self.distribution, 'test_loader', None) + if self.test_loader is None: + self.test_loader = "setuptools.command.test:ScanningLoader" + if self.test_runner is None: + self.test_runner = getattr(self.distribution, 'test_runner', None) + + @NonDataProperty + def test_args(self): + return list(self._test_args()) + + def _test_args(self): + if not self.test_suite and sys.version_info >= (2, 7): + yield 'discover' + if self.verbose: + yield '--verbose' + if self.test_suite: + yield self.test_suite + + def with_project_on_sys_path(self, func): + """ + Backward compatibility for project_on_sys_path context. + """ + with self.project_on_sys_path(): + func() + + @contextlib.contextmanager + def project_on_sys_path(self, include_dists=[]): + with_2to3 = not six.PY2 and getattr(self.distribution, 'use_2to3', False) + + if with_2to3: + # If we run 2to3 we can not do this inplace: + + # Ensure metadata is up-to-date + self.reinitialize_command('build_py', inplace=0) + self.run_command('build_py') + bpy_cmd = self.get_finalized_command("build_py") + build_path = normalize_path(bpy_cmd.build_lib) + + # Build extensions + self.reinitialize_command('egg_info', egg_base=build_path) + self.run_command('egg_info') + + self.reinitialize_command('build_ext', inplace=0) + self.run_command('build_ext') + else: + # Without 2to3 inplace works fine: + self.run_command('egg_info') + + # Build extensions in-place + self.reinitialize_command('build_ext', inplace=1) + self.run_command('build_ext') + + ei_cmd = self.get_finalized_command("egg_info") + + old_path = sys.path[:] + old_modules = sys.modules.copy() + + try: + project_path = normalize_path(ei_cmd.egg_base) + sys.path.insert(0, project_path) + working_set.__init__() + add_activation_listener(lambda dist: dist.activate()) + require('%s==%s' % (ei_cmd.egg_name, ei_cmd.egg_version)) + with self.paths_on_pythonpath([project_path]): + yield + finally: + sys.path[:] = old_path + sys.modules.clear() + sys.modules.update(old_modules) + working_set.__init__() + + @staticmethod + @contextlib.contextmanager + def paths_on_pythonpath(paths): + """ + Add the indicated paths to the head of the PYTHONPATH environment + variable so that subprocesses will also see the packages at + these paths. + + Do this in a context that restores the value on exit. + """ + nothing = object() + orig_pythonpath = os.environ.get('PYTHONPATH', nothing) + current_pythonpath = os.environ.get('PYTHONPATH', '') + try: + prefix = os.pathsep.join(_unique_everseen(paths)) + to_join = filter(None, [prefix, current_pythonpath]) + new_path = os.pathsep.join(to_join) + if new_path: + os.environ['PYTHONPATH'] = new_path + yield + finally: + if orig_pythonpath is nothing: + os.environ.pop('PYTHONPATH', None) + else: + os.environ['PYTHONPATH'] = orig_pythonpath + + @staticmethod + def install_dists(dist): + """ + Install the requirements indicated by self.distribution and + return an iterable of the dists that were built. + """ + ir_d = dist.fetch_build_eggs(dist.install_requires) + tr_d = dist.fetch_build_eggs(dist.tests_require or []) + er_d = dist.fetch_build_eggs( + v for k, v in dist.extras_require.items() + if k.startswith(':') and evaluate_marker(k[1:]) + ) + return itertools.chain(ir_d, tr_d, er_d) + + def run(self): + self.announce( + "WARNING: Testing via this command is deprecated and will be " + "removed in a future version. Users looking for a generic test " + "entry point independent of test runner are encouraged to use " + "tox.", + log.WARN, + ) + + installed_dists = self.install_dists(self.distribution) + + cmd = ' '.join(self._argv) + if self.dry_run: + self.announce('skipping "%s" (dry run)' % cmd) + return + + self.announce('running "%s"' % cmd) + + paths = map(operator.attrgetter('location'), installed_dists) + with self.paths_on_pythonpath(paths): + with self.project_on_sys_path(): + self.run_tests() + + def run_tests(self): + # Purge modules under test from sys.modules. The test loader will + # re-import them from the build location. Required when 2to3 is used + # with namespace packages. + if not six.PY2 and getattr(self.distribution, 'use_2to3', False): + module = self.test_suite.split('.')[0] + if module in _namespace_packages: + del_modules = [] + if module in sys.modules: + del_modules.append(module) + module += '.' + for name in sys.modules: + if name.startswith(module): + del_modules.append(name) + list(map(sys.modules.__delitem__, del_modules)) + + test = unittest.main( + None, None, self._argv, + testLoader=self._resolve_as_ep(self.test_loader), + testRunner=self._resolve_as_ep(self.test_runner), + exit=False, + ) + if not test.result.wasSuccessful(): + msg = 'Test failed: %s' % test.result + self.announce(msg, log.ERROR) + raise DistutilsError(msg) + + @property + def _argv(self): + return ['unittest'] + self.test_args + + @staticmethod + def _resolve_as_ep(val): + """ + Load the indicated attribute value, called, as a as if it were + specified as an entry point. + """ + if val is None: + return + parsed = EntryPoint.parse("x=" + val) + return parsed.resolve()() diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/upload.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/upload.py new file mode 100644 index 0000000000..ec7f81e227 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/upload.py @@ -0,0 +1,17 @@ +from distutils import log +from distutils.command import upload as orig + +from setuptools.errors import RemovedCommandError + + +class upload(orig.upload): + """Formerly used to upload packages to PyPI.""" + + def run(self): + msg = ( + "The upload command has been removed, use twine to upload " + + "instead (https://pypi.org/p/twine)" + ) + + self.announce("ERROR: " + msg, log.ERROR) + raise RemovedCommandError(msg) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/upload_docs.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/upload_docs.py new file mode 100644 index 0000000000..130a0cb6c9 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/command/upload_docs.py @@ -0,0 +1,206 @@ +# -*- coding: utf-8 -*- +"""upload_docs + +Implements a Distutils 'upload_docs' subcommand (upload documentation to +PyPI's pythonhosted.org). +""" + +from base64 import standard_b64encode +from distutils import log +from distutils.errors import DistutilsOptionError +import os +import socket +import zipfile +import tempfile +import shutil +import itertools +import functools + +from setuptools.extern import six +from setuptools.extern.six.moves import http_client, urllib + +from pkg_resources import iter_entry_points +from .upload import upload + + +def _encode(s): + errors = 'strict' if six.PY2 else 'surrogateescape' + return s.encode('utf-8', errors) + + +class upload_docs(upload): + # override the default repository as upload_docs isn't + # supported by Warehouse (and won't be). + DEFAULT_REPOSITORY = 'https://pypi.python.org/pypi/' + + description = 'Upload documentation to PyPI' + + user_options = [ + ('repository=', 'r', + "url of repository [default: %s]" % upload.DEFAULT_REPOSITORY), + ('show-response', None, + 'display full response text from server'), + ('upload-dir=', None, 'directory to upload'), + ] + boolean_options = upload.boolean_options + + def has_sphinx(self): + if self.upload_dir is None: + for ep in iter_entry_points('distutils.commands', 'build_sphinx'): + return True + + sub_commands = [('build_sphinx', has_sphinx)] + + def initialize_options(self): + upload.initialize_options(self) + self.upload_dir = None + self.target_dir = None + + def finalize_options(self): + upload.finalize_options(self) + if self.upload_dir is None: + if self.has_sphinx(): + build_sphinx = self.get_finalized_command('build_sphinx') + self.target_dir = build_sphinx.builder_target_dir + else: + build = self.get_finalized_command('build') + self.target_dir = os.path.join(build.build_base, 'docs') + else: + self.ensure_dirname('upload_dir') + self.target_dir = self.upload_dir + if 'pypi.python.org' in self.repository: + log.warn("Upload_docs command is deprecated. Use RTD instead.") + self.announce('Using upload directory %s' % self.target_dir) + + def create_zipfile(self, filename): + zip_file = zipfile.ZipFile(filename, "w") + try: + self.mkpath(self.target_dir) # just in case + for root, dirs, files in os.walk(self.target_dir): + if root == self.target_dir and not files: + tmpl = "no files found in upload directory '%s'" + raise DistutilsOptionError(tmpl % self.target_dir) + for name in files: + full = os.path.join(root, name) + relative = root[len(self.target_dir):].lstrip(os.path.sep) + dest = os.path.join(relative, name) + zip_file.write(full, dest) + finally: + zip_file.close() + + def run(self): + # Run sub commands + for cmd_name in self.get_sub_commands(): + self.run_command(cmd_name) + + tmp_dir = tempfile.mkdtemp() + name = self.distribution.metadata.get_name() + zip_file = os.path.join(tmp_dir, "%s.zip" % name) + try: + self.create_zipfile(zip_file) + self.upload_file(zip_file) + finally: + shutil.rmtree(tmp_dir) + + @staticmethod + def _build_part(item, sep_boundary): + key, values = item + title = '\nContent-Disposition: form-data; name="%s"' % key + # handle multiple entries for the same name + if not isinstance(values, list): + values = [values] + for value in values: + if isinstance(value, tuple): + title += '; filename="%s"' % value[0] + value = value[1] + else: + value = _encode(value) + yield sep_boundary + yield _encode(title) + yield b"\n\n" + yield value + if value and value[-1:] == b'\r': + yield b'\n' # write an extra newline (lurve Macs) + + @classmethod + def _build_multipart(cls, data): + """ + Build up the MIME payload for the POST data + """ + boundary = b'--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' + sep_boundary = b'\n--' + boundary + end_boundary = sep_boundary + b'--' + end_items = end_boundary, b"\n", + builder = functools.partial( + cls._build_part, + sep_boundary=sep_boundary, + ) + part_groups = map(builder, data.items()) + parts = itertools.chain.from_iterable(part_groups) + body_items = itertools.chain(parts, end_items) + content_type = 'multipart/form-data; boundary=%s' % boundary.decode('ascii') + return b''.join(body_items), content_type + + def upload_file(self, filename): + with open(filename, 'rb') as f: + content = f.read() + meta = self.distribution.metadata + data = { + ':action': 'doc_upload', + 'name': meta.get_name(), + 'content': (os.path.basename(filename), content), + } + # set up the authentication + credentials = _encode(self.username + ':' + self.password) + credentials = standard_b64encode(credentials) + if not six.PY2: + credentials = credentials.decode('ascii') + auth = "Basic " + credentials + + body, ct = self._build_multipart(data) + + msg = "Submitting documentation to %s" % (self.repository) + self.announce(msg, log.INFO) + + # build the Request + # We can't use urllib2 since we need to send the Basic + # auth right with the first request + schema, netloc, url, params, query, fragments = \ + urllib.parse.urlparse(self.repository) + assert not params and not query and not fragments + if schema == 'http': + conn = http_client.HTTPConnection(netloc) + elif schema == 'https': + conn = http_client.HTTPSConnection(netloc) + else: + raise AssertionError("unsupported schema " + schema) + + data = '' + try: + conn.connect() + conn.putrequest("POST", url) + content_type = ct + conn.putheader('Content-type', content_type) + conn.putheader('Content-length', str(len(body))) + conn.putheader('Authorization', auth) + conn.endheaders() + conn.send(body) + except socket.error as e: + self.announce(str(e), log.ERROR) + return + + r = conn.getresponse() + if r.status == 200: + msg = 'Server response (%s): %s' % (r.status, r.reason) + self.announce(msg, log.INFO) + elif r.status == 301: + location = r.getheader('Location') + if location is None: + location = 'https://pythonhosted.org/%s/' % meta.get_name() + msg = 'Upload successful. Visit %s' % location + self.announce(msg, log.INFO) + else: + msg = 'Upload failed (%s): %s' % (r.status, r.reason) + self.announce(msg, log.ERROR) + if self.show_response: + print('-' * 75, r.read(), '-' * 75) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/config.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/config.py new file mode 100644 index 0000000000..9b9a0c45e7 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/config.py @@ -0,0 +1,659 @@ +from __future__ import absolute_import, unicode_literals +import io +import os +import sys + +import warnings +import functools +from collections import defaultdict +from functools import partial +from functools import wraps +from importlib import import_module + +from distutils.errors import DistutilsOptionError, DistutilsFileError +from setuptools.extern.packaging.version import LegacyVersion, parse +from setuptools.extern.packaging.specifiers import SpecifierSet +from setuptools.extern.six import string_types, PY3 + + +__metaclass__ = type + + +def read_configuration( + filepath, find_others=False, ignore_option_errors=False): + """Read given configuration file and returns options from it as a dict. + + :param str|unicode filepath: Path to configuration file + to get options from. + + :param bool find_others: Whether to search for other configuration files + which could be on in various places. + + :param bool ignore_option_errors: Whether to silently ignore + options, values of which could not be resolved (e.g. due to exceptions + in directives such as file:, attr:, etc.). + If False exceptions are propagated as expected. + + :rtype: dict + """ + from setuptools.dist import Distribution, _Distribution + + filepath = os.path.abspath(filepath) + + if not os.path.isfile(filepath): + raise DistutilsFileError( + 'Configuration file %s does not exist.' % filepath) + + current_directory = os.getcwd() + os.chdir(os.path.dirname(filepath)) + + try: + dist = Distribution() + + filenames = dist.find_config_files() if find_others else [] + if filepath not in filenames: + filenames.append(filepath) + + _Distribution.parse_config_files(dist, filenames=filenames) + + handlers = parse_configuration( + dist, dist.command_options, + ignore_option_errors=ignore_option_errors) + + finally: + os.chdir(current_directory) + + return configuration_to_dict(handlers) + + +def _get_option(target_obj, key): + """ + Given a target object and option key, get that option from + the target object, either through a get_{key} method or + from an attribute directly. + """ + getter_name = 'get_{key}'.format(**locals()) + by_attribute = functools.partial(getattr, target_obj, key) + getter = getattr(target_obj, getter_name, by_attribute) + return getter() + + +def configuration_to_dict(handlers): + """Returns configuration data gathered by given handlers as a dict. + + :param list[ConfigHandler] handlers: Handlers list, + usually from parse_configuration() + + :rtype: dict + """ + config_dict = defaultdict(dict) + + for handler in handlers: + for option in handler.set_options: + value = _get_option(handler.target_obj, option) + config_dict[handler.section_prefix][option] = value + + return config_dict + + +def parse_configuration( + distribution, command_options, ignore_option_errors=False): + """Performs additional parsing of configuration options + for a distribution. + + Returns a list of used option handlers. + + :param Distribution distribution: + :param dict command_options: + :param bool ignore_option_errors: Whether to silently ignore + options, values of which could not be resolved (e.g. due to exceptions + in directives such as file:, attr:, etc.). + If False exceptions are propagated as expected. + :rtype: list + """ + options = ConfigOptionsHandler( + distribution, command_options, ignore_option_errors) + options.parse() + + meta = ConfigMetadataHandler( + distribution.metadata, command_options, ignore_option_errors, + distribution.package_dir) + meta.parse() + + return meta, options + + +class ConfigHandler: + """Handles metadata supplied in configuration files.""" + + section_prefix = None + """Prefix for config sections handled by this handler. + Must be provided by class heirs. + + """ + + aliases = {} + """Options aliases. + For compatibility with various packages. E.g.: d2to1 and pbr. + Note: `-` in keys is replaced with `_` by config parser. + + """ + + def __init__(self, target_obj, options, ignore_option_errors=False): + sections = {} + + section_prefix = self.section_prefix + for section_name, section_options in options.items(): + if not section_name.startswith(section_prefix): + continue + + section_name = section_name.replace(section_prefix, '').strip('.') + sections[section_name] = section_options + + self.ignore_option_errors = ignore_option_errors + self.target_obj = target_obj + self.sections = sections + self.set_options = [] + + @property + def parsers(self): + """Metadata item name to parser function mapping.""" + raise NotImplementedError( + '%s must provide .parsers property' % self.__class__.__name__) + + def __setitem__(self, option_name, value): + unknown = tuple() + target_obj = self.target_obj + + # Translate alias into real name. + option_name = self.aliases.get(option_name, option_name) + + current_value = getattr(target_obj, option_name, unknown) + + if current_value is unknown: + raise KeyError(option_name) + + if current_value: + # Already inhabited. Skipping. + return + + skip_option = False + parser = self.parsers.get(option_name) + if parser: + try: + value = parser(value) + + except Exception: + skip_option = True + if not self.ignore_option_errors: + raise + + if skip_option: + return + + setter = getattr(target_obj, 'set_%s' % option_name, None) + if setter is None: + setattr(target_obj, option_name, value) + else: + setter(value) + + self.set_options.append(option_name) + + @classmethod + def _parse_list(cls, value, separator=','): + """Represents value as a list. + + Value is split either by separator (defaults to comma) or by lines. + + :param value: + :param separator: List items separator character. + :rtype: list + """ + if isinstance(value, list): # _get_parser_compound case + return value + + if '\n' in value: + value = value.splitlines() + else: + value = value.split(separator) + + return [chunk.strip() for chunk in value if chunk.strip()] + + @classmethod + def _parse_dict(cls, value): + """Represents value as a dict. + + :param value: + :rtype: dict + """ + separator = '=' + result = {} + for line in cls._parse_list(value): + key, sep, val = line.partition(separator) + if sep != separator: + raise DistutilsOptionError( + 'Unable to parse option value to dict: %s' % value) + result[key.strip()] = val.strip() + + return result + + @classmethod + def _parse_bool(cls, value): + """Represents value as boolean. + + :param value: + :rtype: bool + """ + value = value.lower() + return value in ('1', 'true', 'yes') + + @classmethod + def _exclude_files_parser(cls, key): + """Returns a parser function to make sure field inputs + are not files. + + Parses a value after getting the key so error messages are + more informative. + + :param key: + :rtype: callable + """ + def parser(value): + exclude_directive = 'file:' + if value.startswith(exclude_directive): + raise ValueError( + 'Only strings are accepted for the {0} field, ' + 'files are not accepted'.format(key)) + return value + return parser + + @classmethod + def _parse_file(cls, value): + """Represents value as a string, allowing including text + from nearest files using `file:` directive. + + Directive is sandboxed and won't reach anything outside + directory with setup.py. + + Examples: + file: README.rst, CHANGELOG.md, src/file.txt + + :param str value: + :rtype: str + """ + include_directive = 'file:' + + if not isinstance(value, string_types): + return value + + if not value.startswith(include_directive): + return value + + spec = value[len(include_directive):] + filepaths = (os.path.abspath(path.strip()) for path in spec.split(',')) + return '\n'.join( + cls._read_file(path) + for path in filepaths + if (cls._assert_local(path) or True) + and os.path.isfile(path) + ) + + @staticmethod + def _assert_local(filepath): + if not filepath.startswith(os.getcwd()): + raise DistutilsOptionError( + '`file:` directive can not access %s' % filepath) + + @staticmethod + def _read_file(filepath): + with io.open(filepath, encoding='utf-8') as f: + return f.read() + + @classmethod + def _parse_attr(cls, value, package_dir=None): + """Represents value as a module attribute. + + Examples: + attr: package.attr + attr: package.module.attr + + :param str value: + :rtype: str + """ + attr_directive = 'attr:' + if not value.startswith(attr_directive): + return value + + attrs_path = value.replace(attr_directive, '').strip().split('.') + attr_name = attrs_path.pop() + + module_name = '.'.join(attrs_path) + module_name = module_name or '__init__' + + parent_path = os.getcwd() + if package_dir: + if attrs_path[0] in package_dir: + # A custom path was specified for the module we want to import + custom_path = package_dir[attrs_path[0]] + parts = custom_path.rsplit('/', 1) + if len(parts) > 1: + parent_path = os.path.join(os.getcwd(), parts[0]) + module_name = parts[1] + else: + module_name = custom_path + elif '' in package_dir: + # A custom parent directory was specified for all root modules + parent_path = os.path.join(os.getcwd(), package_dir['']) + sys.path.insert(0, parent_path) + try: + module = import_module(module_name) + value = getattr(module, attr_name) + + finally: + sys.path = sys.path[1:] + + return value + + @classmethod + def _get_parser_compound(cls, *parse_methods): + """Returns parser function to represents value as a list. + + Parses a value applying given methods one after another. + + :param parse_methods: + :rtype: callable + """ + def parse(value): + parsed = value + + for method in parse_methods: + parsed = method(parsed) + + return parsed + + return parse + + @classmethod + def _parse_section_to_dict(cls, section_options, values_parser=None): + """Parses section options into a dictionary. + + Optionally applies a given parser to values. + + :param dict section_options: + :param callable values_parser: + :rtype: dict + """ + value = {} + values_parser = values_parser or (lambda val: val) + for key, (_, val) in section_options.items(): + value[key] = values_parser(val) + return value + + def parse_section(self, section_options): + """Parses configuration file section. + + :param dict section_options: + """ + for (name, (_, value)) in section_options.items(): + try: + self[name] = value + + except KeyError: + pass # Keep silent for a new option may appear anytime. + + def parse(self): + """Parses configuration file items from one + or more related sections. + + """ + for section_name, section_options in self.sections.items(): + + method_postfix = '' + if section_name: # [section.option] variant + method_postfix = '_%s' % section_name + + section_parser_method = getattr( + self, + # Dots in section names are translated into dunderscores. + ('parse_section%s' % method_postfix).replace('.', '__'), + None) + + if section_parser_method is None: + raise DistutilsOptionError( + 'Unsupported distribution option section: [%s.%s]' % ( + self.section_prefix, section_name)) + + section_parser_method(section_options) + + def _deprecated_config_handler(self, func, msg, warning_class): + """ this function will wrap around parameters that are deprecated + + :param msg: deprecation message + :param warning_class: class of warning exception to be raised + :param func: function to be wrapped around + """ + @wraps(func) + def config_handler(*args, **kwargs): + warnings.warn(msg, warning_class) + return func(*args, **kwargs) + + return config_handler + + +class ConfigMetadataHandler(ConfigHandler): + + section_prefix = 'metadata' + + aliases = { + 'home_page': 'url', + 'summary': 'description', + 'classifier': 'classifiers', + 'platform': 'platforms', + } + + strict_mode = False + """We need to keep it loose, to be partially compatible with + `pbr` and `d2to1` packages which also uses `metadata` section. + + """ + + def __init__(self, target_obj, options, ignore_option_errors=False, + package_dir=None): + super(ConfigMetadataHandler, self).__init__(target_obj, options, + ignore_option_errors) + self.package_dir = package_dir + + @property + def parsers(self): + """Metadata item name to parser function mapping.""" + parse_list = self._parse_list + parse_file = self._parse_file + parse_dict = self._parse_dict + exclude_files_parser = self._exclude_files_parser + + return { + 'platforms': parse_list, + 'keywords': parse_list, + 'provides': parse_list, + 'requires': self._deprecated_config_handler( + parse_list, + "The requires parameter is deprecated, please use " + "install_requires for runtime dependencies.", + DeprecationWarning), + 'obsoletes': parse_list, + 'classifiers': self._get_parser_compound(parse_file, parse_list), + 'license': exclude_files_parser('license'), + 'license_files': parse_list, + 'description': parse_file, + 'long_description': parse_file, + 'version': self._parse_version, + 'project_urls': parse_dict, + } + + def _parse_version(self, value): + """Parses `version` option value. + + :param value: + :rtype: str + + """ + version = self._parse_file(value) + + if version != value: + version = version.strip() + # Be strict about versions loaded from file because it's easy to + # accidentally include newlines and other unintended content + if isinstance(parse(version), LegacyVersion): + tmpl = ( + 'Version loaded from {value} does not ' + 'comply with PEP 440: {version}' + ) + raise DistutilsOptionError(tmpl.format(**locals())) + + return version + + version = self._parse_attr(value, self.package_dir) + + if callable(version): + version = version() + + if not isinstance(version, string_types): + if hasattr(version, '__iter__'): + version = '.'.join(map(str, version)) + else: + version = '%s' % version + + return version + + +class ConfigOptionsHandler(ConfigHandler): + + section_prefix = 'options' + + @property + def parsers(self): + """Metadata item name to parser function mapping.""" + parse_list = self._parse_list + parse_list_semicolon = partial(self._parse_list, separator=';') + parse_bool = self._parse_bool + parse_dict = self._parse_dict + + return { + 'zip_safe': parse_bool, + 'use_2to3': parse_bool, + 'include_package_data': parse_bool, + 'package_dir': parse_dict, + 'use_2to3_fixers': parse_list, + 'use_2to3_exclude_fixers': parse_list, + 'convert_2to3_doctests': parse_list, + 'scripts': parse_list, + 'eager_resources': parse_list, + 'dependency_links': parse_list, + 'namespace_packages': parse_list, + 'install_requires': parse_list_semicolon, + 'setup_requires': parse_list_semicolon, + 'tests_require': parse_list_semicolon, + 'packages': self._parse_packages, + 'entry_points': self._parse_file, + 'py_modules': parse_list, + 'python_requires': SpecifierSet, + } + + def _parse_packages(self, value): + """Parses `packages` option value. + + :param value: + :rtype: list + """ + find_directives = ['find:', 'find_namespace:'] + trimmed_value = value.strip() + + if trimmed_value not in find_directives: + return self._parse_list(value) + + findns = trimmed_value == find_directives[1] + if findns and not PY3: + raise DistutilsOptionError( + 'find_namespace: directive is unsupported on Python < 3.3') + + # Read function arguments from a dedicated section. + find_kwargs = self.parse_section_packages__find( + self.sections.get('packages.find', {})) + + if findns: + from setuptools import find_namespace_packages as find_packages + else: + from setuptools import find_packages + + return find_packages(**find_kwargs) + + def parse_section_packages__find(self, section_options): + """Parses `packages.find` configuration file section. + + To be used in conjunction with _parse_packages(). + + :param dict section_options: + """ + section_data = self._parse_section_to_dict( + section_options, self._parse_list) + + valid_keys = ['where', 'include', 'exclude'] + + find_kwargs = dict( + [(k, v) for k, v in section_data.items() if k in valid_keys and v]) + + where = find_kwargs.get('where') + if where is not None: + find_kwargs['where'] = where[0] # cast list to single val + + return find_kwargs + + def parse_section_entry_points(self, section_options): + """Parses `entry_points` configuration file section. + + :param dict section_options: + """ + parsed = self._parse_section_to_dict(section_options, self._parse_list) + self['entry_points'] = parsed + + def _parse_package_data(self, section_options): + parsed = self._parse_section_to_dict(section_options, self._parse_list) + + root = parsed.get('*') + if root: + parsed[''] = root + del parsed['*'] + + return parsed + + def parse_section_package_data(self, section_options): + """Parses `package_data` configuration file section. + + :param dict section_options: + """ + self['package_data'] = self._parse_package_data(section_options) + + def parse_section_exclude_package_data(self, section_options): + """Parses `exclude_package_data` configuration file section. + + :param dict section_options: + """ + self['exclude_package_data'] = self._parse_package_data( + section_options) + + def parse_section_extras_require(self, section_options): + """Parses `extras_require` configuration file section. + + :param dict section_options: + """ + parse_list = partial(self._parse_list, separator=';') + self['extras_require'] = self._parse_section_to_dict( + section_options, parse_list) + + def parse_section_data_files(self, section_options): + """Parses `data_files` configuration file section. + + :param dict section_options: + """ + parsed = self._parse_section_to_dict(section_options, self._parse_list) + self['data_files'] = [(k, v) for k, v in parsed.items()] diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/dep_util.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/dep_util.py new file mode 100644 index 0000000000..2931c13ec3 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/dep_util.py @@ -0,0 +1,23 @@ +from distutils.dep_util import newer_group + +# yes, this is was almost entirely copy-pasted from +# 'newer_pairwise()', this is just another convenience +# function. +def newer_pairwise_group(sources_groups, targets): + """Walk both arguments in parallel, testing if each source group is newer + than its corresponding target. Returns a pair of lists (sources_groups, + targets) where sources is newer than target, according to the semantics + of 'newer_group()'. + """ + if len(sources_groups) != len(targets): + raise ValueError("'sources_group' and 'targets' must be the same length") + + # build a pair of lists (sources_groups, targets) where source is newer + n_sources = [] + n_targets = [] + for i in range(len(sources_groups)): + if newer_group(sources_groups[i], targets[i]): + n_sources.append(sources_groups[i]) + n_targets.append(targets[i]) + + return n_sources, n_targets diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/depends.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/depends.py new file mode 100644 index 0000000000..a37675cbd9 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/depends.py @@ -0,0 +1,176 @@ +import sys +import marshal +import contextlib +from distutils.version import StrictVersion + +from .py33compat import Bytecode + +from .py27compat import find_module, PY_COMPILED, PY_FROZEN, PY_SOURCE +from . import py27compat + + +__all__ = [ + 'Require', 'find_module', 'get_module_constant', 'extract_constant' +] + + +class Require: + """A prerequisite to building or installing a distribution""" + + def __init__( + self, name, requested_version, module, homepage='', + attribute=None, format=None): + + if format is None and requested_version is not None: + format = StrictVersion + + if format is not None: + requested_version = format(requested_version) + if attribute is None: + attribute = '__version__' + + self.__dict__.update(locals()) + del self.self + + def full_name(self): + """Return full package/distribution name, w/version""" + if self.requested_version is not None: + return '%s-%s' % (self.name, self.requested_version) + return self.name + + def version_ok(self, version): + """Is 'version' sufficiently up-to-date?""" + return self.attribute is None or self.format is None or \ + str(version) != "unknown" and version >= self.requested_version + + def get_version(self, paths=None, default="unknown"): + """Get version number of installed module, 'None', or 'default' + + Search 'paths' for module. If not found, return 'None'. If found, + return the extracted version attribute, or 'default' if no version + attribute was specified, or the value cannot be determined without + importing the module. The version is formatted according to the + requirement's version format (if any), unless it is 'None' or the + supplied 'default'. + """ + + if self.attribute is None: + try: + f, p, i = find_module(self.module, paths) + if f: + f.close() + return default + except ImportError: + return None + + v = get_module_constant(self.module, self.attribute, default, paths) + + if v is not None and v is not default and self.format is not None: + return self.format(v) + + return v + + def is_present(self, paths=None): + """Return true if dependency is present on 'paths'""" + return self.get_version(paths) is not None + + def is_current(self, paths=None): + """Return true if dependency is present and up-to-date on 'paths'""" + version = self.get_version(paths) + if version is None: + return False + return self.version_ok(version) + + +def maybe_close(f): + @contextlib.contextmanager + def empty(): + yield + return + if not f: + return empty() + + return contextlib.closing(f) + + +def get_module_constant(module, symbol, default=-1, paths=None): + """Find 'module' by searching 'paths', and extract 'symbol' + + Return 'None' if 'module' does not exist on 'paths', or it does not define + 'symbol'. If the module defines 'symbol' as a constant, return the + constant. Otherwise, return 'default'.""" + + try: + f, path, (suffix, mode, kind) = info = find_module(module, paths) + except ImportError: + # Module doesn't exist + return None + + with maybe_close(f): + if kind == PY_COMPILED: + f.read(8) # skip magic & date + code = marshal.load(f) + elif kind == PY_FROZEN: + code = py27compat.get_frozen_object(module, paths) + elif kind == PY_SOURCE: + code = compile(f.read(), path, 'exec') + else: + # Not something we can parse; we'll have to import it. :( + imported = py27compat.get_module(module, paths, info) + return getattr(imported, symbol, None) + + return extract_constant(code, symbol, default) + + +def extract_constant(code, symbol, default=-1): + """Extract the constant value of 'symbol' from 'code' + + If the name 'symbol' is bound to a constant value by the Python code + object 'code', return that value. If 'symbol' is bound to an expression, + return 'default'. Otherwise, return 'None'. + + Return value is based on the first assignment to 'symbol'. 'symbol' must + be a global, or at least a non-"fast" local in the code block. That is, + only 'STORE_NAME' and 'STORE_GLOBAL' opcodes are checked, and 'symbol' + must be present in 'code.co_names'. + """ + if symbol not in code.co_names: + # name's not there, can't possibly be an assignment + return None + + name_idx = list(code.co_names).index(symbol) + + STORE_NAME = 90 + STORE_GLOBAL = 97 + LOAD_CONST = 100 + + const = default + + for byte_code in Bytecode(code): + op = byte_code.opcode + arg = byte_code.arg + + if op == LOAD_CONST: + const = code.co_consts[arg] + elif arg == name_idx and (op == STORE_NAME or op == STORE_GLOBAL): + return const + else: + const = default + + +def _update_globals(): + """ + Patch the globals to remove the objects not available on some platforms. + + XXX it'd be better to test assertions about bytecode instead. + """ + + if not sys.platform.startswith('java') and sys.platform != 'cli': + return + incompatible = 'extract_constant', 'get_module_constant' + for name in incompatible: + del globals()[name] + __all__.remove(name) + + +_update_globals() diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/dist.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/dist.py new file mode 100644 index 0000000000..0480aaa8e8 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/dist.py @@ -0,0 +1,1274 @@ +# -*- coding: utf-8 -*- +__all__ = ['Distribution'] + +import io +import sys +import re +import os +import warnings +import numbers +import distutils.log +import distutils.core +import distutils.cmd +import distutils.dist +from distutils.util import strtobool +from distutils.debug import DEBUG +from distutils.fancy_getopt import translate_longopt +import itertools + +from collections import defaultdict +from email import message_from_file + +from distutils.errors import ( + DistutilsOptionError, DistutilsPlatformError, DistutilsSetupError, +) +from distutils.util import rfc822_escape +from distutils.version import StrictVersion + +from setuptools.extern import six +from setuptools.extern import packaging +from setuptools.extern import ordered_set +from setuptools.extern.six.moves import map, filter, filterfalse + +from . import SetuptoolsDeprecationWarning + +from setuptools.depends import Require +from setuptools import windows_support +from setuptools.monkey import get_unpatched +from setuptools.config import parse_configuration +import pkg_resources + +__import__('setuptools.extern.packaging.specifiers') +__import__('setuptools.extern.packaging.version') + + +def _get_unpatched(cls): + warnings.warn("Do not call this function", DistDeprecationWarning) + return get_unpatched(cls) + + +def get_metadata_version(self): + mv = getattr(self, 'metadata_version', None) + + if mv is None: + if self.long_description_content_type or self.provides_extras: + mv = StrictVersion('2.1') + elif (self.maintainer is not None or + self.maintainer_email is not None or + getattr(self, 'python_requires', None) is not None or + self.project_urls): + mv = StrictVersion('1.2') + elif (self.provides or self.requires or self.obsoletes or + self.classifiers or self.download_url): + mv = StrictVersion('1.1') + else: + mv = StrictVersion('1.0') + + self.metadata_version = mv + + return mv + + +def read_pkg_file(self, file): + """Reads the metadata values from a file object.""" + msg = message_from_file(file) + + def _read_field(name): + value = msg[name] + if value == 'UNKNOWN': + return None + return value + + def _read_list(name): + values = msg.get_all(name, None) + if values == []: + return None + return values + + self.metadata_version = StrictVersion(msg['metadata-version']) + self.name = _read_field('name') + self.version = _read_field('version') + self.description = _read_field('summary') + # we are filling author only. + self.author = _read_field('author') + self.maintainer = None + self.author_email = _read_field('author-email') + self.maintainer_email = None + self.url = _read_field('home-page') + self.license = _read_field('license') + + if 'download-url' in msg: + self.download_url = _read_field('download-url') + else: + self.download_url = None + + self.long_description = _read_field('description') + self.description = _read_field('summary') + + if 'keywords' in msg: + self.keywords = _read_field('keywords').split(',') + + self.platforms = _read_list('platform') + self.classifiers = _read_list('classifier') + + # PEP 314 - these fields only exist in 1.1 + if self.metadata_version == StrictVersion('1.1'): + self.requires = _read_list('requires') + self.provides = _read_list('provides') + self.obsoletes = _read_list('obsoletes') + else: + self.requires = None + self.provides = None + self.obsoletes = None + + +# Based on Python 3.5 version +def write_pkg_file(self, file): + """Write the PKG-INFO format data to a file object. + """ + version = self.get_metadata_version() + + if six.PY2: + def write_field(key, value): + file.write("%s: %s\n" % (key, self._encode_field(value))) + else: + def write_field(key, value): + file.write("%s: %s\n" % (key, value)) + + write_field('Metadata-Version', str(version)) + write_field('Name', self.get_name()) + write_field('Version', self.get_version()) + write_field('Summary', self.get_description()) + write_field('Home-page', self.get_url()) + + if version < StrictVersion('1.2'): + write_field('Author', self.get_contact()) + write_field('Author-email', self.get_contact_email()) + else: + optional_fields = ( + ('Author', 'author'), + ('Author-email', 'author_email'), + ('Maintainer', 'maintainer'), + ('Maintainer-email', 'maintainer_email'), + ) + + for field, attr in optional_fields: + attr_val = getattr(self, attr) + + if attr_val is not None: + write_field(field, attr_val) + + write_field('License', self.get_license()) + if self.download_url: + write_field('Download-URL', self.download_url) + for project_url in self.project_urls.items(): + write_field('Project-URL', '%s, %s' % project_url) + + long_desc = rfc822_escape(self.get_long_description()) + write_field('Description', long_desc) + + keywords = ','.join(self.get_keywords()) + if keywords: + write_field('Keywords', keywords) + + if version >= StrictVersion('1.2'): + for platform in self.get_platforms(): + write_field('Platform', platform) + else: + self._write_list(file, 'Platform', self.get_platforms()) + + self._write_list(file, 'Classifier', self.get_classifiers()) + + # PEP 314 + self._write_list(file, 'Requires', self.get_requires()) + self._write_list(file, 'Provides', self.get_provides()) + self._write_list(file, 'Obsoletes', self.get_obsoletes()) + + # Setuptools specific for PEP 345 + if hasattr(self, 'python_requires'): + write_field('Requires-Python', self.python_requires) + + # PEP 566 + if self.long_description_content_type: + write_field( + 'Description-Content-Type', + self.long_description_content_type + ) + if self.provides_extras: + for extra in sorted(self.provides_extras): + write_field('Provides-Extra', extra) + + +sequence = tuple, list + + +def check_importable(dist, attr, value): + try: + ep = pkg_resources.EntryPoint.parse('x=' + value) + assert not ep.extras + except (TypeError, ValueError, AttributeError, AssertionError): + raise DistutilsSetupError( + "%r must be importable 'module:attrs' string (got %r)" + % (attr, value) + ) + + +def assert_string_list(dist, attr, value): + """Verify that value is a string list""" + try: + # verify that value is a list or tuple to exclude unordered + # or single-use iterables + assert isinstance(value, (list, tuple)) + # verify that elements of value are strings + assert ''.join(value) != value + except (TypeError, ValueError, AttributeError, AssertionError): + raise DistutilsSetupError( + "%r must be a list of strings (got %r)" % (attr, value) + ) + + +def check_nsp(dist, attr, value): + """Verify that namespace packages are valid""" + ns_packages = value + assert_string_list(dist, attr, ns_packages) + for nsp in ns_packages: + if not dist.has_contents_for(nsp): + raise DistutilsSetupError( + "Distribution contains no modules or packages for " + + "namespace package %r" % nsp + ) + parent, sep, child = nsp.rpartition('.') + if parent and parent not in ns_packages: + distutils.log.warn( + "WARNING: %r is declared as a package namespace, but %r" + " is not: please correct this in setup.py", nsp, parent + ) + + +def check_extras(dist, attr, value): + """Verify that extras_require mapping is valid""" + try: + list(itertools.starmap(_check_extra, value.items())) + except (TypeError, ValueError, AttributeError): + raise DistutilsSetupError( + "'extras_require' must be a dictionary whose values are " + "strings or lists of strings containing valid project/version " + "requirement specifiers." + ) + + +def _check_extra(extra, reqs): + name, sep, marker = extra.partition(':') + if marker and pkg_resources.invalid_marker(marker): + raise DistutilsSetupError("Invalid environment marker: " + marker) + list(pkg_resources.parse_requirements(reqs)) + + +def assert_bool(dist, attr, value): + """Verify that value is True, False, 0, or 1""" + if bool(value) != value: + tmpl = "{attr!r} must be a boolean value (got {value!r})" + raise DistutilsSetupError(tmpl.format(attr=attr, value=value)) + + +def check_requirements(dist, attr, value): + """Verify that install_requires is a valid requirements list""" + try: + list(pkg_resources.parse_requirements(value)) + if isinstance(value, (dict, set)): + raise TypeError("Unordered types are not allowed") + except (TypeError, ValueError) as error: + tmpl = ( + "{attr!r} must be a string or list of strings " + "containing valid project/version requirement specifiers; {error}" + ) + raise DistutilsSetupError(tmpl.format(attr=attr, error=error)) + + +def check_specifier(dist, attr, value): + """Verify that value is a valid version specifier""" + try: + packaging.specifiers.SpecifierSet(value) + except packaging.specifiers.InvalidSpecifier as error: + tmpl = ( + "{attr!r} must be a string " + "containing valid version specifiers; {error}" + ) + raise DistutilsSetupError(tmpl.format(attr=attr, error=error)) + + +def check_entry_points(dist, attr, value): + """Verify that entry_points map is parseable""" + try: + pkg_resources.EntryPoint.parse_map(value) + except ValueError as e: + raise DistutilsSetupError(e) + + +def check_test_suite(dist, attr, value): + if not isinstance(value, six.string_types): + raise DistutilsSetupError("test_suite must be a string") + + +def check_package_data(dist, attr, value): + """Verify that value is a dictionary of package names to glob lists""" + if not isinstance(value, dict): + raise DistutilsSetupError( + "{!r} must be a dictionary mapping package names to lists of " + "string wildcard patterns".format(attr)) + for k, v in value.items(): + if not isinstance(k, six.string_types): + raise DistutilsSetupError( + "keys of {!r} dict must be strings (got {!r})" + .format(attr, k) + ) + assert_string_list(dist, 'values of {!r} dict'.format(attr), v) + + +def check_packages(dist, attr, value): + for pkgname in value: + if not re.match(r'\w+(\.\w+)*', pkgname): + distutils.log.warn( + "WARNING: %r not a valid package name; please use only " + ".-separated package names in setup.py", pkgname + ) + + +_Distribution = get_unpatched(distutils.core.Distribution) + + +class Distribution(_Distribution): + """Distribution with support for features, tests, and package data + + This is an enhanced version of 'distutils.dist.Distribution' that + effectively adds the following new optional keyword arguments to 'setup()': + + 'install_requires' -- a string or sequence of strings specifying project + versions that the distribution requires when installed, in the format + used by 'pkg_resources.require()'. They will be installed + automatically when the package is installed. If you wish to use + packages that are not available in PyPI, or want to give your users an + alternate download location, you can add a 'find_links' option to the + '[easy_install]' section of your project's 'setup.cfg' file, and then + setuptools will scan the listed web pages for links that satisfy the + requirements. + + 'extras_require' -- a dictionary mapping names of optional "extras" to the + additional requirement(s) that using those extras incurs. For example, + this:: + + extras_require = dict(reST = ["docutils>=0.3", "reSTedit"]) + + indicates that the distribution can optionally provide an extra + capability called "reST", but it can only be used if docutils and + reSTedit are installed. If the user installs your package using + EasyInstall and requests one of your extras, the corresponding + additional requirements will be installed if needed. + + 'features' **deprecated** -- a dictionary mapping option names to + 'setuptools.Feature' + objects. Features are a portion of the distribution that can be + included or excluded based on user options, inter-feature dependencies, + and availability on the current system. Excluded features are omitted + from all setup commands, including source and binary distributions, so + you can create multiple distributions from the same source tree. + Feature names should be valid Python identifiers, except that they may + contain the '-' (minus) sign. Features can be included or excluded + via the command line options '--with-X' and '--without-X', where 'X' is + the name of the feature. Whether a feature is included by default, and + whether you are allowed to control this from the command line, is + determined by the Feature object. See the 'Feature' class for more + information. + + 'test_suite' -- the name of a test suite to run for the 'test' command. + If the user runs 'python setup.py test', the package will be installed, + and the named test suite will be run. The format is the same as + would be used on a 'unittest.py' command line. That is, it is the + dotted name of an object to import and call to generate a test suite. + + 'package_data' -- a dictionary mapping package names to lists of filenames + or globs to use to find data files contained in the named packages. + If the dictionary has filenames or globs listed under '""' (the empty + string), those names will be searched for in every package, in addition + to any names for the specific package. Data files found using these + names/globs will be installed along with the package, in the same + location as the package. Note that globs are allowed to reference + the contents of non-package subdirectories, as long as you use '/' as + a path separator. (Globs are automatically converted to + platform-specific paths at runtime.) + + In addition to these new keywords, this class also has several new methods + for manipulating the distribution's contents. For example, the 'include()' + and 'exclude()' methods can be thought of as in-place add and subtract + commands that add or remove packages, modules, extensions, and so on from + the distribution. They are used by the feature subsystem to configure the + distribution for the included and excluded features. + """ + + _DISTUTILS_UNSUPPORTED_METADATA = { + 'long_description_content_type': None, + 'project_urls': dict, + 'provides_extras': ordered_set.OrderedSet, + 'license_files': ordered_set.OrderedSet, + } + + _patched_dist = None + + def patch_missing_pkg_info(self, attrs): + # Fake up a replacement for the data that would normally come from + # PKG-INFO, but which might not yet be built if this is a fresh + # checkout. + # + if not attrs or 'name' not in attrs or 'version' not in attrs: + return + key = pkg_resources.safe_name(str(attrs['name'])).lower() + dist = pkg_resources.working_set.by_key.get(key) + if dist is not None and not dist.has_metadata('PKG-INFO'): + dist._version = pkg_resources.safe_version(str(attrs['version'])) + self._patched_dist = dist + + def __init__(self, attrs=None): + have_package_data = hasattr(self, "package_data") + if not have_package_data: + self.package_data = {} + attrs = attrs or {} + if 'features' in attrs or 'require_features' in attrs: + Feature.warn_deprecated() + self.require_features = [] + self.features = {} + self.dist_files = [] + # Filter-out setuptools' specific options. + self.src_root = attrs.pop("src_root", None) + self.patch_missing_pkg_info(attrs) + self.dependency_links = attrs.pop('dependency_links', []) + self.setup_requires = attrs.pop('setup_requires', []) + for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): + vars(self).setdefault(ep.name, None) + _Distribution.__init__(self, { + k: v for k, v in attrs.items() + if k not in self._DISTUTILS_UNSUPPORTED_METADATA + }) + + # Fill-in missing metadata fields not supported by distutils. + # Note some fields may have been set by other tools (e.g. pbr) + # above; they are taken preferrentially to setup() arguments + for option, default in self._DISTUTILS_UNSUPPORTED_METADATA.items(): + for source in self.metadata.__dict__, attrs: + if option in source: + value = source[option] + break + else: + value = default() if default else None + setattr(self.metadata, option, value) + + if isinstance(self.metadata.version, numbers.Number): + # Some people apparently take "version number" too literally :) + self.metadata.version = str(self.metadata.version) + + if self.metadata.version is not None: + try: + ver = packaging.version.Version(self.metadata.version) + normalized_version = str(ver) + if self.metadata.version != normalized_version: + warnings.warn( + "Normalizing '%s' to '%s'" % ( + self.metadata.version, + normalized_version, + ) + ) + self.metadata.version = normalized_version + except (packaging.version.InvalidVersion, TypeError): + warnings.warn( + "The version specified (%r) is an invalid version, this " + "may not work as expected with newer versions of " + "setuptools, pip, and PyPI. Please see PEP 440 for more " + "details." % self.metadata.version + ) + self._finalize_requires() + + def _finalize_requires(self): + """ + Set `metadata.python_requires` and fix environment markers + in `install_requires` and `extras_require`. + """ + if getattr(self, 'python_requires', None): + self.metadata.python_requires = self.python_requires + + if getattr(self, 'extras_require', None): + for extra in self.extras_require.keys(): + # Since this gets called multiple times at points where the + # keys have become 'converted' extras, ensure that we are only + # truly adding extras we haven't seen before here. + extra = extra.split(':')[0] + if extra: + self.metadata.provides_extras.add(extra) + + self._convert_extras_requirements() + self._move_install_requirements_markers() + + def _convert_extras_requirements(self): + """ + Convert requirements in `extras_require` of the form + `"extra": ["barbazquux; {marker}"]` to + `"extra:{marker}": ["barbazquux"]`. + """ + spec_ext_reqs = getattr(self, 'extras_require', None) or {} + self._tmp_extras_require = defaultdict(list) + for section, v in spec_ext_reqs.items(): + # Do not strip empty sections. + self._tmp_extras_require[section] + for r in pkg_resources.parse_requirements(v): + suffix = self._suffix_for(r) + self._tmp_extras_require[section + suffix].append(r) + + @staticmethod + def _suffix_for(req): + """ + For a requirement, return the 'extras_require' suffix for + that requirement. + """ + return ':' + str(req.marker) if req.marker else '' + + def _move_install_requirements_markers(self): + """ + Move requirements in `install_requires` that are using environment + markers `extras_require`. + """ + + # divide the install_requires into two sets, simple ones still + # handled by install_requires and more complex ones handled + # by extras_require. + + def is_simple_req(req): + return not req.marker + + spec_inst_reqs = getattr(self, 'install_requires', None) or () + inst_reqs = list(pkg_resources.parse_requirements(spec_inst_reqs)) + simple_reqs = filter(is_simple_req, inst_reqs) + complex_reqs = filterfalse(is_simple_req, inst_reqs) + self.install_requires = list(map(str, simple_reqs)) + + for r in complex_reqs: + self._tmp_extras_require[':' + str(r.marker)].append(r) + self.extras_require = dict( + (k, [str(r) for r in map(self._clean_req, v)]) + for k, v in self._tmp_extras_require.items() + ) + + def _clean_req(self, req): + """ + Given a Requirement, remove environment markers and return it. + """ + req.marker = None + return req + + def _parse_config_files(self, filenames=None): + """ + Adapted from distutils.dist.Distribution.parse_config_files, + this method provides the same functionality in subtly-improved + ways. + """ + from setuptools.extern.six.moves.configparser import ConfigParser + + # Ignore install directory options if we have a venv + if not six.PY2 and sys.prefix != sys.base_prefix: + ignore_options = [ + 'install-base', 'install-platbase', 'install-lib', + 'install-platlib', 'install-purelib', 'install-headers', + 'install-scripts', 'install-data', 'prefix', 'exec-prefix', + 'home', 'user', 'root'] + else: + ignore_options = [] + + ignore_options = frozenset(ignore_options) + + if filenames is None: + filenames = self.find_config_files() + + if DEBUG: + self.announce("Distribution.parse_config_files():") + + parser = ConfigParser() + for filename in filenames: + with io.open(filename, encoding='utf-8') as reader: + if DEBUG: + self.announce(" reading {filename}".format(**locals())) + (parser.readfp if six.PY2 else parser.read_file)(reader) + for section in parser.sections(): + options = parser.options(section) + opt_dict = self.get_option_dict(section) + + for opt in options: + if opt != '__name__' and opt not in ignore_options: + val = self._try_str(parser.get(section, opt)) + opt = opt.replace('-', '_') + opt_dict[opt] = (filename, val) + + # Make the ConfigParser forget everything (so we retain + # the original filenames that options come from) + parser.__init__() + + # If there was a "global" section in the config file, use it + # to set Distribution options. + + if 'global' in self.command_options: + for (opt, (src, val)) in self.command_options['global'].items(): + alias = self.negative_opt.get(opt) + try: + if alias: + setattr(self, alias, not strtobool(val)) + elif opt in ('verbose', 'dry_run'): # ugh! + setattr(self, opt, strtobool(val)) + else: + setattr(self, opt, val) + except ValueError as msg: + raise DistutilsOptionError(msg) + + @staticmethod + def _try_str(val): + """ + On Python 2, much of distutils relies on string values being of + type 'str' (bytes) and not unicode text. If the value can be safely + encoded to bytes using the default encoding, prefer that. + + Why the default encoding? Because that value can be implicitly + decoded back to text if needed. + + Ref #1653 + """ + if not six.PY2: + return val + try: + return val.encode() + except UnicodeEncodeError: + pass + return val + + def _set_command_options(self, command_obj, option_dict=None): + """ + Set the options for 'command_obj' from 'option_dict'. Basically + this means copying elements of a dictionary ('option_dict') to + attributes of an instance ('command'). + + 'command_obj' must be a Command instance. If 'option_dict' is not + supplied, uses the standard option dictionary for this command + (from 'self.command_options'). + + (Adopted from distutils.dist.Distribution._set_command_options) + """ + command_name = command_obj.get_command_name() + if option_dict is None: + option_dict = self.get_option_dict(command_name) + + if DEBUG: + self.announce(" setting options for '%s' command:" % command_name) + for (option, (source, value)) in option_dict.items(): + if DEBUG: + self.announce(" %s = %s (from %s)" % (option, value, + source)) + try: + bool_opts = [translate_longopt(o) + for o in command_obj.boolean_options] + except AttributeError: + bool_opts = [] + try: + neg_opt = command_obj.negative_opt + except AttributeError: + neg_opt = {} + + try: + is_string = isinstance(value, six.string_types) + if option in neg_opt and is_string: + setattr(command_obj, neg_opt[option], not strtobool(value)) + elif option in bool_opts and is_string: + setattr(command_obj, option, strtobool(value)) + elif hasattr(command_obj, option): + setattr(command_obj, option, value) + else: + raise DistutilsOptionError( + "error in %s: command '%s' has no such option '%s'" + % (source, command_name, option)) + except ValueError as msg: + raise DistutilsOptionError(msg) + + def parse_config_files(self, filenames=None, ignore_option_errors=False): + """Parses configuration files from various levels + and loads configuration. + + """ + self._parse_config_files(filenames=filenames) + + parse_configuration(self, self.command_options, + ignore_option_errors=ignore_option_errors) + self._finalize_requires() + + def parse_command_line(self): + """Process features after parsing command line options""" + result = _Distribution.parse_command_line(self) + if self.features: + self._finalize_features() + return result + + def _feature_attrname(self, name): + """Convert feature name to corresponding option attribute name""" + return 'with_' + name.replace('-', '_') + + def fetch_build_eggs(self, requires): + """Resolve pre-setup requirements""" + resolved_dists = pkg_resources.working_set.resolve( + pkg_resources.parse_requirements(requires), + installer=self.fetch_build_egg, + replace_conflicting=True, + ) + for dist in resolved_dists: + pkg_resources.working_set.add(dist, replace=True) + return resolved_dists + + def finalize_options(self): + """ + Allow plugins to apply arbitrary operations to the + distribution. Each hook may optionally define a 'order' + to influence the order of execution. Smaller numbers + go first and the default is 0. + """ + group = 'setuptools.finalize_distribution_options' + + def by_order(hook): + return getattr(hook, 'order', 0) + eps = map(lambda e: e.load(), pkg_resources.iter_entry_points(group)) + for ep in sorted(eps, key=by_order): + ep(self) + + def _finalize_setup_keywords(self): + for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): + value = getattr(self, ep.name, None) + if value is not None: + ep.require(installer=self.fetch_build_egg) + ep.load()(self, ep.name, value) + + def _finalize_2to3_doctests(self): + if getattr(self, 'convert_2to3_doctests', None): + # XXX may convert to set here when we can rely on set being builtin + self.convert_2to3_doctests = [ + os.path.abspath(p) + for p in self.convert_2to3_doctests + ] + else: + self.convert_2to3_doctests = [] + + def get_egg_cache_dir(self): + egg_cache_dir = os.path.join(os.curdir, '.eggs') + if not os.path.exists(egg_cache_dir): + os.mkdir(egg_cache_dir) + windows_support.hide_file(egg_cache_dir) + readme_txt_filename = os.path.join(egg_cache_dir, 'README.txt') + with open(readme_txt_filename, 'w') as f: + f.write('This directory contains eggs that were downloaded ' + 'by setuptools to build, test, and run plug-ins.\n\n') + f.write('This directory caches those eggs to prevent ' + 'repeated downloads.\n\n') + f.write('However, it is safe to delete this directory.\n\n') + + return egg_cache_dir + + def fetch_build_egg(self, req): + """Fetch an egg needed for building""" + from setuptools.installer import fetch_build_egg + return fetch_build_egg(self, req) + + def _finalize_feature_opts(self): + """Add --with-X/--without-X options based on optional features""" + + if not self.features: + return + + go = [] + no = self.negative_opt.copy() + + for name, feature in self.features.items(): + self._set_feature(name, None) + feature.validate(self) + + if feature.optional: + descr = feature.description + incdef = ' (default)' + excdef = '' + if not feature.include_by_default(): + excdef, incdef = incdef, excdef + + new = ( + ('with-' + name, None, 'include ' + descr + incdef), + ('without-' + name, None, 'exclude ' + descr + excdef), + ) + go.extend(new) + no['without-' + name] = 'with-' + name + + self.global_options = self.feature_options = go + self.global_options + self.negative_opt = self.feature_negopt = no + + def _finalize_features(self): + """Add/remove features and resolve dependencies between them""" + + # First, flag all the enabled items (and thus their dependencies) + for name, feature in self.features.items(): + enabled = self.feature_is_included(name) + if enabled or (enabled is None and feature.include_by_default()): + feature.include_in(self) + self._set_feature(name, 1) + + # Then disable the rest, so that off-by-default features don't + # get flagged as errors when they're required by an enabled feature + for name, feature in self.features.items(): + if not self.feature_is_included(name): + feature.exclude_from(self) + self._set_feature(name, 0) + + def get_command_class(self, command): + """Pluggable version of get_command_class()""" + if command in self.cmdclass: + return self.cmdclass[command] + + eps = pkg_resources.iter_entry_points('distutils.commands', command) + for ep in eps: + ep.require(installer=self.fetch_build_egg) + self.cmdclass[command] = cmdclass = ep.load() + return cmdclass + else: + return _Distribution.get_command_class(self, command) + + def print_commands(self): + for ep in pkg_resources.iter_entry_points('distutils.commands'): + if ep.name not in self.cmdclass: + # don't require extras as the commands won't be invoked + cmdclass = ep.resolve() + self.cmdclass[ep.name] = cmdclass + return _Distribution.print_commands(self) + + def get_command_list(self): + for ep in pkg_resources.iter_entry_points('distutils.commands'): + if ep.name not in self.cmdclass: + # don't require extras as the commands won't be invoked + cmdclass = ep.resolve() + self.cmdclass[ep.name] = cmdclass + return _Distribution.get_command_list(self) + + def _set_feature(self, name, status): + """Set feature's inclusion status""" + setattr(self, self._feature_attrname(name), status) + + def feature_is_included(self, name): + """Return 1 if feature is included, 0 if excluded, 'None' if unknown""" + return getattr(self, self._feature_attrname(name)) + + def include_feature(self, name): + """Request inclusion of feature named 'name'""" + + if self.feature_is_included(name) == 0: + descr = self.features[name].description + raise DistutilsOptionError( + descr + " is required, but was excluded or is not available" + ) + self.features[name].include_in(self) + self._set_feature(name, 1) + + def include(self, **attrs): + """Add items to distribution that are named in keyword arguments + + For example, 'dist.include(py_modules=["x"])' would add 'x' to + the distribution's 'py_modules' attribute, if it was not already + there. + + Currently, this method only supports inclusion for attributes that are + lists or tuples. If you need to add support for adding to other + attributes in this or a subclass, you can add an '_include_X' method, + where 'X' is the name of the attribute. The method will be called with + the value passed to 'include()'. So, 'dist.include(foo={"bar":"baz"})' + will try to call 'dist._include_foo({"bar":"baz"})', which can then + handle whatever special inclusion logic is needed. + """ + for k, v in attrs.items(): + include = getattr(self, '_include_' + k, None) + if include: + include(v) + else: + self._include_misc(k, v) + + def exclude_package(self, package): + """Remove packages, modules, and extensions in named package""" + + pfx = package + '.' + if self.packages: + self.packages = [ + p for p in self.packages + if p != package and not p.startswith(pfx) + ] + + if self.py_modules: + self.py_modules = [ + p for p in self.py_modules + if p != package and not p.startswith(pfx) + ] + + if self.ext_modules: + self.ext_modules = [ + p for p in self.ext_modules + if p.name != package and not p.name.startswith(pfx) + ] + + def has_contents_for(self, package): + """Return true if 'exclude_package(package)' would do something""" + + pfx = package + '.' + + for p in self.iter_distribution_names(): + if p == package or p.startswith(pfx): + return True + + def _exclude_misc(self, name, value): + """Handle 'exclude()' for list/tuple attrs without a special handler""" + if not isinstance(value, sequence): + raise DistutilsSetupError( + "%s: setting must be a list or tuple (%r)" % (name, value) + ) + try: + old = getattr(self, name) + except AttributeError: + raise DistutilsSetupError( + "%s: No such distribution setting" % name + ) + if old is not None and not isinstance(old, sequence): + raise DistutilsSetupError( + name + ": this setting cannot be changed via include/exclude" + ) + elif old: + setattr(self, name, [item for item in old if item not in value]) + + def _include_misc(self, name, value): + """Handle 'include()' for list/tuple attrs without a special handler""" + + if not isinstance(value, sequence): + raise DistutilsSetupError( + "%s: setting must be a list (%r)" % (name, value) + ) + try: + old = getattr(self, name) + except AttributeError: + raise DistutilsSetupError( + "%s: No such distribution setting" % name + ) + if old is None: + setattr(self, name, value) + elif not isinstance(old, sequence): + raise DistutilsSetupError( + name + ": this setting cannot be changed via include/exclude" + ) + else: + new = [item for item in value if item not in old] + setattr(self, name, old + new) + + def exclude(self, **attrs): + """Remove items from distribution that are named in keyword arguments + + For example, 'dist.exclude(py_modules=["x"])' would remove 'x' from + the distribution's 'py_modules' attribute. Excluding packages uses + the 'exclude_package()' method, so all of the package's contained + packages, modules, and extensions are also excluded. + + Currently, this method only supports exclusion from attributes that are + lists or tuples. If you need to add support for excluding from other + attributes in this or a subclass, you can add an '_exclude_X' method, + where 'X' is the name of the attribute. The method will be called with + the value passed to 'exclude()'. So, 'dist.exclude(foo={"bar":"baz"})' + will try to call 'dist._exclude_foo({"bar":"baz"})', which can then + handle whatever special exclusion logic is needed. + """ + for k, v in attrs.items(): + exclude = getattr(self, '_exclude_' + k, None) + if exclude: + exclude(v) + else: + self._exclude_misc(k, v) + + def _exclude_packages(self, packages): + if not isinstance(packages, sequence): + raise DistutilsSetupError( + "packages: setting must be a list or tuple (%r)" % (packages,) + ) + list(map(self.exclude_package, packages)) + + def _parse_command_opts(self, parser, args): + # Remove --with-X/--without-X options when processing command args + self.global_options = self.__class__.global_options + self.negative_opt = self.__class__.negative_opt + + # First, expand any aliases + command = args[0] + aliases = self.get_option_dict('aliases') + while command in aliases: + src, alias = aliases[command] + del aliases[command] # ensure each alias can expand only once! + import shlex + args[:1] = shlex.split(alias, True) + command = args[0] + + nargs = _Distribution._parse_command_opts(self, parser, args) + + # Handle commands that want to consume all remaining arguments + cmd_class = self.get_command_class(command) + if getattr(cmd_class, 'command_consumes_arguments', None): + self.get_option_dict(command)['args'] = ("command line", nargs) + if nargs is not None: + return [] + + return nargs + + def get_cmdline_options(self): + """Return a '{cmd: {opt:val}}' map of all command-line options + + Option names are all long, but do not include the leading '--', and + contain dashes rather than underscores. If the option doesn't take + an argument (e.g. '--quiet'), the 'val' is 'None'. + + Note that options provided by config files are intentionally excluded. + """ + + d = {} + + for cmd, opts in self.command_options.items(): + + for opt, (src, val) in opts.items(): + + if src != "command line": + continue + + opt = opt.replace('_', '-') + + if val == 0: + cmdobj = self.get_command_obj(cmd) + neg_opt = self.negative_opt.copy() + neg_opt.update(getattr(cmdobj, 'negative_opt', {})) + for neg, pos in neg_opt.items(): + if pos == opt: + opt = neg + val = None + break + else: + raise AssertionError("Shouldn't be able to get here") + + elif val == 1: + val = None + + d.setdefault(cmd, {})[opt] = val + + return d + + def iter_distribution_names(self): + """Yield all packages, modules, and extension names in distribution""" + + for pkg in self.packages or (): + yield pkg + + for module in self.py_modules or (): + yield module + + for ext in self.ext_modules or (): + if isinstance(ext, tuple): + name, buildinfo = ext + else: + name = ext.name + if name.endswith('module'): + name = name[:-6] + yield name + + def handle_display_options(self, option_order): + """If there were any non-global "display-only" options + (--help-commands or the metadata display options) on the command + line, display the requested info and return true; else return + false. + """ + import sys + + if six.PY2 or self.help_commands: + return _Distribution.handle_display_options(self, option_order) + + # Stdout may be StringIO (e.g. in tests) + if not isinstance(sys.stdout, io.TextIOWrapper): + return _Distribution.handle_display_options(self, option_order) + + # Don't wrap stdout if utf-8 is already the encoding. Provides + # workaround for #334. + if sys.stdout.encoding.lower() in ('utf-8', 'utf8'): + return _Distribution.handle_display_options(self, option_order) + + # Print metadata in UTF-8 no matter the platform + encoding = sys.stdout.encoding + errors = sys.stdout.errors + newline = sys.platform != 'win32' and '\n' or None + line_buffering = sys.stdout.line_buffering + + sys.stdout = io.TextIOWrapper( + sys.stdout.detach(), 'utf-8', errors, newline, line_buffering) + try: + return _Distribution.handle_display_options(self, option_order) + finally: + sys.stdout = io.TextIOWrapper( + sys.stdout.detach(), encoding, errors, newline, line_buffering) + + +class Feature: + """ + **deprecated** -- The `Feature` facility was never completely implemented + or supported, `has reported issues + `_ and will be removed in + a future version. + + A subset of the distribution that can be excluded if unneeded/wanted + + Features are created using these keyword arguments: + + 'description' -- a short, human readable description of the feature, to + be used in error messages, and option help messages. + + 'standard' -- if true, the feature is included by default if it is + available on the current system. Otherwise, the feature is only + included if requested via a command line '--with-X' option, or if + another included feature requires it. The default setting is 'False'. + + 'available' -- if true, the feature is available for installation on the + current system. The default setting is 'True'. + + 'optional' -- if true, the feature's inclusion can be controlled from the + command line, using the '--with-X' or '--without-X' options. If + false, the feature's inclusion status is determined automatically, + based on 'availabile', 'standard', and whether any other feature + requires it. The default setting is 'True'. + + 'require_features' -- a string or sequence of strings naming features + that should also be included if this feature is included. Defaults to + empty list. May also contain 'Require' objects that should be + added/removed from the distribution. + + 'remove' -- a string or list of strings naming packages to be removed + from the distribution if this feature is *not* included. If the + feature *is* included, this argument is ignored. This argument exists + to support removing features that "crosscut" a distribution, such as + defining a 'tests' feature that removes all the 'tests' subpackages + provided by other features. The default for this argument is an empty + list. (Note: the named package(s) or modules must exist in the base + distribution when the 'setup()' function is initially called.) + + other keywords -- any other keyword arguments are saved, and passed to + the distribution's 'include()' and 'exclude()' methods when the + feature is included or excluded, respectively. So, for example, you + could pass 'packages=["a","b"]' to cause packages 'a' and 'b' to be + added or removed from the distribution as appropriate. + + A feature must include at least one 'requires', 'remove', or other + keyword argument. Otherwise, it can't affect the distribution in any way. + Note also that you can subclass 'Feature' to create your own specialized + feature types that modify the distribution in other ways when included or + excluded. See the docstrings for the various methods here for more detail. + Aside from the methods, the only feature attributes that distributions look + at are 'description' and 'optional'. + """ + + @staticmethod + def warn_deprecated(): + msg = ( + "Features are deprecated and will be removed in a future " + "version. See https://github.com/pypa/setuptools/issues/65." + ) + warnings.warn(msg, DistDeprecationWarning, stacklevel=3) + + def __init__( + self, description, standard=False, available=True, + optional=True, require_features=(), remove=(), **extras): + self.warn_deprecated() + + self.description = description + self.standard = standard + self.available = available + self.optional = optional + if isinstance(require_features, (str, Require)): + require_features = require_features, + + self.require_features = [ + r for r in require_features if isinstance(r, str) + ] + er = [r for r in require_features if not isinstance(r, str)] + if er: + extras['require_features'] = er + + if isinstance(remove, str): + remove = remove, + self.remove = remove + self.extras = extras + + if not remove and not require_features and not extras: + raise DistutilsSetupError( + "Feature %s: must define 'require_features', 'remove', or " + "at least one of 'packages', 'py_modules', etc." + ) + + def include_by_default(self): + """Should this feature be included by default?""" + return self.available and self.standard + + def include_in(self, dist): + """Ensure feature and its requirements are included in distribution + + You may override this in a subclass to perform additional operations on + the distribution. Note that this method may be called more than once + per feature, and so should be idempotent. + + """ + + if not self.available: + raise DistutilsPlatformError( + self.description + " is required, " + "but is not available on this platform" + ) + + dist.include(**self.extras) + + for f in self.require_features: + dist.include_feature(f) + + def exclude_from(self, dist): + """Ensure feature is excluded from distribution + + You may override this in a subclass to perform additional operations on + the distribution. This method will be called at most once per + feature, and only after all included features have been asked to + include themselves. + """ + + dist.exclude(**self.extras) + + if self.remove: + for item in self.remove: + dist.exclude_package(item) + + def validate(self, dist): + """Verify that feature makes sense in context of distribution + + This method is called by the distribution just before it parses its + command line. It checks to ensure that the 'remove' attribute, if any, + contains only valid package/module names that are present in the base + distribution when 'setup()' is called. You may override it in a + subclass to perform any other required validation of the feature + against a target distribution. + """ + + for item in self.remove: + if not dist.has_contents_for(item): + raise DistutilsSetupError( + "%s wants to be able to remove %s, but the distribution" + " doesn't contain any packages or modules under %s" + % (self.description, item, item) + ) + + +class DistDeprecationWarning(SetuptoolsDeprecationWarning): + """Class for warning about deprecations in dist in + setuptools. Not ignored by default, unlike DeprecationWarning.""" diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/errors.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/errors.py new file mode 100644 index 0000000000..2701747f56 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/errors.py @@ -0,0 +1,16 @@ +"""setuptools.errors + +Provides exceptions used by setuptools modules. +""" + +from distutils.errors import DistutilsError + + +class RemovedCommandError(DistutilsError, RuntimeError): + """Error used for commands that have been removed in setuptools. + + Since ``setuptools`` is built on ``distutils``, simply removing a command + from ``setuptools`` will make the behavior fall back to ``distutils``; this + error is raised if a command exists in ``distutils`` but has been actively + removed in ``setuptools``. + """ diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/extension.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/extension.py new file mode 100644 index 0000000000..29468894f8 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/extension.py @@ -0,0 +1,57 @@ +import re +import functools +import distutils.core +import distutils.errors +import distutils.extension + +from setuptools.extern.six.moves import map + +from .monkey import get_unpatched + + +def _have_cython(): + """ + Return True if Cython can be imported. + """ + cython_impl = 'Cython.Distutils.build_ext' + try: + # from (cython_impl) import build_ext + __import__(cython_impl, fromlist=['build_ext']).build_ext + return True + except Exception: + pass + return False + + +# for compatibility +have_pyrex = _have_cython + +_Extension = get_unpatched(distutils.core.Extension) + + +class Extension(_Extension): + """Extension that uses '.c' files in place of '.pyx' files""" + + def __init__(self, name, sources, *args, **kw): + # The *args is needed for compatibility as calls may use positional + # arguments. py_limited_api may be set only via keyword. + self.py_limited_api = kw.pop("py_limited_api", False) + _Extension.__init__(self, name, sources, *args, **kw) + + def _convert_pyx_sources_to_lang(self): + """ + Replace sources with .pyx extensions to sources with the target + language extension. This mechanism allows language authors to supply + pre-converted sources but to prefer the .pyx sources. + """ + if _have_cython(): + # the build has Cython, so allow it to compile the .pyx files + return + lang = self.language or '' + target_ext = '.cpp' if lang.lower() == 'c++' else '.c' + sub = functools.partial(re.sub, '.pyx$', target_ext) + self.sources = list(map(sub, self.sources)) + + +class Library(Extension): + """Just like a regular Extension, but built as a library instead""" diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/extern/__init__.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/extern/__init__.py new file mode 100644 index 0000000000..e8c616f910 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/extern/__init__.py @@ -0,0 +1,73 @@ +import sys + + +class VendorImporter: + """ + A PEP 302 meta path importer for finding optionally-vendored + or otherwise naturally-installed packages from root_name. + """ + + def __init__(self, root_name, vendored_names=(), vendor_pkg=None): + self.root_name = root_name + self.vendored_names = set(vendored_names) + self.vendor_pkg = vendor_pkg or root_name.replace('extern', '_vendor') + + @property + def search_path(self): + """ + Search first the vendor package then as a natural package. + """ + yield self.vendor_pkg + '.' + yield '' + + def find_module(self, fullname, path=None): + """ + Return self when fullname starts with root_name and the + target module is one vendored through this importer. + """ + root, base, target = fullname.partition(self.root_name + '.') + if root: + return + if not any(map(target.startswith, self.vendored_names)): + return + return self + + def load_module(self, fullname): + """ + Iterate over the search path to locate and load fullname. + """ + root, base, target = fullname.partition(self.root_name + '.') + for prefix in self.search_path: + try: + extant = prefix + target + __import__(extant) + mod = sys.modules[extant] + sys.modules[fullname] = mod + # mysterious hack: + # Remove the reference to the extant package/module + # on later Python versions to cause relative imports + # in the vendor package to resolve the same modules + # as those going through this importer. + if sys.version_info >= (3, ): + del sys.modules[extant] + return mod + except ImportError: + pass + else: + raise ImportError( + "The '{target}' package is required; " + "normally this is bundled with this package so if you get " + "this warning, consult the packager of your " + "distribution.".format(**locals()) + ) + + def install(self): + """ + Install this importer into sys.meta_path if not already present. + """ + if self not in sys.meta_path: + sys.meta_path.append(self) + + +names = 'six', 'packaging', 'pyparsing', 'ordered_set', +VendorImporter(__name__, names, 'setuptools._vendor').install() diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/glob.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/glob.py new file mode 100644 index 0000000000..9d7cbc5da6 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/glob.py @@ -0,0 +1,174 @@ +""" +Filename globbing utility. Mostly a copy of `glob` from Python 3.5. + +Changes include: + * `yield from` and PEP3102 `*` removed. + * Hidden files are not ignored. +""" + +import os +import re +import fnmatch + +__all__ = ["glob", "iglob", "escape"] + + +def glob(pathname, recursive=False): + """Return a list of paths matching a pathname pattern. + + The pattern may contain simple shell-style wildcards a la + fnmatch. However, unlike fnmatch, filenames starting with a + dot are special cases that are not matched by '*' and '?' + patterns. + + If recursive is true, the pattern '**' will match any files and + zero or more directories and subdirectories. + """ + return list(iglob(pathname, recursive=recursive)) + + +def iglob(pathname, recursive=False): + """Return an iterator which yields the paths matching a pathname pattern. + + The pattern may contain simple shell-style wildcards a la + fnmatch. However, unlike fnmatch, filenames starting with a + dot are special cases that are not matched by '*' and '?' + patterns. + + If recursive is true, the pattern '**' will match any files and + zero or more directories and subdirectories. + """ + it = _iglob(pathname, recursive) + if recursive and _isrecursive(pathname): + s = next(it) # skip empty string + assert not s + return it + + +def _iglob(pathname, recursive): + dirname, basename = os.path.split(pathname) + if not has_magic(pathname): + if basename: + if os.path.lexists(pathname): + yield pathname + else: + # Patterns ending with a slash should match only directories + if os.path.isdir(dirname): + yield pathname + return + if not dirname: + if recursive and _isrecursive(basename): + for x in glob2(dirname, basename): + yield x + else: + for x in glob1(dirname, basename): + yield x + return + # `os.path.split()` returns the argument itself as a dirname if it is a + # drive or UNC path. Prevent an infinite recursion if a drive or UNC path + # contains magic characters (i.e. r'\\?\C:'). + if dirname != pathname and has_magic(dirname): + dirs = _iglob(dirname, recursive) + else: + dirs = [dirname] + if has_magic(basename): + if recursive and _isrecursive(basename): + glob_in_dir = glob2 + else: + glob_in_dir = glob1 + else: + glob_in_dir = glob0 + for dirname in dirs: + for name in glob_in_dir(dirname, basename): + yield os.path.join(dirname, name) + + +# These 2 helper functions non-recursively glob inside a literal directory. +# They return a list of basenames. `glob1` accepts a pattern while `glob0` +# takes a literal basename (so it only has to check for its existence). + + +def glob1(dirname, pattern): + if not dirname: + if isinstance(pattern, bytes): + dirname = os.curdir.encode('ASCII') + else: + dirname = os.curdir + try: + names = os.listdir(dirname) + except OSError: + return [] + return fnmatch.filter(names, pattern) + + +def glob0(dirname, basename): + if not basename: + # `os.path.split()` returns an empty basename for paths ending with a + # directory separator. 'q*x/' should match only directories. + if os.path.isdir(dirname): + return [basename] + else: + if os.path.lexists(os.path.join(dirname, basename)): + return [basename] + return [] + + +# This helper function recursively yields relative pathnames inside a literal +# directory. + + +def glob2(dirname, pattern): + assert _isrecursive(pattern) + yield pattern[:0] + for x in _rlistdir(dirname): + yield x + + +# Recursively yields relative pathnames inside a literal directory. +def _rlistdir(dirname): + if not dirname: + if isinstance(dirname, bytes): + dirname = os.curdir.encode('ASCII') + else: + dirname = os.curdir + try: + names = os.listdir(dirname) + except os.error: + return + for x in names: + yield x + path = os.path.join(dirname, x) if dirname else x + for y in _rlistdir(path): + yield os.path.join(x, y) + + +magic_check = re.compile('([*?[])') +magic_check_bytes = re.compile(b'([*?[])') + + +def has_magic(s): + if isinstance(s, bytes): + match = magic_check_bytes.search(s) + else: + match = magic_check.search(s) + return match is not None + + +def _isrecursive(pattern): + if isinstance(pattern, bytes): + return pattern == b'**' + else: + return pattern == '**' + + +def escape(pathname): + """Escape all special characters. + """ + # Escaping is done by wrapping any of "*?[" between square brackets. + # Metacharacters do not work in the drive part and shouldn't be escaped. + drive, pathname = os.path.splitdrive(pathname) + if isinstance(pathname, bytes): + pathname = magic_check_bytes.sub(br'[\1]', pathname) + else: + pathname = magic_check.sub(r'[\1]', pathname) + return drive + pathname diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/installer.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/installer.py new file mode 100644 index 0000000000..9f8be2ef84 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/installer.py @@ -0,0 +1,150 @@ +import glob +import os +import subprocess +import sys +from distutils import log +from distutils.errors import DistutilsError + +import pkg_resources +from setuptools.command.easy_install import easy_install +from setuptools.extern import six +from setuptools.wheel import Wheel + +from .py31compat import TemporaryDirectory + + +def _fixup_find_links(find_links): + """Ensure find-links option end-up being a list of strings.""" + if isinstance(find_links, six.string_types): + return find_links.split() + assert isinstance(find_links, (tuple, list)) + return find_links + + +def _legacy_fetch_build_egg(dist, req): + """Fetch an egg needed for building. + + Legacy path using EasyInstall. + """ + tmp_dist = dist.__class__({'script_args': ['easy_install']}) + opts = tmp_dist.get_option_dict('easy_install') + opts.clear() + opts.update( + (k, v) + for k, v in dist.get_option_dict('easy_install').items() + if k in ( + # don't use any other settings + 'find_links', 'site_dirs', 'index_url', + 'optimize', 'site_dirs', 'allow_hosts', + )) + if dist.dependency_links: + links = dist.dependency_links[:] + if 'find_links' in opts: + links = _fixup_find_links(opts['find_links'][1]) + links + opts['find_links'] = ('setup', links) + install_dir = dist.get_egg_cache_dir() + cmd = easy_install( + tmp_dist, args=["x"], install_dir=install_dir, + exclude_scripts=True, + always_copy=False, build_directory=None, editable=False, + upgrade=False, multi_version=True, no_report=True, user=False + ) + cmd.ensure_finalized() + return cmd.easy_install(req) + + +def fetch_build_egg(dist, req): + """Fetch an egg needed for building. + + Use pip/wheel to fetch/build a wheel.""" + # Check pip is available. + try: + pkg_resources.get_distribution('pip') + except pkg_resources.DistributionNotFound: + dist.announce( + 'WARNING: The pip package is not available, falling back ' + 'to EasyInstall for handling setup_requires/test_requires; ' + 'this is deprecated and will be removed in a future version.' + , log.WARN + ) + return _legacy_fetch_build_egg(dist, req) + # Warn if wheel is not. + try: + pkg_resources.get_distribution('wheel') + except pkg_resources.DistributionNotFound: + dist.announce('WARNING: The wheel package is not available.', log.WARN) + # Ignore environment markers; if supplied, it is required. + req = strip_marker(req) + # Take easy_install options into account, but do not override relevant + # pip environment variables (like PIP_INDEX_URL or PIP_QUIET); they'll + # take precedence. + opts = dist.get_option_dict('easy_install') + if 'allow_hosts' in opts: + raise DistutilsError('the `allow-hosts` option is not supported ' + 'when using pip to install requirements.') + if 'PIP_QUIET' in os.environ or 'PIP_VERBOSE' in os.environ: + quiet = False + else: + quiet = True + if 'PIP_INDEX_URL' in os.environ: + index_url = None + elif 'index_url' in opts: + index_url = opts['index_url'][1] + else: + index_url = None + if 'find_links' in opts: + find_links = _fixup_find_links(opts['find_links'][1])[:] + else: + find_links = [] + if dist.dependency_links: + find_links.extend(dist.dependency_links) + eggs_dir = os.path.realpath(dist.get_egg_cache_dir()) + environment = pkg_resources.Environment() + for egg_dist in pkg_resources.find_distributions(eggs_dir): + if egg_dist in req and environment.can_add(egg_dist): + return egg_dist + with TemporaryDirectory() as tmpdir: + cmd = [ + sys.executable, '-m', 'pip', + '--disable-pip-version-check', + 'wheel', '--no-deps', + '-w', tmpdir, + ] + if quiet: + cmd.append('--quiet') + if index_url is not None: + cmd.extend(('--index-url', index_url)) + if find_links is not None: + for link in find_links: + cmd.extend(('--find-links', link)) + # If requirement is a PEP 508 direct URL, directly pass + # the URL to pip, as `req @ url` does not work on the + # command line. + if req.url: + cmd.append(req.url) + else: + cmd.append(str(req)) + try: + subprocess.check_call(cmd) + except subprocess.CalledProcessError as e: + raise DistutilsError(str(e)) + wheel = Wheel(glob.glob(os.path.join(tmpdir, '*.whl'))[0]) + dist_location = os.path.join(eggs_dir, wheel.egg_name()) + wheel.install_as_egg(dist_location) + dist_metadata = pkg_resources.PathMetadata( + dist_location, os.path.join(dist_location, 'EGG-INFO')) + dist = pkg_resources.Distribution.from_filename( + dist_location, metadata=dist_metadata) + return dist + + +def strip_marker(req): + """ + Return a new requirement without the environment marker to avoid + calling pip with something like `babel; extra == "i18n"`, which + would always be ignored. + """ + # create a copy to avoid mutating the input + req = pkg_resources.Requirement.parse(str(req)) + req.marker = None + return req diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/launch.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/launch.py new file mode 100644 index 0000000000..308283ea93 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/launch.py @@ -0,0 +1,35 @@ +""" +Launch the Python script on the command line after +setuptools is bootstrapped via import. +""" + +# Note that setuptools gets imported implicitly by the +# invocation of this script using python -m setuptools.launch + +import tokenize +import sys + + +def run(): + """ + Run the script in sys.argv[1] as if it had + been invoked naturally. + """ + __builtins__ + script_name = sys.argv[1] + namespace = dict( + __file__=script_name, + __name__='__main__', + __doc__=None, + ) + sys.argv[:] = sys.argv[1:] + + open_ = getattr(tokenize, 'open', open) + script = open_(script_name).read() + norm_script = script.replace('\\r\\n', '\\n') + code = compile(norm_script, script_name, 'exec') + exec(code, namespace) + + +if __name__ == '__main__': + run() diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/lib2to3_ex.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/lib2to3_ex.py new file mode 100644 index 0000000000..4b1a73feb2 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/lib2to3_ex.py @@ -0,0 +1,62 @@ +""" +Customized Mixin2to3 support: + + - adds support for converting doctests + + +This module raises an ImportError on Python 2. +""" + +from distutils.util import Mixin2to3 as _Mixin2to3 +from distutils import log +from lib2to3.refactor import RefactoringTool, get_fixers_from_package + +import setuptools + + +class DistutilsRefactoringTool(RefactoringTool): + def log_error(self, msg, *args, **kw): + log.error(msg, *args) + + def log_message(self, msg, *args): + log.info(msg, *args) + + def log_debug(self, msg, *args): + log.debug(msg, *args) + + +class Mixin2to3(_Mixin2to3): + def run_2to3(self, files, doctests=False): + # See of the distribution option has been set, otherwise check the + # setuptools default. + if self.distribution.use_2to3 is not True: + return + if not files: + return + log.info("Fixing " + " ".join(files)) + self.__build_fixer_names() + self.__exclude_fixers() + if doctests: + if setuptools.run_2to3_on_doctests: + r = DistutilsRefactoringTool(self.fixer_names) + r.refactor(files, write=True, doctests_only=True) + else: + _Mixin2to3.run_2to3(self, files) + + def __build_fixer_names(self): + if self.fixer_names: + return + self.fixer_names = [] + for p in setuptools.lib2to3_fixer_packages: + self.fixer_names.extend(get_fixers_from_package(p)) + if self.distribution.use_2to3_fixers is not None: + for p in self.distribution.use_2to3_fixers: + self.fixer_names.extend(get_fixers_from_package(p)) + + def __exclude_fixers(self): + excluded_fixers = getattr(self, 'exclude_fixers', []) + if self.distribution.use_2to3_exclude_fixers is not None: + excluded_fixers.extend(self.distribution.use_2to3_exclude_fixers) + for fixer_name in excluded_fixers: + if fixer_name in self.fixer_names: + self.fixer_names.remove(fixer_name) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/monkey.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/monkey.py new file mode 100644 index 0000000000..3c77f8cf27 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/monkey.py @@ -0,0 +1,179 @@ +""" +Monkey patching of distutils. +""" + +import sys +import distutils.filelist +import platform +import types +import functools +from importlib import import_module +import inspect + +from setuptools.extern import six + +import setuptools + +__all__ = [] +""" +Everything is private. Contact the project team +if you think you need this functionality. +""" + + +def _get_mro(cls): + """ + Returns the bases classes for cls sorted by the MRO. + + Works around an issue on Jython where inspect.getmro will not return all + base classes if multiple classes share the same name. Instead, this + function will return a tuple containing the class itself, and the contents + of cls.__bases__. See https://github.com/pypa/setuptools/issues/1024. + """ + if platform.python_implementation() == "Jython": + return (cls,) + cls.__bases__ + return inspect.getmro(cls) + + +def get_unpatched(item): + lookup = ( + get_unpatched_class if isinstance(item, six.class_types) else + get_unpatched_function if isinstance(item, types.FunctionType) else + lambda item: None + ) + return lookup(item) + + +def get_unpatched_class(cls): + """Protect against re-patching the distutils if reloaded + + Also ensures that no other distutils extension monkeypatched the distutils + first. + """ + external_bases = ( + cls + for cls in _get_mro(cls) + if not cls.__module__.startswith('setuptools') + ) + base = next(external_bases) + if not base.__module__.startswith('distutils'): + msg = "distutils has already been patched by %r" % cls + raise AssertionError(msg) + return base + + +def patch_all(): + # we can't patch distutils.cmd, alas + distutils.core.Command = setuptools.Command + + has_issue_12885 = sys.version_info <= (3, 5, 3) + + if has_issue_12885: + # fix findall bug in distutils (http://bugs.python.org/issue12885) + distutils.filelist.findall = setuptools.findall + + needs_warehouse = ( + sys.version_info < (2, 7, 13) + or + (3, 4) < sys.version_info < (3, 4, 6) + or + (3, 5) < sys.version_info <= (3, 5, 3) + ) + + if needs_warehouse: + warehouse = 'https://upload.pypi.org/legacy/' + distutils.config.PyPIRCCommand.DEFAULT_REPOSITORY = warehouse + + _patch_distribution_metadata() + + # Install Distribution throughout the distutils + for module in distutils.dist, distutils.core, distutils.cmd: + module.Distribution = setuptools.dist.Distribution + + # Install the patched Extension + distutils.core.Extension = setuptools.extension.Extension + distutils.extension.Extension = setuptools.extension.Extension + if 'distutils.command.build_ext' in sys.modules: + sys.modules['distutils.command.build_ext'].Extension = ( + setuptools.extension.Extension + ) + + patch_for_msvc_specialized_compiler() + + +def _patch_distribution_metadata(): + """Patch write_pkg_file and read_pkg_file for higher metadata standards""" + for attr in ('write_pkg_file', 'read_pkg_file', 'get_metadata_version'): + new_val = getattr(setuptools.dist, attr) + setattr(distutils.dist.DistributionMetadata, attr, new_val) + + +def patch_func(replacement, target_mod, func_name): + """ + Patch func_name in target_mod with replacement + + Important - original must be resolved by name to avoid + patching an already patched function. + """ + original = getattr(target_mod, func_name) + + # set the 'unpatched' attribute on the replacement to + # point to the original. + vars(replacement).setdefault('unpatched', original) + + # replace the function in the original module + setattr(target_mod, func_name, replacement) + + +def get_unpatched_function(candidate): + return getattr(candidate, 'unpatched') + + +def patch_for_msvc_specialized_compiler(): + """ + Patch functions in distutils to use standalone Microsoft Visual C++ + compilers. + """ + # import late to avoid circular imports on Python < 3.5 + msvc = import_module('setuptools.msvc') + + if platform.system() != 'Windows': + # Compilers only availables on Microsoft Windows + return + + def patch_params(mod_name, func_name): + """ + Prepare the parameters for patch_func to patch indicated function. + """ + repl_prefix = 'msvc9_' if 'msvc9' in mod_name else 'msvc14_' + repl_name = repl_prefix + func_name.lstrip('_') + repl = getattr(msvc, repl_name) + mod = import_module(mod_name) + if not hasattr(mod, func_name): + raise ImportError(func_name) + return repl, mod, func_name + + # Python 2.7 to 3.4 + msvc9 = functools.partial(patch_params, 'distutils.msvc9compiler') + + # Python 3.5+ + msvc14 = functools.partial(patch_params, 'distutils._msvccompiler') + + try: + # Patch distutils.msvc9compiler + patch_func(*msvc9('find_vcvarsall')) + patch_func(*msvc9('query_vcvarsall')) + except ImportError: + pass + + try: + # Patch distutils._msvccompiler._get_vc_env + patch_func(*msvc14('_get_vc_env')) + except ImportError: + pass + + try: + # Patch distutils._msvccompiler.gen_lib_options for Numpy + patch_func(*msvc14('gen_lib_options')) + except ImportError: + pass diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/msvc.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/msvc.py new file mode 100644 index 0000000000..2ffe1c81ee --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/msvc.py @@ -0,0 +1,1679 @@ +""" +Improved support for Microsoft Visual C++ compilers. + +Known supported compilers: +-------------------------- +Microsoft Visual C++ 9.0: + Microsoft Visual C++ Compiler for Python 2.7 (x86, amd64) + Microsoft Windows SDK 6.1 (x86, x64, ia64) + Microsoft Windows SDK 7.0 (x86, x64, ia64) + +Microsoft Visual C++ 10.0: + Microsoft Windows SDK 7.1 (x86, x64, ia64) + +Microsoft Visual C++ 14.X: + Microsoft Visual C++ Build Tools 2015 (x86, x64, arm) + Microsoft Visual Studio Build Tools 2017 (x86, x64, arm, arm64) + Microsoft Visual Studio Build Tools 2019 (x86, x64, arm, arm64) + +This may also support compilers shipped with compatible Visual Studio versions. +""" + +import json +from io import open +from os import listdir, pathsep +from os.path import join, isfile, isdir, dirname +import sys +import platform +import itertools +import distutils.errors +from setuptools.extern.packaging.version import LegacyVersion + +from setuptools.extern.six.moves import filterfalse + +from .monkey import get_unpatched + +if platform.system() == 'Windows': + from setuptools.extern.six.moves import winreg + from os import environ +else: + # Mock winreg and environ so the module can be imported on this platform. + + class winreg: + HKEY_USERS = None + HKEY_CURRENT_USER = None + HKEY_LOCAL_MACHINE = None + HKEY_CLASSES_ROOT = None + + environ = dict() + +_msvc9_suppress_errors = ( + # msvc9compiler isn't available on some platforms + ImportError, + + # msvc9compiler raises DistutilsPlatformError in some + # environments. See #1118. + distutils.errors.DistutilsPlatformError, +) + +try: + from distutils.msvc9compiler import Reg +except _msvc9_suppress_errors: + pass + + +def msvc9_find_vcvarsall(version): + """ + Patched "distutils.msvc9compiler.find_vcvarsall" to use the standalone + compiler build for Python + (VCForPython / Microsoft Visual C++ Compiler for Python 2.7). + + Fall back to original behavior when the standalone compiler is not + available. + + Redirect the path of "vcvarsall.bat". + + Parameters + ---------- + version: float + Required Microsoft Visual C++ version. + + Return + ------ + str + vcvarsall.bat path + """ + vc_base = r'Software\%sMicrosoft\DevDiv\VCForPython\%0.1f' + key = vc_base % ('', version) + try: + # Per-user installs register the compiler path here + productdir = Reg.get_value(key, "installdir") + except KeyError: + try: + # All-user installs on a 64-bit system register here + key = vc_base % ('Wow6432Node\\', version) + productdir = Reg.get_value(key, "installdir") + except KeyError: + productdir = None + + if productdir: + vcvarsall = join(productdir, "vcvarsall.bat") + if isfile(vcvarsall): + return vcvarsall + + return get_unpatched(msvc9_find_vcvarsall)(version) + + +def msvc9_query_vcvarsall(ver, arch='x86', *args, **kwargs): + """ + Patched "distutils.msvc9compiler.query_vcvarsall" for support extra + Microsoft Visual C++ 9.0 and 10.0 compilers. + + Set environment without use of "vcvarsall.bat". + + Parameters + ---------- + ver: float + Required Microsoft Visual C++ version. + arch: str + Target architecture. + + Return + ------ + dict + environment + """ + # Try to get environment from vcvarsall.bat (Classical way) + try: + orig = get_unpatched(msvc9_query_vcvarsall) + return orig(ver, arch, *args, **kwargs) + except distutils.errors.DistutilsPlatformError: + # Pass error if Vcvarsall.bat is missing + pass + except ValueError: + # Pass error if environment not set after executing vcvarsall.bat + pass + + # If error, try to set environment directly + try: + return EnvironmentInfo(arch, ver).return_env() + except distutils.errors.DistutilsPlatformError as exc: + _augment_exception(exc, ver, arch) + raise + + +def msvc14_get_vc_env(plat_spec): + """ + Patched "distutils._msvccompiler._get_vc_env" for support extra + Microsoft Visual C++ 14.X compilers. + + Set environment without use of "vcvarsall.bat". + + Parameters + ---------- + plat_spec: str + Target architecture. + + Return + ------ + dict + environment + """ + # Try to get environment from vcvarsall.bat (Classical way) + try: + return get_unpatched(msvc14_get_vc_env)(plat_spec) + except distutils.errors.DistutilsPlatformError: + # Pass error Vcvarsall.bat is missing + pass + + # If error, try to set environment directly + try: + return EnvironmentInfo(plat_spec, vc_min_ver=14.0).return_env() + except distutils.errors.DistutilsPlatformError as exc: + _augment_exception(exc, 14.0) + raise + + +def msvc14_gen_lib_options(*args, **kwargs): + """ + Patched "distutils._msvccompiler.gen_lib_options" for fix + compatibility between "numpy.distutils" and "distutils._msvccompiler" + (for Numpy < 1.11.2) + """ + if "numpy.distutils" in sys.modules: + import numpy as np + if LegacyVersion(np.__version__) < LegacyVersion('1.11.2'): + return np.distutils.ccompiler.gen_lib_options(*args, **kwargs) + return get_unpatched(msvc14_gen_lib_options)(*args, **kwargs) + + +def _augment_exception(exc, version, arch=''): + """ + Add details to the exception message to help guide the user + as to what action will resolve it. + """ + # Error if MSVC++ directory not found or environment not set + message = exc.args[0] + + if "vcvarsall" in message.lower() or "visual c" in message.lower(): + # Special error message if MSVC++ not installed + tmpl = 'Microsoft Visual C++ {version:0.1f} is required.' + message = tmpl.format(**locals()) + msdownload = 'www.microsoft.com/download/details.aspx?id=%d' + if version == 9.0: + if arch.lower().find('ia64') > -1: + # For VC++ 9.0, if IA64 support is needed, redirect user + # to Windows SDK 7.0. + # Note: No download link available from Microsoft. + message += ' Get it with "Microsoft Windows SDK 7.0"' + else: + # For VC++ 9.0 redirect user to Vc++ for Python 2.7 : + # This redirection link is maintained by Microsoft. + # Contact vspython@microsoft.com if it needs updating. + message += ' Get it from http://aka.ms/vcpython27' + elif version == 10.0: + # For VC++ 10.0 Redirect user to Windows SDK 7.1 + message += ' Get it with "Microsoft Windows SDK 7.1": ' + message += msdownload % 8279 + elif version >= 14.0: + # For VC++ 14.X Redirect user to latest Visual C++ Build Tools + message += (' Get it with "Build Tools for Visual Studio": ' + r'https://visualstudio.microsoft.com/downloads/') + + exc.args = (message, ) + + +class PlatformInfo: + """ + Current and Target Architectures information. + + Parameters + ---------- + arch: str + Target architecture. + """ + current_cpu = environ.get('processor_architecture', '').lower() + + def __init__(self, arch): + self.arch = arch.lower().replace('x64', 'amd64') + + @property + def target_cpu(self): + """ + Return Target CPU architecture. + + Return + ------ + str + Target CPU + """ + return self.arch[self.arch.find('_') + 1:] + + def target_is_x86(self): + """ + Return True if target CPU is x86 32 bits.. + + Return + ------ + bool + CPU is x86 32 bits + """ + return self.target_cpu == 'x86' + + def current_is_x86(self): + """ + Return True if current CPU is x86 32 bits.. + + Return + ------ + bool + CPU is x86 32 bits + """ + return self.current_cpu == 'x86' + + def current_dir(self, hidex86=False, x64=False): + """ + Current platform specific subfolder. + + Parameters + ---------- + hidex86: bool + return '' and not '\x86' if architecture is x86. + x64: bool + return '\x64' and not '\amd64' if architecture is amd64. + + Return + ------ + str + subfolder: '\target', or '' (see hidex86 parameter) + """ + return ( + '' if (self.current_cpu == 'x86' and hidex86) else + r'\x64' if (self.current_cpu == 'amd64' and x64) else + r'\%s' % self.current_cpu + ) + + def target_dir(self, hidex86=False, x64=False): + r""" + Target platform specific subfolder. + + Parameters + ---------- + hidex86: bool + return '' and not '\x86' if architecture is x86. + x64: bool + return '\x64' and not '\amd64' if architecture is amd64. + + Return + ------ + str + subfolder: '\current', or '' (see hidex86 parameter) + """ + return ( + '' if (self.target_cpu == 'x86' and hidex86) else + r'\x64' if (self.target_cpu == 'amd64' and x64) else + r'\%s' % self.target_cpu + ) + + def cross_dir(self, forcex86=False): + r""" + Cross platform specific subfolder. + + Parameters + ---------- + forcex86: bool + Use 'x86' as current architecture even if current architecture is + not x86. + + Return + ------ + str + subfolder: '' if target architecture is current architecture, + '\current_target' if not. + """ + current = 'x86' if forcex86 else self.current_cpu + return ( + '' if self.target_cpu == current else + self.target_dir().replace('\\', '\\%s_' % current) + ) + + +class RegistryInfo: + """ + Microsoft Visual Studio related registry information. + + Parameters + ---------- + platform_info: PlatformInfo + "PlatformInfo" instance. + """ + HKEYS = (winreg.HKEY_USERS, + winreg.HKEY_CURRENT_USER, + winreg.HKEY_LOCAL_MACHINE, + winreg.HKEY_CLASSES_ROOT) + + def __init__(self, platform_info): + self.pi = platform_info + + @property + def visualstudio(self): + """ + Microsoft Visual Studio root registry key. + + Return + ------ + str + Registry key + """ + return 'VisualStudio' + + @property + def sxs(self): + """ + Microsoft Visual Studio SxS registry key. + + Return + ------ + str + Registry key + """ + return join(self.visualstudio, 'SxS') + + @property + def vc(self): + """ + Microsoft Visual C++ VC7 registry key. + + Return + ------ + str + Registry key + """ + return join(self.sxs, 'VC7') + + @property + def vs(self): + """ + Microsoft Visual Studio VS7 registry key. + + Return + ------ + str + Registry key + """ + return join(self.sxs, 'VS7') + + @property + def vc_for_python(self): + """ + Microsoft Visual C++ for Python registry key. + + Return + ------ + str + Registry key + """ + return r'DevDiv\VCForPython' + + @property + def microsoft_sdk(self): + """ + Microsoft SDK registry key. + + Return + ------ + str + Registry key + """ + return 'Microsoft SDKs' + + @property + def windows_sdk(self): + """ + Microsoft Windows/Platform SDK registry key. + + Return + ------ + str + Registry key + """ + return join(self.microsoft_sdk, 'Windows') + + @property + def netfx_sdk(self): + """ + Microsoft .NET Framework SDK registry key. + + Return + ------ + str + Registry key + """ + return join(self.microsoft_sdk, 'NETFXSDK') + + @property + def windows_kits_roots(self): + """ + Microsoft Windows Kits Roots registry key. + + Return + ------ + str + Registry key + """ + return r'Windows Kits\Installed Roots' + + def microsoft(self, key, x86=False): + """ + Return key in Microsoft software registry. + + Parameters + ---------- + key: str + Registry key path where look. + x86: str + Force x86 software registry. + + Return + ------ + str + Registry key + """ + node64 = '' if self.pi.current_is_x86() or x86 else 'Wow6432Node' + return join('Software', node64, 'Microsoft', key) + + def lookup(self, key, name): + """ + Look for values in registry in Microsoft software registry. + + Parameters + ---------- + key: str + Registry key path where look. + name: str + Value name to find. + + Return + ------ + str + value + """ + key_read = winreg.KEY_READ + openkey = winreg.OpenKey + ms = self.microsoft + for hkey in self.HKEYS: + try: + bkey = openkey(hkey, ms(key), 0, key_read) + except (OSError, IOError): + if not self.pi.current_is_x86(): + try: + bkey = openkey(hkey, ms(key, True), 0, key_read) + except (OSError, IOError): + continue + else: + continue + try: + return winreg.QueryValueEx(bkey, name)[0] + except (OSError, IOError): + pass + + +class SystemInfo: + """ + Microsoft Windows and Visual Studio related system information. + + Parameters + ---------- + registry_info: RegistryInfo + "RegistryInfo" instance. + vc_ver: float + Required Microsoft Visual C++ version. + """ + + # Variables and properties in this class use originals CamelCase variables + # names from Microsoft source files for more easy comparison. + WinDir = environ.get('WinDir', '') + ProgramFiles = environ.get('ProgramFiles', '') + ProgramFilesx86 = environ.get('ProgramFiles(x86)', ProgramFiles) + + def __init__(self, registry_info, vc_ver=None): + self.ri = registry_info + self.pi = self.ri.pi + + self.known_vs_paths = self.find_programdata_vs_vers() + + # Except for VS15+, VC version is aligned with VS version + self.vs_ver = self.vc_ver = ( + vc_ver or self._find_latest_available_vs_ver()) + + def _find_latest_available_vs_ver(self): + """ + Find the latest VC version + + Return + ------ + float + version + """ + reg_vc_vers = self.find_reg_vs_vers() + + if not (reg_vc_vers or self.known_vs_paths): + raise distutils.errors.DistutilsPlatformError( + 'No Microsoft Visual C++ version found') + + vc_vers = set(reg_vc_vers) + vc_vers.update(self.known_vs_paths) + return sorted(vc_vers)[-1] + + def find_reg_vs_vers(self): + """ + Find Microsoft Visual Studio versions available in registry. + + Return + ------ + list of float + Versions + """ + ms = self.ri.microsoft + vckeys = (self.ri.vc, self.ri.vc_for_python, self.ri.vs) + vs_vers = [] + for hkey in self.ri.HKEYS: + for key in vckeys: + try: + bkey = winreg.OpenKey(hkey, ms(key), 0, winreg.KEY_READ) + except (OSError, IOError): + continue + subkeys, values, _ = winreg.QueryInfoKey(bkey) + for i in range(values): + try: + ver = float(winreg.EnumValue(bkey, i)[0]) + if ver not in vs_vers: + vs_vers.append(ver) + except ValueError: + pass + for i in range(subkeys): + try: + ver = float(winreg.EnumKey(bkey, i)) + if ver not in vs_vers: + vs_vers.append(ver) + except ValueError: + pass + return sorted(vs_vers) + + def find_programdata_vs_vers(self): + r""" + Find Visual studio 2017+ versions from information in + "C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances". + + Return + ------ + dict + float version as key, path as value. + """ + vs_versions = {} + instances_dir = \ + r'C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances' + + try: + hashed_names = listdir(instances_dir) + + except (OSError, IOError): + # Directory not exists with all Visual Studio versions + return vs_versions + + for name in hashed_names: + try: + # Get VS installation path from "state.json" file + state_path = join(instances_dir, name, 'state.json') + with open(state_path, 'rt', encoding='utf-8') as state_file: + state = json.load(state_file) + vs_path = state['installationPath'] + + # Raises OSError if this VS installation does not contain VC + listdir(join(vs_path, r'VC\Tools\MSVC')) + + # Store version and path + vs_versions[self._as_float_version( + state['installationVersion'])] = vs_path + + except (OSError, IOError, KeyError): + # Skip if "state.json" file is missing or bad format + continue + + return vs_versions + + @staticmethod + def _as_float_version(version): + """ + Return a string version as a simplified float version (major.minor) + + Parameters + ---------- + version: str + Version. + + Return + ------ + float + version + """ + return float('.'.join(version.split('.')[:2])) + + @property + def VSInstallDir(self): + """ + Microsoft Visual Studio directory. + + Return + ------ + str + path + """ + # Default path + default = join(self.ProgramFilesx86, + 'Microsoft Visual Studio %0.1f' % self.vs_ver) + + # Try to get path from registry, if fail use default path + return self.ri.lookup(self.ri.vs, '%0.1f' % self.vs_ver) or default + + @property + def VCInstallDir(self): + """ + Microsoft Visual C++ directory. + + Return + ------ + str + path + """ + path = self._guess_vc() or self._guess_vc_legacy() + + if not isdir(path): + msg = 'Microsoft Visual C++ directory not found' + raise distutils.errors.DistutilsPlatformError(msg) + + return path + + def _guess_vc(self): + """ + Locate Visual C++ for VS2017+. + + Return + ------ + str + path + """ + if self.vs_ver <= 14.0: + return '' + + try: + # First search in known VS paths + vs_dir = self.known_vs_paths[self.vs_ver] + except KeyError: + # Else, search with path from registry + vs_dir = self.VSInstallDir + + guess_vc = join(vs_dir, r'VC\Tools\MSVC') + + # Subdir with VC exact version as name + try: + # Update the VC version with real one instead of VS version + vc_ver = listdir(guess_vc)[-1] + self.vc_ver = self._as_float_version(vc_ver) + return join(guess_vc, vc_ver) + except (OSError, IOError, IndexError): + return '' + + def _guess_vc_legacy(self): + """ + Locate Visual C++ for versions prior to 2017. + + Return + ------ + str + path + """ + default = join(self.ProgramFilesx86, + r'Microsoft Visual Studio %0.1f\VC' % self.vs_ver) + + # Try to get "VC++ for Python" path from registry as default path + reg_path = join(self.ri.vc_for_python, '%0.1f' % self.vs_ver) + python_vc = self.ri.lookup(reg_path, 'installdir') + default_vc = join(python_vc, 'VC') if python_vc else default + + # Try to get path from registry, if fail use default path + return self.ri.lookup(self.ri.vc, '%0.1f' % self.vs_ver) or default_vc + + @property + def WindowsSdkVersion(self): + """ + Microsoft Windows SDK versions for specified MSVC++ version. + + Return + ------ + tuple of str + versions + """ + if self.vs_ver <= 9.0: + return '7.0', '6.1', '6.0a' + elif self.vs_ver == 10.0: + return '7.1', '7.0a' + elif self.vs_ver == 11.0: + return '8.0', '8.0a' + elif self.vs_ver == 12.0: + return '8.1', '8.1a' + elif self.vs_ver >= 14.0: + return '10.0', '8.1' + + @property + def WindowsSdkLastVersion(self): + """ + Microsoft Windows SDK last version. + + Return + ------ + str + version + """ + return self._use_last_dir_name(join(self.WindowsSdkDir, 'lib')) + + @property + def WindowsSdkDir(self): + """ + Microsoft Windows SDK directory. + + Return + ------ + str + path + """ + sdkdir = '' + for ver in self.WindowsSdkVersion: + # Try to get it from registry + loc = join(self.ri.windows_sdk, 'v%s' % ver) + sdkdir = self.ri.lookup(loc, 'installationfolder') + if sdkdir: + break + if not sdkdir or not isdir(sdkdir): + # Try to get "VC++ for Python" version from registry + path = join(self.ri.vc_for_python, '%0.1f' % self.vc_ver) + install_base = self.ri.lookup(path, 'installdir') + if install_base: + sdkdir = join(install_base, 'WinSDK') + if not sdkdir or not isdir(sdkdir): + # If fail, use default new path + for ver in self.WindowsSdkVersion: + intver = ver[:ver.rfind('.')] + path = r'Microsoft SDKs\Windows Kits\%s' % intver + d = join(self.ProgramFiles, path) + if isdir(d): + sdkdir = d + if not sdkdir or not isdir(sdkdir): + # If fail, use default old path + for ver in self.WindowsSdkVersion: + path = r'Microsoft SDKs\Windows\v%s' % ver + d = join(self.ProgramFiles, path) + if isdir(d): + sdkdir = d + if not sdkdir: + # If fail, use Platform SDK + sdkdir = join(self.VCInstallDir, 'PlatformSDK') + return sdkdir + + @property + def WindowsSDKExecutablePath(self): + """ + Microsoft Windows SDK executable directory. + + Return + ------ + str + path + """ + # Find WinSDK NetFx Tools registry dir name + if self.vs_ver <= 11.0: + netfxver = 35 + arch = '' + else: + netfxver = 40 + hidex86 = True if self.vs_ver <= 12.0 else False + arch = self.pi.current_dir(x64=True, hidex86=hidex86) + fx = 'WinSDK-NetFx%dTools%s' % (netfxver, arch.replace('\\', '-')) + + # list all possibles registry paths + regpaths = [] + if self.vs_ver >= 14.0: + for ver in self.NetFxSdkVersion: + regpaths += [join(self.ri.netfx_sdk, ver, fx)] + + for ver in self.WindowsSdkVersion: + regpaths += [join(self.ri.windows_sdk, 'v%sA' % ver, fx)] + + # Return installation folder from the more recent path + for path in regpaths: + execpath = self.ri.lookup(path, 'installationfolder') + if execpath: + return execpath + + @property + def FSharpInstallDir(self): + """ + Microsoft Visual F# directory. + + Return + ------ + str + path + """ + path = join(self.ri.visualstudio, r'%0.1f\Setup\F#' % self.vs_ver) + return self.ri.lookup(path, 'productdir') or '' + + @property + def UniversalCRTSdkDir(self): + """ + Microsoft Universal CRT SDK directory. + + Return + ------ + str + path + """ + # Set Kit Roots versions for specified MSVC++ version + vers = ('10', '81') if self.vs_ver >= 14.0 else () + + # Find path of the more recent Kit + for ver in vers: + sdkdir = self.ri.lookup(self.ri.windows_kits_roots, + 'kitsroot%s' % ver) + if sdkdir: + return sdkdir or '' + + @property + def UniversalCRTSdkLastVersion(self): + """ + Microsoft Universal C Runtime SDK last version. + + Return + ------ + str + version + """ + return self._use_last_dir_name(join(self.UniversalCRTSdkDir, 'lib')) + + @property + def NetFxSdkVersion(self): + """ + Microsoft .NET Framework SDK versions. + + Return + ------ + tuple of str + versions + """ + # Set FxSdk versions for specified VS version + return (('4.7.2', '4.7.1', '4.7', + '4.6.2', '4.6.1', '4.6', + '4.5.2', '4.5.1', '4.5') + if self.vs_ver >= 14.0 else ()) + + @property + def NetFxSdkDir(self): + """ + Microsoft .NET Framework SDK directory. + + Return + ------ + str + path + """ + sdkdir = '' + for ver in self.NetFxSdkVersion: + loc = join(self.ri.netfx_sdk, ver) + sdkdir = self.ri.lookup(loc, 'kitsinstallationfolder') + if sdkdir: + break + return sdkdir + + @property + def FrameworkDir32(self): + """ + Microsoft .NET Framework 32bit directory. + + Return + ------ + str + path + """ + # Default path + guess_fw = join(self.WinDir, r'Microsoft.NET\Framework') + + # Try to get path from registry, if fail use default path + return self.ri.lookup(self.ri.vc, 'frameworkdir32') or guess_fw + + @property + def FrameworkDir64(self): + """ + Microsoft .NET Framework 64bit directory. + + Return + ------ + str + path + """ + # Default path + guess_fw = join(self.WinDir, r'Microsoft.NET\Framework64') + + # Try to get path from registry, if fail use default path + return self.ri.lookup(self.ri.vc, 'frameworkdir64') or guess_fw + + @property + def FrameworkVersion32(self): + """ + Microsoft .NET Framework 32bit versions. + + Return + ------ + tuple of str + versions + """ + return self._find_dot_net_versions(32) + + @property + def FrameworkVersion64(self): + """ + Microsoft .NET Framework 64bit versions. + + Return + ------ + tuple of str + versions + """ + return self._find_dot_net_versions(64) + + def _find_dot_net_versions(self, bits): + """ + Find Microsoft .NET Framework versions. + + Parameters + ---------- + bits: int + Platform number of bits: 32 or 64. + + Return + ------ + tuple of str + versions + """ + # Find actual .NET version in registry + reg_ver = self.ri.lookup(self.ri.vc, 'frameworkver%d' % bits) + dot_net_dir = getattr(self, 'FrameworkDir%d' % bits) + ver = reg_ver or self._use_last_dir_name(dot_net_dir, 'v') or '' + + # Set .NET versions for specified MSVC++ version + if self.vs_ver >= 12.0: + return ver, 'v4.0' + elif self.vs_ver >= 10.0: + return 'v4.0.30319' if ver.lower()[:2] != 'v4' else ver, 'v3.5' + elif self.vs_ver == 9.0: + return 'v3.5', 'v2.0.50727' + elif self.vs_ver == 8.0: + return 'v3.0', 'v2.0.50727' + + @staticmethod + def _use_last_dir_name(path, prefix=''): + """ + Return name of the last dir in path or '' if no dir found. + + Parameters + ---------- + path: str + Use dirs in this path + prefix: str + Use only dirs starting by this prefix + + Return + ------ + str + name + """ + matching_dirs = ( + dir_name + for dir_name in reversed(listdir(path)) + if isdir(join(path, dir_name)) and + dir_name.startswith(prefix) + ) + return next(matching_dirs, None) or '' + + +class EnvironmentInfo: + """ + Return environment variables for specified Microsoft Visual C++ version + and platform : Lib, Include, Path and libpath. + + This function is compatible with Microsoft Visual C++ 9.0 to 14.X. + + Script created by analysing Microsoft environment configuration files like + "vcvars[...].bat", "SetEnv.Cmd", "vcbuildtools.bat", ... + + Parameters + ---------- + arch: str + Target architecture. + vc_ver: float + Required Microsoft Visual C++ version. If not set, autodetect the last + version. + vc_min_ver: float + Minimum Microsoft Visual C++ version. + """ + + # Variables and properties in this class use originals CamelCase variables + # names from Microsoft source files for more easy comparison. + + def __init__(self, arch, vc_ver=None, vc_min_ver=0): + self.pi = PlatformInfo(arch) + self.ri = RegistryInfo(self.pi) + self.si = SystemInfo(self.ri, vc_ver) + + if self.vc_ver < vc_min_ver: + err = 'No suitable Microsoft Visual C++ version found' + raise distutils.errors.DistutilsPlatformError(err) + + @property + def vs_ver(self): + """ + Microsoft Visual Studio. + + Return + ------ + float + version + """ + return self.si.vs_ver + + @property + def vc_ver(self): + """ + Microsoft Visual C++ version. + + Return + ------ + float + version + """ + return self.si.vc_ver + + @property + def VSTools(self): + """ + Microsoft Visual Studio Tools. + + Return + ------ + list of str + paths + """ + paths = [r'Common7\IDE', r'Common7\Tools'] + + if self.vs_ver >= 14.0: + arch_subdir = self.pi.current_dir(hidex86=True, x64=True) + paths += [r'Common7\IDE\CommonExtensions\Microsoft\TestWindow'] + paths += [r'Team Tools\Performance Tools'] + paths += [r'Team Tools\Performance Tools%s' % arch_subdir] + + return [join(self.si.VSInstallDir, path) for path in paths] + + @property + def VCIncludes(self): + """ + Microsoft Visual C++ & Microsoft Foundation Class Includes. + + Return + ------ + list of str + paths + """ + return [join(self.si.VCInstallDir, 'Include'), + join(self.si.VCInstallDir, r'ATLMFC\Include')] + + @property + def VCLibraries(self): + """ + Microsoft Visual C++ & Microsoft Foundation Class Libraries. + + Return + ------ + list of str + paths + """ + if self.vs_ver >= 15.0: + arch_subdir = self.pi.target_dir(x64=True) + else: + arch_subdir = self.pi.target_dir(hidex86=True) + paths = ['Lib%s' % arch_subdir, r'ATLMFC\Lib%s' % arch_subdir] + + if self.vs_ver >= 14.0: + paths += [r'Lib\store%s' % arch_subdir] + + return [join(self.si.VCInstallDir, path) for path in paths] + + @property + def VCStoreRefs(self): + """ + Microsoft Visual C++ store references Libraries. + + Return + ------ + list of str + paths + """ + if self.vs_ver < 14.0: + return [] + return [join(self.si.VCInstallDir, r'Lib\store\references')] + + @property + def VCTools(self): + """ + Microsoft Visual C++ Tools. + + Return + ------ + list of str + paths + """ + si = self.si + tools = [join(si.VCInstallDir, 'VCPackages')] + + forcex86 = True if self.vs_ver <= 10.0 else False + arch_subdir = self.pi.cross_dir(forcex86) + if arch_subdir: + tools += [join(si.VCInstallDir, 'Bin%s' % arch_subdir)] + + if self.vs_ver == 14.0: + path = 'Bin%s' % self.pi.current_dir(hidex86=True) + tools += [join(si.VCInstallDir, path)] + + elif self.vs_ver >= 15.0: + host_dir = (r'bin\HostX86%s' if self.pi.current_is_x86() else + r'bin\HostX64%s') + tools += [join( + si.VCInstallDir, host_dir % self.pi.target_dir(x64=True))] + + if self.pi.current_cpu != self.pi.target_cpu: + tools += [join( + si.VCInstallDir, host_dir % self.pi.current_dir(x64=True))] + + else: + tools += [join(si.VCInstallDir, 'Bin')] + + return tools + + @property + def OSLibraries(self): + """ + Microsoft Windows SDK Libraries. + + Return + ------ + list of str + paths + """ + if self.vs_ver <= 10.0: + arch_subdir = self.pi.target_dir(hidex86=True, x64=True) + return [join(self.si.WindowsSdkDir, 'Lib%s' % arch_subdir)] + + else: + arch_subdir = self.pi.target_dir(x64=True) + lib = join(self.si.WindowsSdkDir, 'lib') + libver = self._sdk_subdir + return [join(lib, '%sum%s' % (libver , arch_subdir))] + + @property + def OSIncludes(self): + """ + Microsoft Windows SDK Include. + + Return + ------ + list of str + paths + """ + include = join(self.si.WindowsSdkDir, 'include') + + if self.vs_ver <= 10.0: + return [include, join(include, 'gl')] + + else: + if self.vs_ver >= 14.0: + sdkver = self._sdk_subdir + else: + sdkver = '' + return [join(include, '%sshared' % sdkver), + join(include, '%sum' % sdkver), + join(include, '%swinrt' % sdkver)] + + @property + def OSLibpath(self): + """ + Microsoft Windows SDK Libraries Paths. + + Return + ------ + list of str + paths + """ + ref = join(self.si.WindowsSdkDir, 'References') + libpath = [] + + if self.vs_ver <= 9.0: + libpath += self.OSLibraries + + if self.vs_ver >= 11.0: + libpath += [join(ref, r'CommonConfiguration\Neutral')] + + if self.vs_ver >= 14.0: + libpath += [ + ref, + join(self.si.WindowsSdkDir, 'UnionMetadata'), + join(ref, 'Windows.Foundation.UniversalApiContract', '1.0.0.0'), + join(ref, 'Windows.Foundation.FoundationContract', '1.0.0.0'), + join(ref,'Windows.Networking.Connectivity.WwanContract', + '1.0.0.0'), + join(self.si.WindowsSdkDir, 'ExtensionSDKs', 'Microsoft.VCLibs', + '%0.1f' % self.vs_ver, 'References', 'CommonConfiguration', + 'neutral'), + ] + return libpath + + @property + def SdkTools(self): + """ + Microsoft Windows SDK Tools. + + Return + ------ + list of str + paths + """ + return list(self._sdk_tools()) + + def _sdk_tools(self): + """ + Microsoft Windows SDK Tools paths generator. + + Return + ------ + generator of str + paths + """ + if self.vs_ver < 15.0: + bin_dir = 'Bin' if self.vs_ver <= 11.0 else r'Bin\x86' + yield join(self.si.WindowsSdkDir, bin_dir) + + if not self.pi.current_is_x86(): + arch_subdir = self.pi.current_dir(x64=True) + path = 'Bin%s' % arch_subdir + yield join(self.si.WindowsSdkDir, path) + + if self.vs_ver in (10.0, 11.0): + if self.pi.target_is_x86(): + arch_subdir = '' + else: + arch_subdir = self.pi.current_dir(hidex86=True, x64=True) + path = r'Bin\NETFX 4.0 Tools%s' % arch_subdir + yield join(self.si.WindowsSdkDir, path) + + elif self.vs_ver >= 15.0: + path = join(self.si.WindowsSdkDir, 'Bin') + arch_subdir = self.pi.current_dir(x64=True) + sdkver = self.si.WindowsSdkLastVersion + yield join(path, '%s%s' % (sdkver, arch_subdir)) + + if self.si.WindowsSDKExecutablePath: + yield self.si.WindowsSDKExecutablePath + + @property + def _sdk_subdir(self): + """ + Microsoft Windows SDK version subdir. + + Return + ------ + str + subdir + """ + ucrtver = self.si.WindowsSdkLastVersion + return ('%s\\' % ucrtver) if ucrtver else '' + + @property + def SdkSetup(self): + """ + Microsoft Windows SDK Setup. + + Return + ------ + list of str + paths + """ + if self.vs_ver > 9.0: + return [] + + return [join(self.si.WindowsSdkDir, 'Setup')] + + @property + def FxTools(self): + """ + Microsoft .NET Framework Tools. + + Return + ------ + list of str + paths + """ + pi = self.pi + si = self.si + + if self.vs_ver <= 10.0: + include32 = True + include64 = not pi.target_is_x86() and not pi.current_is_x86() + else: + include32 = pi.target_is_x86() or pi.current_is_x86() + include64 = pi.current_cpu == 'amd64' or pi.target_cpu == 'amd64' + + tools = [] + if include32: + tools += [join(si.FrameworkDir32, ver) + for ver in si.FrameworkVersion32] + if include64: + tools += [join(si.FrameworkDir64, ver) + for ver in si.FrameworkVersion64] + return tools + + @property + def NetFxSDKLibraries(self): + """ + Microsoft .Net Framework SDK Libraries. + + Return + ------ + list of str + paths + """ + if self.vs_ver < 14.0 or not self.si.NetFxSdkDir: + return [] + + arch_subdir = self.pi.target_dir(x64=True) + return [join(self.si.NetFxSdkDir, r'lib\um%s' % arch_subdir)] + + @property + def NetFxSDKIncludes(self): + """ + Microsoft .Net Framework SDK Includes. + + Return + ------ + list of str + paths + """ + if self.vs_ver < 14.0 or not self.si.NetFxSdkDir: + return [] + + return [join(self.si.NetFxSdkDir, r'include\um')] + + @property + def VsTDb(self): + """ + Microsoft Visual Studio Team System Database. + + Return + ------ + list of str + paths + """ + return [join(self.si.VSInstallDir, r'VSTSDB\Deploy')] + + @property + def MSBuild(self): + """ + Microsoft Build Engine. + + Return + ------ + list of str + paths + """ + if self.vs_ver < 12.0: + return [] + elif self.vs_ver < 15.0: + base_path = self.si.ProgramFilesx86 + arch_subdir = self.pi.current_dir(hidex86=True) + else: + base_path = self.si.VSInstallDir + arch_subdir = '' + + path = r'MSBuild\%0.1f\bin%s' % (self.vs_ver, arch_subdir) + build = [join(base_path, path)] + + if self.vs_ver >= 15.0: + # Add Roslyn C# & Visual Basic Compiler + build += [join(base_path, path, 'Roslyn')] + + return build + + @property + def HTMLHelpWorkshop(self): + """ + Microsoft HTML Help Workshop. + + Return + ------ + list of str + paths + """ + if self.vs_ver < 11.0: + return [] + + return [join(self.si.ProgramFilesx86, 'HTML Help Workshop')] + + @property + def UCRTLibraries(self): + """ + Microsoft Universal C Runtime SDK Libraries. + + Return + ------ + list of str + paths + """ + if self.vs_ver < 14.0: + return [] + + arch_subdir = self.pi.target_dir(x64=True) + lib = join(self.si.UniversalCRTSdkDir, 'lib') + ucrtver = self._ucrt_subdir + return [join(lib, '%sucrt%s' % (ucrtver, arch_subdir))] + + @property + def UCRTIncludes(self): + """ + Microsoft Universal C Runtime SDK Include. + + Return + ------ + list of str + paths + """ + if self.vs_ver < 14.0: + return [] + + include = join(self.si.UniversalCRTSdkDir, 'include') + return [join(include, '%sucrt' % self._ucrt_subdir)] + + @property + def _ucrt_subdir(self): + """ + Microsoft Universal C Runtime SDK version subdir. + + Return + ------ + str + subdir + """ + ucrtver = self.si.UniversalCRTSdkLastVersion + return ('%s\\' % ucrtver) if ucrtver else '' + + @property + def FSharp(self): + """ + Microsoft Visual F#. + + Return + ------ + list of str + paths + """ + if 11.0 > self.vs_ver > 12.0: + return [] + + return [self.si.FSharpInstallDir] + + @property + def VCRuntimeRedist(self): + """ + Microsoft Visual C++ runtime redistributable dll. + + Return + ------ + str + path + """ + vcruntime = 'vcruntime%d0.dll' % self.vc_ver + arch_subdir = self.pi.target_dir(x64=True).strip('\\') + + # Installation prefixes candidates + prefixes = [] + tools_path = self.si.VCInstallDir + redist_path = dirname(tools_path.replace(r'\Tools', r'\Redist')) + if isdir(redist_path): + # Redist version may not be exactly the same as tools + redist_path = join(redist_path, listdir(redist_path)[-1]) + prefixes += [redist_path, join(redist_path, 'onecore')] + + prefixes += [join(tools_path, 'redist')] # VS14 legacy path + + # CRT directory + crt_dirs = ('Microsoft.VC%d.CRT' % (self.vc_ver * 10), + # Sometime store in directory with VS version instead of VC + 'Microsoft.VC%d.CRT' % (int(self.vs_ver) * 10)) + + # vcruntime path + for prefix, crt_dir in itertools.product(prefixes, crt_dirs): + path = join(prefix, arch_subdir, crt_dir, vcruntime) + if isfile(path): + return path + + def return_env(self, exists=True): + """ + Return environment dict. + + Parameters + ---------- + exists: bool + It True, only return existing paths. + + Return + ------ + dict + environment + """ + env = dict( + include=self._build_paths('include', + [self.VCIncludes, + self.OSIncludes, + self.UCRTIncludes, + self.NetFxSDKIncludes], + exists), + lib=self._build_paths('lib', + [self.VCLibraries, + self.OSLibraries, + self.FxTools, + self.UCRTLibraries, + self.NetFxSDKLibraries], + exists), + libpath=self._build_paths('libpath', + [self.VCLibraries, + self.FxTools, + self.VCStoreRefs, + self.OSLibpath], + exists), + path=self._build_paths('path', + [self.VCTools, + self.VSTools, + self.VsTDb, + self.SdkTools, + self.SdkSetup, + self.FxTools, + self.MSBuild, + self.HTMLHelpWorkshop, + self.FSharp], + exists), + ) + if self.vs_ver >= 14 and isfile(self.VCRuntimeRedist): + env['py_vcruntime_redist'] = self.VCRuntimeRedist + return env + + def _build_paths(self, name, spec_path_lists, exists): + """ + Given an environment variable name and specified paths, + return a pathsep-separated string of paths containing + unique, extant, directories from those paths and from + the environment variable. Raise an error if no paths + are resolved. + + Parameters + ---------- + name: str + Environment variable name + spec_path_lists: list of str + Paths + exists: bool + It True, only return existing paths. + + Return + ------ + str + Pathsep-separated paths + """ + # flatten spec_path_lists + spec_paths = itertools.chain.from_iterable(spec_path_lists) + env_paths = environ.get(name, '').split(pathsep) + paths = itertools.chain(spec_paths, env_paths) + extant_paths = list(filter(isdir, paths)) if exists else paths + if not extant_paths: + msg = "%s environment variable is empty" % name.upper() + raise distutils.errors.DistutilsPlatformError(msg) + unique_paths = self._unique_everseen(extant_paths) + return pathsep.join(unique_paths) + + # from Python docs + @staticmethod + def _unique_everseen(iterable, key=None): + """ + List unique elements, preserving order. + Remember all elements ever seen. + + _unique_everseen('AAAABBBCCDAABBB') --> A B C D + + _unique_everseen('ABBCcAD', str.lower) --> A B C D + """ + seen = set() + seen_add = seen.add + if key is None: + for element in filterfalse(seen.__contains__, iterable): + seen_add(element) + yield element + else: + for element in iterable: + k = key(element) + if k not in seen: + seen_add(k) + yield element diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/namespaces.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/namespaces.py new file mode 100644 index 0000000000..dc16106d3d --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/namespaces.py @@ -0,0 +1,107 @@ +import os +from distutils import log +import itertools + +from setuptools.extern.six.moves import map + + +flatten = itertools.chain.from_iterable + + +class Installer: + + nspkg_ext = '-nspkg.pth' + + def install_namespaces(self): + nsp = self._get_all_ns_packages() + if not nsp: + return + filename, ext = os.path.splitext(self._get_target()) + filename += self.nspkg_ext + self.outputs.append(filename) + log.info("Installing %s", filename) + lines = map(self._gen_nspkg_line, nsp) + + if self.dry_run: + # always generate the lines, even in dry run + list(lines) + return + + with open(filename, 'wt') as f: + f.writelines(lines) + + def uninstall_namespaces(self): + filename, ext = os.path.splitext(self._get_target()) + filename += self.nspkg_ext + if not os.path.exists(filename): + return + log.info("Removing %s", filename) + os.remove(filename) + + def _get_target(self): + return self.target + + _nspkg_tmpl = ( + "import sys, types, os", + "has_mfs = sys.version_info > (3, 5)", + "p = os.path.join(%(root)s, *%(pth)r)", + "importlib = has_mfs and __import__('importlib.util')", + "has_mfs and __import__('importlib.machinery')", + "m = has_mfs and " + "sys.modules.setdefault(%(pkg)r, " + "importlib.util.module_from_spec(" + "importlib.machinery.PathFinder.find_spec(%(pkg)r, " + "[os.path.dirname(p)])))", + "m = m or " + "sys.modules.setdefault(%(pkg)r, types.ModuleType(%(pkg)r))", + "mp = (m or []) and m.__dict__.setdefault('__path__',[])", + "(p not in mp) and mp.append(p)", + ) + "lines for the namespace installer" + + _nspkg_tmpl_multi = ( + 'm and setattr(sys.modules[%(parent)r], %(child)r, m)', + ) + "additional line(s) when a parent package is indicated" + + def _get_root(self): + return "sys._getframe(1).f_locals['sitedir']" + + def _gen_nspkg_line(self, pkg): + # ensure pkg is not a unicode string under Python 2.7 + pkg = str(pkg) + pth = tuple(pkg.split('.')) + root = self._get_root() + tmpl_lines = self._nspkg_tmpl + parent, sep, child = pkg.rpartition('.') + if parent: + tmpl_lines += self._nspkg_tmpl_multi + return ';'.join(tmpl_lines) % locals() + '\n' + + def _get_all_ns_packages(self): + """Return sorted list of all package namespaces""" + pkgs = self.distribution.namespace_packages or [] + return sorted(flatten(map(self._pkg_names, pkgs))) + + @staticmethod + def _pkg_names(pkg): + """ + Given a namespace package, yield the components of that + package. + + >>> names = Installer._pkg_names('a.b.c') + >>> set(names) == set(['a', 'a.b', 'a.b.c']) + True + """ + parts = pkg.split('.') + while parts: + yield '.'.join(parts) + parts.pop() + + +class DevelopInstaller(Installer): + def _get_root(self): + return repr(str(self.egg_path)) + + def _get_target(self): + return self.egg_link diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/package_index.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/package_index.py new file mode 100644 index 0000000000..f419d47167 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/package_index.py @@ -0,0 +1,1136 @@ +"""PyPI and direct package downloading""" +import sys +import os +import re +import shutil +import socket +import base64 +import hashlib +import itertools +import warnings +from functools import wraps + +from setuptools.extern import six +from setuptools.extern.six.moves import urllib, http_client, configparser, map + +import setuptools +from pkg_resources import ( + CHECKOUT_DIST, Distribution, BINARY_DIST, normalize_path, SOURCE_DIST, + Environment, find_distributions, safe_name, safe_version, + to_filename, Requirement, DEVELOP_DIST, EGG_DIST, +) +from setuptools import ssl_support +from distutils import log +from distutils.errors import DistutilsError +from fnmatch import translate +from setuptools.py27compat import get_all_headers +from setuptools.py33compat import unescape +from setuptools.wheel import Wheel + +__metaclass__ = type + +EGG_FRAGMENT = re.compile(r'^egg=([-A-Za-z0-9_.+!]+)$') +HREF = re.compile(r"""href\s*=\s*['"]?([^'"> ]+)""", re.I) +PYPI_MD5 = re.compile( + r'([^<]+)\n\s+\(md5\)' +) +URL_SCHEME = re.compile('([-+.a-z0-9]{2,}):', re.I).match +EXTENSIONS = ".tar.gz .tar.bz2 .tar .zip .tgz".split() + +__all__ = [ + 'PackageIndex', 'distros_for_url', 'parse_bdist_wininst', + 'interpret_distro_name', +] + +_SOCKET_TIMEOUT = 15 + +_tmpl = "setuptools/{setuptools.__version__} Python-urllib/{py_major}" +user_agent = _tmpl.format(py_major='{}.{}'.format(*sys.version_info), setuptools=setuptools) + + +def parse_requirement_arg(spec): + try: + return Requirement.parse(spec) + except ValueError: + raise DistutilsError( + "Not a URL, existing file, or requirement spec: %r" % (spec,) + ) + + +def parse_bdist_wininst(name): + """Return (base,pyversion) or (None,None) for possible .exe name""" + + lower = name.lower() + base, py_ver, plat = None, None, None + + if lower.endswith('.exe'): + if lower.endswith('.win32.exe'): + base = name[:-10] + plat = 'win32' + elif lower.startswith('.win32-py', -16): + py_ver = name[-7:-4] + base = name[:-16] + plat = 'win32' + elif lower.endswith('.win-amd64.exe'): + base = name[:-14] + plat = 'win-amd64' + elif lower.startswith('.win-amd64-py', -20): + py_ver = name[-7:-4] + base = name[:-20] + plat = 'win-amd64' + return base, py_ver, plat + + +def egg_info_for_url(url): + parts = urllib.parse.urlparse(url) + scheme, server, path, parameters, query, fragment = parts + base = urllib.parse.unquote(path.split('/')[-1]) + if server == 'sourceforge.net' and base == 'download': # XXX Yuck + base = urllib.parse.unquote(path.split('/')[-2]) + if '#' in base: + base, fragment = base.split('#', 1) + return base, fragment + + +def distros_for_url(url, metadata=None): + """Yield egg or source distribution objects that might be found at a URL""" + base, fragment = egg_info_for_url(url) + for dist in distros_for_location(url, base, metadata): + yield dist + if fragment: + match = EGG_FRAGMENT.match(fragment) + if match: + for dist in interpret_distro_name( + url, match.group(1), metadata, precedence=CHECKOUT_DIST + ): + yield dist + + +def distros_for_location(location, basename, metadata=None): + """Yield egg or source distribution objects based on basename""" + if basename.endswith('.egg.zip'): + basename = basename[:-4] # strip the .zip + if basename.endswith('.egg') and '-' in basename: + # only one, unambiguous interpretation + return [Distribution.from_location(location, basename, metadata)] + if basename.endswith('.whl') and '-' in basename: + wheel = Wheel(basename) + if not wheel.is_compatible(): + return [] + return [Distribution( + location=location, + project_name=wheel.project_name, + version=wheel.version, + # Increase priority over eggs. + precedence=EGG_DIST + 1, + )] + if basename.endswith('.exe'): + win_base, py_ver, platform = parse_bdist_wininst(basename) + if win_base is not None: + return interpret_distro_name( + location, win_base, metadata, py_ver, BINARY_DIST, platform + ) + # Try source distro extensions (.zip, .tgz, etc.) + # + for ext in EXTENSIONS: + if basename.endswith(ext): + basename = basename[:-len(ext)] + return interpret_distro_name(location, basename, metadata) + return [] # no extension matched + + +def distros_for_filename(filename, metadata=None): + """Yield possible egg or source distribution objects based on a filename""" + return distros_for_location( + normalize_path(filename), os.path.basename(filename), metadata + ) + + +def interpret_distro_name( + location, basename, metadata, py_version=None, precedence=SOURCE_DIST, + platform=None +): + """Generate alternative interpretations of a source distro name + + Note: if `location` is a filesystem filename, you should call + ``pkg_resources.normalize_path()`` on it before passing it to this + routine! + """ + # Generate alternative interpretations of a source distro name + # Because some packages are ambiguous as to name/versions split + # e.g. "adns-python-1.1.0", "egenix-mx-commercial", etc. + # So, we generate each possible interepretation (e.g. "adns, python-1.1.0" + # "adns-python, 1.1.0", and "adns-python-1.1.0, no version"). In practice, + # the spurious interpretations should be ignored, because in the event + # there's also an "adns" package, the spurious "python-1.1.0" version will + # compare lower than any numeric version number, and is therefore unlikely + # to match a request for it. It's still a potential problem, though, and + # in the long run PyPI and the distutils should go for "safe" names and + # versions in distribution archive names (sdist and bdist). + + parts = basename.split('-') + if not py_version and any(re.match(r'py\d\.\d$', p) for p in parts[2:]): + # it is a bdist_dumb, not an sdist -- bail out + return + + for p in range(1, len(parts) + 1): + yield Distribution( + location, metadata, '-'.join(parts[:p]), '-'.join(parts[p:]), + py_version=py_version, precedence=precedence, + platform=platform + ) + + +# From Python 2.7 docs +def unique_everseen(iterable, key=None): + "List unique elements, preserving order. Remember all elements ever seen." + # unique_everseen('AAAABBBCCDAABBB') --> A B C D + # unique_everseen('ABBCcAD', str.lower) --> A B C D + seen = set() + seen_add = seen.add + if key is None: + for element in six.moves.filterfalse(seen.__contains__, iterable): + seen_add(element) + yield element + else: + for element in iterable: + k = key(element) + if k not in seen: + seen_add(k) + yield element + + +def unique_values(func): + """ + Wrap a function returning an iterable such that the resulting iterable + only ever yields unique items. + """ + + @wraps(func) + def wrapper(*args, **kwargs): + return unique_everseen(func(*args, **kwargs)) + + return wrapper + + +REL = re.compile(r"""<([^>]*\srel\s*=\s*['"]?([^'">]+)[^>]*)>""", re.I) +# this line is here to fix emacs' cruddy broken syntax highlighting + + +@unique_values +def find_external_links(url, page): + """Find rel="homepage" and rel="download" links in `page`, yielding URLs""" + + for match in REL.finditer(page): + tag, rel = match.groups() + rels = set(map(str.strip, rel.lower().split(','))) + if 'homepage' in rels or 'download' in rels: + for match in HREF.finditer(tag): + yield urllib.parse.urljoin(url, htmldecode(match.group(1))) + + for tag in ("Home Page", "Download URL"): + pos = page.find(tag) + if pos != -1: + match = HREF.search(page, pos) + if match: + yield urllib.parse.urljoin(url, htmldecode(match.group(1))) + + +class ContentChecker: + """ + A null content checker that defines the interface for checking content + """ + + def feed(self, block): + """ + Feed a block of data to the hash. + """ + return + + def is_valid(self): + """ + Check the hash. Return False if validation fails. + """ + return True + + def report(self, reporter, template): + """ + Call reporter with information about the checker (hash name) + substituted into the template. + """ + return + + +class HashChecker(ContentChecker): + pattern = re.compile( + r'(?Psha1|sha224|sha384|sha256|sha512|md5)=' + r'(?P[a-f0-9]+)' + ) + + def __init__(self, hash_name, expected): + self.hash_name = hash_name + self.hash = hashlib.new(hash_name) + self.expected = expected + + @classmethod + def from_url(cls, url): + "Construct a (possibly null) ContentChecker from a URL" + fragment = urllib.parse.urlparse(url)[-1] + if not fragment: + return ContentChecker() + match = cls.pattern.search(fragment) + if not match: + return ContentChecker() + return cls(**match.groupdict()) + + def feed(self, block): + self.hash.update(block) + + def is_valid(self): + return self.hash.hexdigest() == self.expected + + def report(self, reporter, template): + msg = template % self.hash_name + return reporter(msg) + + +class PackageIndex(Environment): + """A distribution index that scans web pages for download URLs""" + + def __init__( + self, index_url="https://pypi.org/simple/", hosts=('*',), + ca_bundle=None, verify_ssl=True, *args, **kw + ): + Environment.__init__(self, *args, **kw) + self.index_url = index_url + "/" [:not index_url.endswith('/')] + self.scanned_urls = {} + self.fetched_urls = {} + self.package_pages = {} + self.allows = re.compile('|'.join(map(translate, hosts))).match + self.to_scan = [] + use_ssl = ( + verify_ssl + and ssl_support.is_available + and (ca_bundle or ssl_support.find_ca_bundle()) + ) + if use_ssl: + self.opener = ssl_support.opener_for(ca_bundle) + else: + self.opener = urllib.request.urlopen + + def process_url(self, url, retrieve=False): + """Evaluate a URL as a possible download, and maybe retrieve it""" + if url in self.scanned_urls and not retrieve: + return + self.scanned_urls[url] = True + if not URL_SCHEME(url): + self.process_filename(url) + return + else: + dists = list(distros_for_url(url)) + if dists: + if not self.url_ok(url): + return + self.debug("Found link: %s", url) + + if dists or not retrieve or url in self.fetched_urls: + list(map(self.add, dists)) + return # don't need the actual page + + if not self.url_ok(url): + self.fetched_urls[url] = True + return + + self.info("Reading %s", url) + self.fetched_urls[url] = True # prevent multiple fetch attempts + tmpl = "Download error on %s: %%s -- Some packages may not be found!" + f = self.open_url(url, tmpl % url) + if f is None: + return + self.fetched_urls[f.url] = True + if 'html' not in f.headers.get('content-type', '').lower(): + f.close() # not html, we can't process it + return + + base = f.url # handle redirects + page = f.read() + if not isinstance(page, str): + # In Python 3 and got bytes but want str. + if isinstance(f, urllib.error.HTTPError): + # Errors have no charset, assume latin1: + charset = 'latin-1' + else: + charset = f.headers.get_param('charset') or 'latin-1' + page = page.decode(charset, "ignore") + f.close() + for match in HREF.finditer(page): + link = urllib.parse.urljoin(base, htmldecode(match.group(1))) + self.process_url(link) + if url.startswith(self.index_url) and getattr(f, 'code', None) != 404: + page = self.process_index(url, page) + + def process_filename(self, fn, nested=False): + # process filenames or directories + if not os.path.exists(fn): + self.warn("Not found: %s", fn) + return + + if os.path.isdir(fn) and not nested: + path = os.path.realpath(fn) + for item in os.listdir(path): + self.process_filename(os.path.join(path, item), True) + + dists = distros_for_filename(fn) + if dists: + self.debug("Found: %s", fn) + list(map(self.add, dists)) + + def url_ok(self, url, fatal=False): + s = URL_SCHEME(url) + is_file = s and s.group(1).lower() == 'file' + if is_file or self.allows(urllib.parse.urlparse(url)[1]): + return True + msg = ( + "\nNote: Bypassing %s (disallowed host; see " + "http://bit.ly/2hrImnY for details).\n") + if fatal: + raise DistutilsError(msg % url) + else: + self.warn(msg, url) + + def scan_egg_links(self, search_path): + dirs = filter(os.path.isdir, search_path) + egg_links = ( + (path, entry) + for path in dirs + for entry in os.listdir(path) + if entry.endswith('.egg-link') + ) + list(itertools.starmap(self.scan_egg_link, egg_links)) + + def scan_egg_link(self, path, entry): + with open(os.path.join(path, entry)) as raw_lines: + # filter non-empty lines + lines = list(filter(None, map(str.strip, raw_lines))) + + if len(lines) != 2: + # format is not recognized; punt + return + + egg_path, setup_path = lines + + for dist in find_distributions(os.path.join(path, egg_path)): + dist.location = os.path.join(path, *lines) + dist.precedence = SOURCE_DIST + self.add(dist) + + def process_index(self, url, page): + """Process the contents of a PyPI page""" + + def scan(link): + # Process a URL to see if it's for a package page + if link.startswith(self.index_url): + parts = list(map( + urllib.parse.unquote, link[len(self.index_url):].split('/') + )) + if len(parts) == 2 and '#' not in parts[1]: + # it's a package page, sanitize and index it + pkg = safe_name(parts[0]) + ver = safe_version(parts[1]) + self.package_pages.setdefault(pkg.lower(), {})[link] = True + return to_filename(pkg), to_filename(ver) + return None, None + + # process an index page into the package-page index + for match in HREF.finditer(page): + try: + scan(urllib.parse.urljoin(url, htmldecode(match.group(1)))) + except ValueError: + pass + + pkg, ver = scan(url) # ensure this page is in the page index + if pkg: + # process individual package page + for new_url in find_external_links(url, page): + # Process the found URL + base, frag = egg_info_for_url(new_url) + if base.endswith('.py') and not frag: + if ver: + new_url += '#egg=%s-%s' % (pkg, ver) + else: + self.need_version_info(url) + self.scan_url(new_url) + + return PYPI_MD5.sub( + lambda m: '%s' % m.group(1, 3, 2), page + ) + else: + return "" # no sense double-scanning non-package pages + + def need_version_info(self, url): + self.scan_all( + "Page at %s links to .py file(s) without version info; an index " + "scan is required.", url + ) + + def scan_all(self, msg=None, *args): + if self.index_url not in self.fetched_urls: + if msg: + self.warn(msg, *args) + self.info( + "Scanning index of all packages (this may take a while)" + ) + self.scan_url(self.index_url) + + def find_packages(self, requirement): + self.scan_url(self.index_url + requirement.unsafe_name + '/') + + if not self.package_pages.get(requirement.key): + # Fall back to safe version of the name + self.scan_url(self.index_url + requirement.project_name + '/') + + if not self.package_pages.get(requirement.key): + # We couldn't find the target package, so search the index page too + self.not_found_in_index(requirement) + + for url in list(self.package_pages.get(requirement.key, ())): + # scan each page that might be related to the desired package + self.scan_url(url) + + def obtain(self, requirement, installer=None): + self.prescan() + self.find_packages(requirement) + for dist in self[requirement.key]: + if dist in requirement: + return dist + self.debug("%s does not match %s", requirement, dist) + return super(PackageIndex, self).obtain(requirement, installer) + + def check_hash(self, checker, filename, tfp): + """ + checker is a ContentChecker + """ + checker.report( + self.debug, + "Validating %%s checksum for %s" % filename) + if not checker.is_valid(): + tfp.close() + os.unlink(filename) + raise DistutilsError( + "%s validation failed for %s; " + "possible download problem?" + % (checker.hash.name, os.path.basename(filename)) + ) + + def add_find_links(self, urls): + """Add `urls` to the list that will be prescanned for searches""" + for url in urls: + if ( + self.to_scan is None # if we have already "gone online" + or not URL_SCHEME(url) # or it's a local file/directory + or url.startswith('file:') + or list(distros_for_url(url)) # or a direct package link + ): + # then go ahead and process it now + self.scan_url(url) + else: + # otherwise, defer retrieval till later + self.to_scan.append(url) + + def prescan(self): + """Scan urls scheduled for prescanning (e.g. --find-links)""" + if self.to_scan: + list(map(self.scan_url, self.to_scan)) + self.to_scan = None # from now on, go ahead and process immediately + + def not_found_in_index(self, requirement): + if self[requirement.key]: # we've seen at least one distro + meth, msg = self.info, "Couldn't retrieve index page for %r" + else: # no distros seen for this name, might be misspelled + meth, msg = ( + self.warn, + "Couldn't find index page for %r (maybe misspelled?)") + meth(msg, requirement.unsafe_name) + self.scan_all() + + def download(self, spec, tmpdir): + """Locate and/or download `spec` to `tmpdir`, returning a local path + + `spec` may be a ``Requirement`` object, or a string containing a URL, + an existing local filename, or a project/version requirement spec + (i.e. the string form of a ``Requirement`` object). If it is the URL + of a .py file with an unambiguous ``#egg=name-version`` tag (i.e., one + that escapes ``-`` as ``_`` throughout), a trivial ``setup.py`` is + automatically created alongside the downloaded file. + + If `spec` is a ``Requirement`` object or a string containing a + project/version requirement spec, this method returns the location of + a matching distribution (possibly after downloading it to `tmpdir`). + If `spec` is a locally existing file or directory name, it is simply + returned unchanged. If `spec` is a URL, it is downloaded to a subpath + of `tmpdir`, and the local filename is returned. Various errors may be + raised if a problem occurs during downloading. + """ + if not isinstance(spec, Requirement): + scheme = URL_SCHEME(spec) + if scheme: + # It's a url, download it to tmpdir + found = self._download_url(scheme.group(1), spec, tmpdir) + base, fragment = egg_info_for_url(spec) + if base.endswith('.py'): + found = self.gen_setup(found, fragment, tmpdir) + return found + elif os.path.exists(spec): + # Existing file or directory, just return it + return spec + else: + spec = parse_requirement_arg(spec) + return getattr(self.fetch_distribution(spec, tmpdir), 'location', None) + + def fetch_distribution( + self, requirement, tmpdir, force_scan=False, source=False, + develop_ok=False, local_index=None): + """Obtain a distribution suitable for fulfilling `requirement` + + `requirement` must be a ``pkg_resources.Requirement`` instance. + If necessary, or if the `force_scan` flag is set, the requirement is + searched for in the (online) package index as well as the locally + installed packages. If a distribution matching `requirement` is found, + the returned distribution's ``location`` is the value you would have + gotten from calling the ``download()`` method with the matching + distribution's URL or filename. If no matching distribution is found, + ``None`` is returned. + + If the `source` flag is set, only source distributions and source + checkout links will be considered. Unless the `develop_ok` flag is + set, development and system eggs (i.e., those using the ``.egg-info`` + format) will be ignored. + """ + # process a Requirement + self.info("Searching for %s", requirement) + skipped = {} + dist = None + + def find(req, env=None): + if env is None: + env = self + # Find a matching distribution; may be called more than once + + for dist in env[req.key]: + + if dist.precedence == DEVELOP_DIST and not develop_ok: + if dist not in skipped: + self.warn( + "Skipping development or system egg: %s", dist, + ) + skipped[dist] = 1 + continue + + test = ( + dist in req + and (dist.precedence <= SOURCE_DIST or not source) + ) + if test: + loc = self.download(dist.location, tmpdir) + dist.download_location = loc + if os.path.exists(dist.download_location): + return dist + + if force_scan: + self.prescan() + self.find_packages(requirement) + dist = find(requirement) + + if not dist and local_index is not None: + dist = find(requirement, local_index) + + if dist is None: + if self.to_scan is not None: + self.prescan() + dist = find(requirement) + + if dist is None and not force_scan: + self.find_packages(requirement) + dist = find(requirement) + + if dist is None: + self.warn( + "No local packages or working download links found for %s%s", + (source and "a source distribution of " or ""), + requirement, + ) + else: + self.info("Best match: %s", dist) + return dist.clone(location=dist.download_location) + + def fetch(self, requirement, tmpdir, force_scan=False, source=False): + """Obtain a file suitable for fulfilling `requirement` + + DEPRECATED; use the ``fetch_distribution()`` method now instead. For + backward compatibility, this routine is identical but returns the + ``location`` of the downloaded distribution instead of a distribution + object. + """ + dist = self.fetch_distribution(requirement, tmpdir, force_scan, source) + if dist is not None: + return dist.location + return None + + def gen_setup(self, filename, fragment, tmpdir): + match = EGG_FRAGMENT.match(fragment) + dists = match and [ + d for d in + interpret_distro_name(filename, match.group(1), None) if d.version + ] or [] + + if len(dists) == 1: # unambiguous ``#egg`` fragment + basename = os.path.basename(filename) + + # Make sure the file has been downloaded to the temp dir. + if os.path.dirname(filename) != tmpdir: + dst = os.path.join(tmpdir, basename) + from setuptools.command.easy_install import samefile + if not samefile(filename, dst): + shutil.copy2(filename, dst) + filename = dst + + with open(os.path.join(tmpdir, 'setup.py'), 'w') as file: + file.write( + "from setuptools import setup\n" + "setup(name=%r, version=%r, py_modules=[%r])\n" + % ( + dists[0].project_name, dists[0].version, + os.path.splitext(basename)[0] + ) + ) + return filename + + elif match: + raise DistutilsError( + "Can't unambiguously interpret project/version identifier %r; " + "any dashes in the name or version should be escaped using " + "underscores. %r" % (fragment, dists) + ) + else: + raise DistutilsError( + "Can't process plain .py files without an '#egg=name-version'" + " suffix to enable automatic setup script generation." + ) + + dl_blocksize = 8192 + + def _download_to(self, url, filename): + self.info("Downloading %s", url) + # Download the file + fp = None + try: + checker = HashChecker.from_url(url) + fp = self.open_url(url) + if isinstance(fp, urllib.error.HTTPError): + raise DistutilsError( + "Can't download %s: %s %s" % (url, fp.code, fp.msg) + ) + headers = fp.info() + blocknum = 0 + bs = self.dl_blocksize + size = -1 + if "content-length" in headers: + # Some servers return multiple Content-Length headers :( + sizes = get_all_headers(headers, 'Content-Length') + size = max(map(int, sizes)) + self.reporthook(url, filename, blocknum, bs, size) + with open(filename, 'wb') as tfp: + while True: + block = fp.read(bs) + if block: + checker.feed(block) + tfp.write(block) + blocknum += 1 + self.reporthook(url, filename, blocknum, bs, size) + else: + break + self.check_hash(checker, filename, tfp) + return headers + finally: + if fp: + fp.close() + + def reporthook(self, url, filename, blocknum, blksize, size): + pass # no-op + + def open_url(self, url, warning=None): + if url.startswith('file:'): + return local_open(url) + try: + return open_with_auth(url, self.opener) + except (ValueError, http_client.InvalidURL) as v: + msg = ' '.join([str(arg) for arg in v.args]) + if warning: + self.warn(warning, msg) + else: + raise DistutilsError('%s %s' % (url, msg)) + except urllib.error.HTTPError as v: + return v + except urllib.error.URLError as v: + if warning: + self.warn(warning, v.reason) + else: + raise DistutilsError("Download error for %s: %s" + % (url, v.reason)) + except http_client.BadStatusLine as v: + if warning: + self.warn(warning, v.line) + else: + raise DistutilsError( + '%s returned a bad status line. The server might be ' + 'down, %s' % + (url, v.line) + ) + except (http_client.HTTPException, socket.error) as v: + if warning: + self.warn(warning, v) + else: + raise DistutilsError("Download error for %s: %s" + % (url, v)) + + def _download_url(self, scheme, url, tmpdir): + # Determine download filename + # + name, fragment = egg_info_for_url(url) + if name: + while '..' in name: + name = name.replace('..', '.').replace('\\', '_') + else: + name = "__downloaded__" # default if URL has no path contents + + if name.endswith('.egg.zip'): + name = name[:-4] # strip the extra .zip before download + + filename = os.path.join(tmpdir, name) + + # Download the file + # + if scheme == 'svn' or scheme.startswith('svn+'): + return self._download_svn(url, filename) + elif scheme == 'git' or scheme.startswith('git+'): + return self._download_git(url, filename) + elif scheme.startswith('hg+'): + return self._download_hg(url, filename) + elif scheme == 'file': + return urllib.request.url2pathname(urllib.parse.urlparse(url)[2]) + else: + self.url_ok(url, True) # raises error if not allowed + return self._attempt_download(url, filename) + + def scan_url(self, url): + self.process_url(url, True) + + def _attempt_download(self, url, filename): + headers = self._download_to(url, filename) + if 'html' in headers.get('content-type', '').lower(): + return self._download_html(url, headers, filename) + else: + return filename + + def _download_html(self, url, headers, filename): + file = open(filename) + for line in file: + if line.strip(): + # Check for a subversion index page + if re.search(r'([^- ]+ - )?Revision \d+:', line): + # it's a subversion index page: + file.close() + os.unlink(filename) + return self._download_svn(url, filename) + break # not an index page + file.close() + os.unlink(filename) + raise DistutilsError("Unexpected HTML page found at " + url) + + def _download_svn(self, url, filename): + warnings.warn("SVN download support is deprecated", UserWarning) + url = url.split('#', 1)[0] # remove any fragment for svn's sake + creds = '' + if url.lower().startswith('svn:') and '@' in url: + scheme, netloc, path, p, q, f = urllib.parse.urlparse(url) + if not netloc and path.startswith('//') and '/' in path[2:]: + netloc, path = path[2:].split('/', 1) + auth, host = _splituser(netloc) + if auth: + if ':' in auth: + user, pw = auth.split(':', 1) + creds = " --username=%s --password=%s" % (user, pw) + else: + creds = " --username=" + auth + netloc = host + parts = scheme, netloc, url, p, q, f + url = urllib.parse.urlunparse(parts) + self.info("Doing subversion checkout from %s to %s", url, filename) + os.system("svn checkout%s -q %s %s" % (creds, url, filename)) + return filename + + @staticmethod + def _vcs_split_rev_from_url(url, pop_prefix=False): + scheme, netloc, path, query, frag = urllib.parse.urlsplit(url) + + scheme = scheme.split('+', 1)[-1] + + # Some fragment identification fails + path = path.split('#', 1)[0] + + rev = None + if '@' in path: + path, rev = path.rsplit('@', 1) + + # Also, discard fragment + url = urllib.parse.urlunsplit((scheme, netloc, path, query, '')) + + return url, rev + + def _download_git(self, url, filename): + filename = filename.split('#', 1)[0] + url, rev = self._vcs_split_rev_from_url(url, pop_prefix=True) + + self.info("Doing git clone from %s to %s", url, filename) + os.system("git clone --quiet %s %s" % (url, filename)) + + if rev is not None: + self.info("Checking out %s", rev) + os.system("git -C %s checkout --quiet %s" % ( + filename, + rev, + )) + + return filename + + def _download_hg(self, url, filename): + filename = filename.split('#', 1)[0] + url, rev = self._vcs_split_rev_from_url(url, pop_prefix=True) + + self.info("Doing hg clone from %s to %s", url, filename) + os.system("hg clone --quiet %s %s" % (url, filename)) + + if rev is not None: + self.info("Updating to %s", rev) + os.system("hg --cwd %s up -C -r %s -q" % ( + filename, + rev, + )) + + return filename + + def debug(self, msg, *args): + log.debug(msg, *args) + + def info(self, msg, *args): + log.info(msg, *args) + + def warn(self, msg, *args): + log.warn(msg, *args) + + +# This pattern matches a character entity reference (a decimal numeric +# references, a hexadecimal numeric reference, or a named reference). +entity_sub = re.compile(r'&(#(\d+|x[\da-fA-F]+)|[\w.:-]+);?').sub + + +def decode_entity(match): + what = match.group(0) + return unescape(what) + + +def htmldecode(text): + """ + Decode HTML entities in the given text. + + >>> htmldecode( + ... 'https://../package_name-0.1.2.tar.gz' + ... '?tokena=A&tokenb=B">package_name-0.1.2.tar.gz') + 'https://../package_name-0.1.2.tar.gz?tokena=A&tokenb=B">package_name-0.1.2.tar.gz' + """ + return entity_sub(decode_entity, text) + + +def socket_timeout(timeout=15): + def _socket_timeout(func): + def _socket_timeout(*args, **kwargs): + old_timeout = socket.getdefaulttimeout() + socket.setdefaulttimeout(timeout) + try: + return func(*args, **kwargs) + finally: + socket.setdefaulttimeout(old_timeout) + + return _socket_timeout + + return _socket_timeout + + +def _encode_auth(auth): + """ + A function compatible with Python 2.3-3.3 that will encode + auth from a URL suitable for an HTTP header. + >>> str(_encode_auth('username%3Apassword')) + 'dXNlcm5hbWU6cGFzc3dvcmQ=' + + Long auth strings should not cause a newline to be inserted. + >>> long_auth = 'username:' + 'password'*10 + >>> chr(10) in str(_encode_auth(long_auth)) + False + """ + auth_s = urllib.parse.unquote(auth) + # convert to bytes + auth_bytes = auth_s.encode() + encoded_bytes = base64.b64encode(auth_bytes) + # convert back to a string + encoded = encoded_bytes.decode() + # strip the trailing carriage return + return encoded.replace('\n', '') + + +class Credential: + """ + A username/password pair. Use like a namedtuple. + """ + + def __init__(self, username, password): + self.username = username + self.password = password + + def __iter__(self): + yield self.username + yield self.password + + def __str__(self): + return '%(username)s:%(password)s' % vars(self) + + +class PyPIConfig(configparser.RawConfigParser): + def __init__(self): + """ + Load from ~/.pypirc + """ + defaults = dict.fromkeys(['username', 'password', 'repository'], '') + configparser.RawConfigParser.__init__(self, defaults) + + rc = os.path.join(os.path.expanduser('~'), '.pypirc') + if os.path.exists(rc): + self.read(rc) + + @property + def creds_by_repository(self): + sections_with_repositories = [ + section for section in self.sections() + if self.get(section, 'repository').strip() + ] + + return dict(map(self._get_repo_cred, sections_with_repositories)) + + def _get_repo_cred(self, section): + repo = self.get(section, 'repository').strip() + return repo, Credential( + self.get(section, 'username').strip(), + self.get(section, 'password').strip(), + ) + + def find_credential(self, url): + """ + If the URL indicated appears to be a repository defined in this + config, return the credential for that repository. + """ + for repository, cred in self.creds_by_repository.items(): + if url.startswith(repository): + return cred + + +def open_with_auth(url, opener=urllib.request.urlopen): + """Open a urllib2 request, handling HTTP authentication""" + + parsed = urllib.parse.urlparse(url) + scheme, netloc, path, params, query, frag = parsed + + # Double scheme does not raise on Mac OS X as revealed by a + # failing test. We would expect "nonnumeric port". Refs #20. + if netloc.endswith(':'): + raise http_client.InvalidURL("nonnumeric port: ''") + + if scheme in ('http', 'https'): + auth, address = _splituser(netloc) + else: + auth = None + + if not auth: + cred = PyPIConfig().find_credential(url) + if cred: + auth = str(cred) + info = cred.username, url + log.info('Authenticating as %s for %s (from .pypirc)', *info) + + if auth: + auth = "Basic " + _encode_auth(auth) + parts = scheme, address, path, params, query, frag + new_url = urllib.parse.urlunparse(parts) + request = urllib.request.Request(new_url) + request.add_header("Authorization", auth) + else: + request = urllib.request.Request(url) + + request.add_header('User-Agent', user_agent) + fp = opener(request) + + if auth: + # Put authentication info back into request URL if same host, + # so that links found on the page will work + s2, h2, path2, param2, query2, frag2 = urllib.parse.urlparse(fp.url) + if s2 == scheme and h2 == address: + parts = s2, netloc, path2, param2, query2, frag2 + fp.url = urllib.parse.urlunparse(parts) + + return fp + + +# copy of urllib.parse._splituser from Python 3.8 +def _splituser(host): + """splituser('user[:passwd]@host[:port]') --> 'user[:passwd]', 'host[:port]'.""" + user, delim, host = host.rpartition('@') + return (user if delim else None), host + + +# adding a timeout to avoid freezing package_index +open_with_auth = socket_timeout(_SOCKET_TIMEOUT)(open_with_auth) + + +def fix_sf_url(url): + return url # backward compatibility + + +def local_open(url): + """Read a local path, with special support for directories""" + scheme, server, path, param, query, frag = urllib.parse.urlparse(url) + filename = urllib.request.url2pathname(path) + if os.path.isfile(filename): + return urllib.request.urlopen(url) + elif path.endswith('/') and os.path.isdir(filename): + files = [] + for f in os.listdir(filename): + filepath = os.path.join(filename, f) + if f == 'index.html': + with open(filepath, 'r') as fp: + body = fp.read() + break + elif os.path.isdir(filepath): + f += '/' + files.append('<a href="{name}">{name}</a>'.format(name=f)) + else: + tmpl = ( + "<html><head><title>{url}" + "{files}") + body = tmpl.format(url=url, files='\n'.join(files)) + status, message = 200, "OK" + else: + status, message, body = 404, "Path not found", "Not found" + + headers = {'content-type': 'text/html'} + body_stream = six.StringIO(body) + return urllib.error.HTTPError(url, status, message, headers, body_stream) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/py27compat.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/py27compat.py new file mode 100644 index 0000000000..1d57360f4e --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/py27compat.py @@ -0,0 +1,60 @@ +""" +Compatibility Support for Python 2.7 and earlier +""" + +import sys +import platform + +from setuptools.extern import six + + +def get_all_headers(message, key): + """ + Given an HTTPMessage, return all headers matching a given key. + """ + return message.get_all(key) + + +if six.PY2: + def get_all_headers(message, key): + return message.getheaders(key) + + +linux_py2_ascii = ( + platform.system() == 'Linux' and + six.PY2 +) + +rmtree_safe = str if linux_py2_ascii else lambda x: x +"""Workaround for http://bugs.python.org/issue24672""" + + +try: + from ._imp import find_module, PY_COMPILED, PY_FROZEN, PY_SOURCE + from ._imp import get_frozen_object, get_module +except ImportError: + import imp + from imp import PY_COMPILED, PY_FROZEN, PY_SOURCE # noqa + + def find_module(module, paths=None): + """Just like 'imp.find_module()', but with package support""" + parts = module.split('.') + while parts: + part = parts.pop(0) + f, path, (suffix, mode, kind) = info = imp.find_module(part, paths) + + if kind == imp.PKG_DIRECTORY: + parts = parts or ['__init__'] + paths = [path] + + elif parts: + raise ImportError("Can't find %r in %s" % (parts, module)) + + return info + + def get_frozen_object(module, paths): + return imp.get_frozen_object(module) + + def get_module(module, paths, info): + imp.load_module(module, *info) + return sys.modules[module] diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/py31compat.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/py31compat.py new file mode 100644 index 0000000000..e1da7ee2a2 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/py31compat.py @@ -0,0 +1,32 @@ +__all__ = [] + +__metaclass__ = type + + +try: + # Python >=3.2 + from tempfile import TemporaryDirectory +except ImportError: + import shutil + import tempfile + + class TemporaryDirectory: + """ + Very simple temporary directory context manager. + Will try to delete afterward, but will also ignore OS and similar + errors on deletion. + """ + + def __init__(self, **kwargs): + self.name = None # Handle mkdtemp raising an exception + self.name = tempfile.mkdtemp(**kwargs) + + def __enter__(self): + return self.name + + def __exit__(self, exctype, excvalue, exctrace): + try: + shutil.rmtree(self.name, True) + except OSError: # removal errors are not the only possible + pass + self.name = None diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/py33compat.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/py33compat.py new file mode 100644 index 0000000000..cb69443638 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/py33compat.py @@ -0,0 +1,59 @@ +import dis +import array +import collections + +try: + import html +except ImportError: + html = None + +from setuptools.extern import six +from setuptools.extern.six.moves import html_parser + +__metaclass__ = type + +OpArg = collections.namedtuple('OpArg', 'opcode arg') + + +class Bytecode_compat: + def __init__(self, code): + self.code = code + + def __iter__(self): + """Yield '(op,arg)' pair for each operation in code object 'code'""" + + bytes = array.array('b', self.code.co_code) + eof = len(self.code.co_code) + + ptr = 0 + extended_arg = 0 + + while ptr < eof: + + op = bytes[ptr] + + if op >= dis.HAVE_ARGUMENT: + + arg = bytes[ptr + 1] + bytes[ptr + 2] * 256 + extended_arg + ptr += 3 + + if op == dis.EXTENDED_ARG: + long_type = six.integer_types[-1] + extended_arg = arg * long_type(65536) + continue + + else: + arg = None + ptr += 1 + + yield OpArg(op, arg) + + +Bytecode = getattr(dis, 'Bytecode', Bytecode_compat) + + +unescape = getattr(html, 'unescape', None) +if unescape is None: + # HTMLParser.unescape is deprecated since Python 3.4, and will be removed + # from 3.9. + unescape = html_parser.HTMLParser().unescape diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/py34compat.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/py34compat.py new file mode 100644 index 0000000000..3ad917222a --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/py34compat.py @@ -0,0 +1,13 @@ +import importlib + +try: + import importlib.util +except ImportError: + pass + + +try: + module_from_spec = importlib.util.module_from_spec +except AttributeError: + def module_from_spec(spec): + return spec.loader.load_module(spec.name) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/sandbox.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/sandbox.py new file mode 100644 index 0000000000..685f3f72e3 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/sandbox.py @@ -0,0 +1,491 @@ +import os +import sys +import tempfile +import operator +import functools +import itertools +import re +import contextlib +import pickle +import textwrap + +from setuptools.extern import six +from setuptools.extern.six.moves import builtins, map + +import pkg_resources.py31compat + +if sys.platform.startswith('java'): + import org.python.modules.posix.PosixModule as _os +else: + _os = sys.modules[os.name] +try: + _file = file +except NameError: + _file = None +_open = open +from distutils.errors import DistutilsError +from pkg_resources import working_set + + +__all__ = [ + "AbstractSandbox", "DirectorySandbox", "SandboxViolation", "run_setup", +] + + +def _execfile(filename, globals, locals=None): + """ + Python 3 implementation of execfile. + """ + mode = 'rb' + with open(filename, mode) as stream: + script = stream.read() + if locals is None: + locals = globals + code = compile(script, filename, 'exec') + exec(code, globals, locals) + + +@contextlib.contextmanager +def save_argv(repl=None): + saved = sys.argv[:] + if repl is not None: + sys.argv[:] = repl + try: + yield saved + finally: + sys.argv[:] = saved + + +@contextlib.contextmanager +def save_path(): + saved = sys.path[:] + try: + yield saved + finally: + sys.path[:] = saved + + +@contextlib.contextmanager +def override_temp(replacement): + """ + Monkey-patch tempfile.tempdir with replacement, ensuring it exists + """ + pkg_resources.py31compat.makedirs(replacement, exist_ok=True) + + saved = tempfile.tempdir + + tempfile.tempdir = replacement + + try: + yield + finally: + tempfile.tempdir = saved + + +@contextlib.contextmanager +def pushd(target): + saved = os.getcwd() + os.chdir(target) + try: + yield saved + finally: + os.chdir(saved) + + +class UnpickleableException(Exception): + """ + An exception representing another Exception that could not be pickled. + """ + + @staticmethod + def dump(type, exc): + """ + Always return a dumped (pickled) type and exc. If exc can't be pickled, + wrap it in UnpickleableException first. + """ + try: + return pickle.dumps(type), pickle.dumps(exc) + except Exception: + # get UnpickleableException inside the sandbox + from setuptools.sandbox import UnpickleableException as cls + return cls.dump(cls, cls(repr(exc))) + + +class ExceptionSaver: + """ + A Context Manager that will save an exception, serialized, and restore it + later. + """ + + def __enter__(self): + return self + + def __exit__(self, type, exc, tb): + if not exc: + return + + # dump the exception + self._saved = UnpickleableException.dump(type, exc) + self._tb = tb + + # suppress the exception + return True + + def resume(self): + "restore and re-raise any exception" + + if '_saved' not in vars(self): + return + + type, exc = map(pickle.loads, self._saved) + six.reraise(type, exc, self._tb) + + +@contextlib.contextmanager +def save_modules(): + """ + Context in which imported modules are saved. + + Translates exceptions internal to the context into the equivalent exception + outside the context. + """ + saved = sys.modules.copy() + with ExceptionSaver() as saved_exc: + yield saved + + sys.modules.update(saved) + # remove any modules imported since + del_modules = ( + mod_name for mod_name in sys.modules + if mod_name not in saved + # exclude any encodings modules. See #285 + and not mod_name.startswith('encodings.') + ) + _clear_modules(del_modules) + + saved_exc.resume() + + +def _clear_modules(module_names): + for mod_name in list(module_names): + del sys.modules[mod_name] + + +@contextlib.contextmanager +def save_pkg_resources_state(): + saved = pkg_resources.__getstate__() + try: + yield saved + finally: + pkg_resources.__setstate__(saved) + + +@contextlib.contextmanager +def setup_context(setup_dir): + temp_dir = os.path.join(setup_dir, 'temp') + with save_pkg_resources_state(): + with save_modules(): + hide_setuptools() + with save_path(): + with save_argv(): + with override_temp(temp_dir): + with pushd(setup_dir): + # ensure setuptools commands are available + __import__('setuptools') + yield + + +def _needs_hiding(mod_name): + """ + >>> _needs_hiding('setuptools') + True + >>> _needs_hiding('pkg_resources') + True + >>> _needs_hiding('setuptools_plugin') + False + >>> _needs_hiding('setuptools.__init__') + True + >>> _needs_hiding('distutils') + True + >>> _needs_hiding('os') + False + >>> _needs_hiding('Cython') + True + """ + pattern = re.compile(r'(setuptools|pkg_resources|distutils|Cython)(\.|$)') + return bool(pattern.match(mod_name)) + + +def hide_setuptools(): + """ + Remove references to setuptools' modules from sys.modules to allow the + invocation to import the most appropriate setuptools. This technique is + necessary to avoid issues such as #315 where setuptools upgrading itself + would fail to find a function declared in the metadata. + """ + modules = filter(_needs_hiding, sys.modules) + _clear_modules(modules) + + +def run_setup(setup_script, args): + """Run a distutils setup script, sandboxed in its directory""" + setup_dir = os.path.abspath(os.path.dirname(setup_script)) + with setup_context(setup_dir): + try: + sys.argv[:] = [setup_script] + list(args) + sys.path.insert(0, setup_dir) + # reset to include setup dir, w/clean callback list + working_set.__init__() + working_set.callbacks.append(lambda dist: dist.activate()) + + # __file__ should be a byte string on Python 2 (#712) + dunder_file = ( + setup_script + if isinstance(setup_script, str) else + setup_script.encode(sys.getfilesystemencoding()) + ) + + with DirectorySandbox(setup_dir): + ns = dict(__file__=dunder_file, __name__='__main__') + _execfile(setup_script, ns) + except SystemExit as v: + if v.args and v.args[0]: + raise + # Normal exit, just return + + +class AbstractSandbox: + """Wrap 'os' module and 'open()' builtin for virtualizing setup scripts""" + + _active = False + + def __init__(self): + self._attrs = [ + name for name in dir(_os) + if not name.startswith('_') and hasattr(self, name) + ] + + def _copy(self, source): + for name in self._attrs: + setattr(os, name, getattr(source, name)) + + def __enter__(self): + self._copy(self) + if _file: + builtins.file = self._file + builtins.open = self._open + self._active = True + + def __exit__(self, exc_type, exc_value, traceback): + self._active = False + if _file: + builtins.file = _file + builtins.open = _open + self._copy(_os) + + def run(self, func): + """Run 'func' under os sandboxing""" + with self: + return func() + + def _mk_dual_path_wrapper(name): + original = getattr(_os, name) + + def wrap(self, src, dst, *args, **kw): + if self._active: + src, dst = self._remap_pair(name, src, dst, *args, **kw) + return original(src, dst, *args, **kw) + + return wrap + + for name in ["rename", "link", "symlink"]: + if hasattr(_os, name): + locals()[name] = _mk_dual_path_wrapper(name) + + def _mk_single_path_wrapper(name, original=None): + original = original or getattr(_os, name) + + def wrap(self, path, *args, **kw): + if self._active: + path = self._remap_input(name, path, *args, **kw) + return original(path, *args, **kw) + + return wrap + + if _file: + _file = _mk_single_path_wrapper('file', _file) + _open = _mk_single_path_wrapper('open', _open) + for name in [ + "stat", "listdir", "chdir", "open", "chmod", "chown", "mkdir", + "remove", "unlink", "rmdir", "utime", "lchown", "chroot", "lstat", + "startfile", "mkfifo", "mknod", "pathconf", "access" + ]: + if hasattr(_os, name): + locals()[name] = _mk_single_path_wrapper(name) + + def _mk_single_with_return(name): + original = getattr(_os, name) + + def wrap(self, path, *args, **kw): + if self._active: + path = self._remap_input(name, path, *args, **kw) + return self._remap_output(name, original(path, *args, **kw)) + return original(path, *args, **kw) + + return wrap + + for name in ['readlink', 'tempnam']: + if hasattr(_os, name): + locals()[name] = _mk_single_with_return(name) + + def _mk_query(name): + original = getattr(_os, name) + + def wrap(self, *args, **kw): + retval = original(*args, **kw) + if self._active: + return self._remap_output(name, retval) + return retval + + return wrap + + for name in ['getcwd', 'tmpnam']: + if hasattr(_os, name): + locals()[name] = _mk_query(name) + + def _validate_path(self, path): + """Called to remap or validate any path, whether input or output""" + return path + + def _remap_input(self, operation, path, *args, **kw): + """Called for path inputs""" + return self._validate_path(path) + + def _remap_output(self, operation, path): + """Called for path outputs""" + return self._validate_path(path) + + def _remap_pair(self, operation, src, dst, *args, **kw): + """Called for path pairs like rename, link, and symlink operations""" + return ( + self._remap_input(operation + '-from', src, *args, **kw), + self._remap_input(operation + '-to', dst, *args, **kw) + ) + + +if hasattr(os, 'devnull'): + _EXCEPTIONS = [os.devnull,] +else: + _EXCEPTIONS = [] + + +class DirectorySandbox(AbstractSandbox): + """Restrict operations to a single subdirectory - pseudo-chroot""" + + write_ops = dict.fromkeys([ + "open", "chmod", "chown", "mkdir", "remove", "unlink", "rmdir", + "utime", "lchown", "chroot", "mkfifo", "mknod", "tempnam", + ]) + + _exception_patterns = [ + # Allow lib2to3 to attempt to save a pickled grammar object (#121) + r'.*lib2to3.*\.pickle$', + ] + "exempt writing to paths that match the pattern" + + def __init__(self, sandbox, exceptions=_EXCEPTIONS): + self._sandbox = os.path.normcase(os.path.realpath(sandbox)) + self._prefix = os.path.join(self._sandbox, '') + self._exceptions = [ + os.path.normcase(os.path.realpath(path)) + for path in exceptions + ] + AbstractSandbox.__init__(self) + + def _violation(self, operation, *args, **kw): + from setuptools.sandbox import SandboxViolation + raise SandboxViolation(operation, args, kw) + + if _file: + + def _file(self, path, mode='r', *args, **kw): + if mode not in ('r', 'rt', 'rb', 'rU', 'U') and not self._ok(path): + self._violation("file", path, mode, *args, **kw) + return _file(path, mode, *args, **kw) + + def _open(self, path, mode='r', *args, **kw): + if mode not in ('r', 'rt', 'rb', 'rU', 'U') and not self._ok(path): + self._violation("open", path, mode, *args, **kw) + return _open(path, mode, *args, **kw) + + def tmpnam(self): + self._violation("tmpnam") + + def _ok(self, path): + active = self._active + try: + self._active = False + realpath = os.path.normcase(os.path.realpath(path)) + return ( + self._exempted(realpath) + or realpath == self._sandbox + or realpath.startswith(self._prefix) + ) + finally: + self._active = active + + def _exempted(self, filepath): + start_matches = ( + filepath.startswith(exception) + for exception in self._exceptions + ) + pattern_matches = ( + re.match(pattern, filepath) + for pattern in self._exception_patterns + ) + candidates = itertools.chain(start_matches, pattern_matches) + return any(candidates) + + def _remap_input(self, operation, path, *args, **kw): + """Called for path inputs""" + if operation in self.write_ops and not self._ok(path): + self._violation(operation, os.path.realpath(path), *args, **kw) + return path + + def _remap_pair(self, operation, src, dst, *args, **kw): + """Called for path pairs like rename, link, and symlink operations""" + if not self._ok(src) or not self._ok(dst): + self._violation(operation, src, dst, *args, **kw) + return (src, dst) + + def open(self, file, flags, mode=0o777, *args, **kw): + """Called for low-level os.open()""" + if flags & WRITE_FLAGS and not self._ok(file): + self._violation("os.open", file, flags, mode, *args, **kw) + return _os.open(file, flags, mode, *args, **kw) + + +WRITE_FLAGS = functools.reduce( + operator.or_, [getattr(_os, a, 0) for a in + "O_WRONLY O_RDWR O_APPEND O_CREAT O_TRUNC O_TEMPORARY".split()] +) + + +class SandboxViolation(DistutilsError): + """A setup script attempted to modify the filesystem outside the sandbox""" + + tmpl = textwrap.dedent(""" + SandboxViolation: {cmd}{args!r} {kwargs} + + The package setup script has attempted to modify files on your system + that are not within the EasyInstall build area, and has been aborted. + + This package cannot be safely installed by EasyInstall, and may not + support alternate installation locations even if you run its setup + script by hand. Please inform the package's author and the EasyInstall + maintainers to find out if a fix or workaround is available. + """).lstrip() + + def __str__(self): + cmd, args, kwargs = self.args + return self.tmpl.format(**locals()) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/script (dev).tmpl b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/script (dev).tmpl new file mode 100644 index 0000000000..39a24b0488 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/script (dev).tmpl @@ -0,0 +1,6 @@ +# EASY-INSTALL-DEV-SCRIPT: %(spec)r,%(script_name)r +__requires__ = %(spec)r +__import__('pkg_resources').require(%(spec)r) +__file__ = %(dev_path)r +with open(__file__) as f: + exec(compile(f.read(), __file__, 'exec')) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/script.tmpl b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/script.tmpl new file mode 100644 index 0000000000..ff5efbcab3 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/script.tmpl @@ -0,0 +1,3 @@ +# EASY-INSTALL-SCRIPT: %(spec)r,%(script_name)r +__requires__ = %(spec)r +__import__('pkg_resources').run_script(%(spec)r, %(script_name)r) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/site-patch.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/site-patch.py new file mode 100644 index 0000000000..40b00de0a7 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/site-patch.py @@ -0,0 +1,74 @@ +def __boot(): + import sys + import os + PYTHONPATH = os.environ.get('PYTHONPATH') + if PYTHONPATH is None or (sys.platform == 'win32' and not PYTHONPATH): + PYTHONPATH = [] + else: + PYTHONPATH = PYTHONPATH.split(os.pathsep) + + pic = getattr(sys, 'path_importer_cache', {}) + stdpath = sys.path[len(PYTHONPATH):] + mydir = os.path.dirname(__file__) + + for item in stdpath: + if item == mydir or not item: + continue # skip if current dir. on Windows, or my own directory + importer = pic.get(item) + if importer is not None: + loader = importer.find_module('site') + if loader is not None: + # This should actually reload the current module + loader.load_module('site') + break + else: + try: + import imp # Avoid import loop in Python 3 + stream, path, descr = imp.find_module('site', [item]) + except ImportError: + continue + if stream is None: + continue + try: + # This should actually reload the current module + imp.load_module('site', stream, path, descr) + finally: + stream.close() + break + else: + raise ImportError("Couldn't find the real 'site' module") + + known_paths = dict([(makepath(item)[1], 1) for item in sys.path]) # 2.2 comp + + oldpos = getattr(sys, '__egginsert', 0) # save old insertion position + sys.__egginsert = 0 # and reset the current one + + for item in PYTHONPATH: + addsitedir(item) + + sys.__egginsert += oldpos # restore effective old position + + d, nd = makepath(stdpath[0]) + insert_at = None + new_path = [] + + for item in sys.path: + p, np = makepath(item) + + if np == nd and insert_at is None: + # We've hit the first 'system' path entry, so added entries go here + insert_at = len(new_path) + + if np in known_paths or insert_at is None: + new_path.append(item) + else: + # new path after the insert point, back-insert it + new_path.insert(insert_at, item) + insert_at += 1 + + sys.path[:] = new_path + + +if __name__ == 'site': + __boot() + del __boot diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/ssl_support.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/ssl_support.py new file mode 100644 index 0000000000..226db694bb --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/ssl_support.py @@ -0,0 +1,260 @@ +import os +import socket +import atexit +import re +import functools + +from setuptools.extern.six.moves import urllib, http_client, map, filter + +from pkg_resources import ResolutionError, ExtractionError + +try: + import ssl +except ImportError: + ssl = None + +__all__ = [ + 'VerifyingHTTPSHandler', 'find_ca_bundle', 'is_available', 'cert_paths', + 'opener_for' +] + +cert_paths = """ +/etc/pki/tls/certs/ca-bundle.crt +/etc/ssl/certs/ca-certificates.crt +/usr/share/ssl/certs/ca-bundle.crt +/usr/local/share/certs/ca-root.crt +/etc/ssl/cert.pem +/System/Library/OpenSSL/certs/cert.pem +/usr/local/share/certs/ca-root-nss.crt +/etc/ssl/ca-bundle.pem +""".strip().split() + +try: + HTTPSHandler = urllib.request.HTTPSHandler + HTTPSConnection = http_client.HTTPSConnection +except AttributeError: + HTTPSHandler = HTTPSConnection = object + +is_available = ssl is not None and object not in (HTTPSHandler, HTTPSConnection) + + +try: + from ssl import CertificateError, match_hostname +except ImportError: + try: + from backports.ssl_match_hostname import CertificateError + from backports.ssl_match_hostname import match_hostname + except ImportError: + CertificateError = None + match_hostname = None + +if not CertificateError: + + class CertificateError(ValueError): + pass + + +if not match_hostname: + + def _dnsname_match(dn, hostname, max_wildcards=1): + """Matching according to RFC 6125, section 6.4.3 + + https://tools.ietf.org/html/rfc6125#section-6.4.3 + """ + pats = [] + if not dn: + return False + + # Ported from python3-syntax: + # leftmost, *remainder = dn.split(r'.') + parts = dn.split(r'.') + leftmost = parts[0] + remainder = parts[1:] + + wildcards = leftmost.count('*') + if wildcards > max_wildcards: + # Issue #17980: avoid denials of service by refusing more + # than one wildcard per fragment. A survey of established + # policy among SSL implementations showed it to be a + # reasonable choice. + raise CertificateError( + "too many wildcards in certificate DNS name: " + repr(dn)) + + # speed up common case w/o wildcards + if not wildcards: + return dn.lower() == hostname.lower() + + # RFC 6125, section 6.4.3, subitem 1. + # The client SHOULD NOT attempt to match a presented identifier in which + # the wildcard character comprises a label other than the left-most label. + if leftmost == '*': + # When '*' is a fragment by itself, it matches a non-empty dotless + # fragment. + pats.append('[^.]+') + elif leftmost.startswith('xn--') or hostname.startswith('xn--'): + # RFC 6125, section 6.4.3, subitem 3. + # The client SHOULD NOT attempt to match a presented identifier + # where the wildcard character is embedded within an A-label or + # U-label of an internationalized domain name. + pats.append(re.escape(leftmost)) + else: + # Otherwise, '*' matches any dotless string, e.g. www* + pats.append(re.escape(leftmost).replace(r'\*', '[^.]*')) + + # add the remaining fragments, ignore any wildcards + for frag in remainder: + pats.append(re.escape(frag)) + + pat = re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE) + return pat.match(hostname) + + def match_hostname(cert, hostname): + """Verify that *cert* (in decoded format as returned by + SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 and RFC 6125 + rules are followed, but IP addresses are not accepted for *hostname*. + + CertificateError is raised on failure. On success, the function + returns nothing. + """ + if not cert: + raise ValueError("empty or no certificate") + dnsnames = [] + san = cert.get('subjectAltName', ()) + for key, value in san: + if key == 'DNS': + if _dnsname_match(value, hostname): + return + dnsnames.append(value) + if not dnsnames: + # The subject is only checked when there is no dNSName entry + # in subjectAltName + for sub in cert.get('subject', ()): + for key, value in sub: + # XXX according to RFC 2818, the most specific Common Name + # must be used. + if key == 'commonName': + if _dnsname_match(value, hostname): + return + dnsnames.append(value) + if len(dnsnames) > 1: + raise CertificateError("hostname %r " + "doesn't match either of %s" + % (hostname, ', '.join(map(repr, dnsnames)))) + elif len(dnsnames) == 1: + raise CertificateError("hostname %r " + "doesn't match %r" + % (hostname, dnsnames[0])) + else: + raise CertificateError("no appropriate commonName or " + "subjectAltName fields were found") + + +class VerifyingHTTPSHandler(HTTPSHandler): + """Simple verifying handler: no auth, subclasses, timeouts, etc.""" + + def __init__(self, ca_bundle): + self.ca_bundle = ca_bundle + HTTPSHandler.__init__(self) + + def https_open(self, req): + return self.do_open( + lambda host, **kw: VerifyingHTTPSConn(host, self.ca_bundle, **kw), req + ) + + +class VerifyingHTTPSConn(HTTPSConnection): + """Simple verifying connection: no auth, subclasses, timeouts, etc.""" + + def __init__(self, host, ca_bundle, **kw): + HTTPSConnection.__init__(self, host, **kw) + self.ca_bundle = ca_bundle + + def connect(self): + sock = socket.create_connection( + (self.host, self.port), getattr(self, 'source_address', None) + ) + + # Handle the socket if a (proxy) tunnel is present + if hasattr(self, '_tunnel') and getattr(self, '_tunnel_host', None): + self.sock = sock + self._tunnel() + # http://bugs.python.org/issue7776: Python>=3.4.1 and >=2.7.7 + # change self.host to mean the proxy server host when tunneling is + # being used. Adapt, since we are interested in the destination + # host for the match_hostname() comparison. + actual_host = self._tunnel_host + else: + actual_host = self.host + + if hasattr(ssl, 'create_default_context'): + ctx = ssl.create_default_context(cafile=self.ca_bundle) + self.sock = ctx.wrap_socket(sock, server_hostname=actual_host) + else: + # This is for python < 2.7.9 and < 3.4? + self.sock = ssl.wrap_socket( + sock, cert_reqs=ssl.CERT_REQUIRED, ca_certs=self.ca_bundle + ) + try: + match_hostname(self.sock.getpeercert(), actual_host) + except CertificateError: + self.sock.shutdown(socket.SHUT_RDWR) + self.sock.close() + raise + + +def opener_for(ca_bundle=None): + """Get a urlopen() replacement that uses ca_bundle for verification""" + return urllib.request.build_opener( + VerifyingHTTPSHandler(ca_bundle or find_ca_bundle()) + ).open + + +# from jaraco.functools +def once(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + if not hasattr(func, 'always_returns'): + func.always_returns = func(*args, **kwargs) + return func.always_returns + return wrapper + + +@once +def get_win_certfile(): + try: + import wincertstore + except ImportError: + return None + + class CertFile(wincertstore.CertFile): + def __init__(self): + super(CertFile, self).__init__() + atexit.register(self.close) + + def close(self): + try: + super(CertFile, self).close() + except OSError: + pass + + _wincerts = CertFile() + _wincerts.addstore('CA') + _wincerts.addstore('ROOT') + return _wincerts.name + + +def find_ca_bundle(): + """Return an existing CA bundle path, or None""" + extant_cert_paths = filter(os.path.isfile, cert_paths) + return ( + get_win_certfile() + or next(extant_cert_paths, None) + or _certifi_where() + ) + + +def _certifi_where(): + try: + return __import__('certifi').where() + except (ImportError, ResolutionError, ExtractionError): + pass diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/unicode_utils.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/unicode_utils.py new file mode 100644 index 0000000000..7c63efd20b --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/unicode_utils.py @@ -0,0 +1,44 @@ +import unicodedata +import sys + +from setuptools.extern import six + + +# HFS Plus uses decomposed UTF-8 +def decompose(path): + if isinstance(path, six.text_type): + return unicodedata.normalize('NFD', path) + try: + path = path.decode('utf-8') + path = unicodedata.normalize('NFD', path) + path = path.encode('utf-8') + except UnicodeError: + pass # Not UTF-8 + return path + + +def filesys_decode(path): + """ + Ensure that the given path is decoded, + NONE when no expected encoding works + """ + + if isinstance(path, six.text_type): + return path + + fs_enc = sys.getfilesystemencoding() or 'utf-8' + candidates = fs_enc, 'utf-8' + + for enc in candidates: + try: + return path.decode(enc) + except UnicodeDecodeError: + continue + + +def try_encode(string, enc): + "turn unicode encoding into a functional routine" + try: + return string.encode(enc) + except UnicodeEncodeError: + return None diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/version.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/version.py new file mode 100644 index 0000000000..95e1869658 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/version.py @@ -0,0 +1,6 @@ +import pkg_resources + +try: + __version__ = pkg_resources.get_distribution('setuptools').version +except Exception: + __version__ = 'unknown' diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/wheel.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/wheel.py new file mode 100644 index 0000000000..025aaa828a --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/wheel.py @@ -0,0 +1,220 @@ +"""Wheels support.""" + +from distutils.util import get_platform +from distutils import log +import email +import itertools +import os +import posixpath +import re +import zipfile + +import pkg_resources +import setuptools +from pkg_resources import parse_version +from setuptools.extern.packaging.tags import sys_tags +from setuptools.extern.packaging.utils import canonicalize_name +from setuptools.extern.six import PY3 +from setuptools.command.egg_info import write_requirements + + +__metaclass__ = type + + +WHEEL_NAME = re.compile( + r"""^(?P.+?)-(?P\d.*?) + ((-(?P\d.*?))?-(?P.+?)-(?P.+?)-(?P.+?) + )\.whl$""", + re.VERBOSE).match + +NAMESPACE_PACKAGE_INIT = '''\ +try: + __import__('pkg_resources').declare_namespace(__name__) +except ImportError: + __path__ = __import__('pkgutil').extend_path(__path__, __name__) +''' + + +def unpack(src_dir, dst_dir): + '''Move everything under `src_dir` to `dst_dir`, and delete the former.''' + for dirpath, dirnames, filenames in os.walk(src_dir): + subdir = os.path.relpath(dirpath, src_dir) + for f in filenames: + src = os.path.join(dirpath, f) + dst = os.path.join(dst_dir, subdir, f) + os.renames(src, dst) + for n, d in reversed(list(enumerate(dirnames))): + src = os.path.join(dirpath, d) + dst = os.path.join(dst_dir, subdir, d) + if not os.path.exists(dst): + # Directory does not exist in destination, + # rename it and prune it from os.walk list. + os.renames(src, dst) + del dirnames[n] + # Cleanup. + for dirpath, dirnames, filenames in os.walk(src_dir, topdown=True): + assert not filenames + os.rmdir(dirpath) + + +class Wheel: + + def __init__(self, filename): + match = WHEEL_NAME(os.path.basename(filename)) + if match is None: + raise ValueError('invalid wheel name: %r' % filename) + self.filename = filename + for k, v in match.groupdict().items(): + setattr(self, k, v) + + def tags(self): + '''List tags (py_version, abi, platform) supported by this wheel.''' + return itertools.product( + self.py_version.split('.'), + self.abi.split('.'), + self.platform.split('.'), + ) + + def is_compatible(self): + '''Is the wheel is compatible with the current platform?''' + supported_tags = set((t.interpreter, t.abi, t.platform) for t in sys_tags()) + return next((True for t in self.tags() if t in supported_tags), False) + + def egg_name(self): + return pkg_resources.Distribution( + project_name=self.project_name, version=self.version, + platform=(None if self.platform == 'any' else get_platform()), + ).egg_name() + '.egg' + + def get_dist_info(self, zf): + # find the correct name of the .dist-info dir in the wheel file + for member in zf.namelist(): + dirname = posixpath.dirname(member) + if (dirname.endswith('.dist-info') and + canonicalize_name(dirname).startswith( + canonicalize_name(self.project_name))): + return dirname + raise ValueError("unsupported wheel format. .dist-info not found") + + def install_as_egg(self, destination_eggdir): + '''Install wheel as an egg directory.''' + with zipfile.ZipFile(self.filename) as zf: + self._install_as_egg(destination_eggdir, zf) + + def _install_as_egg(self, destination_eggdir, zf): + dist_basename = '%s-%s' % (self.project_name, self.version) + dist_info = self.get_dist_info(zf) + dist_data = '%s.data' % dist_basename + egg_info = os.path.join(destination_eggdir, 'EGG-INFO') + + self._convert_metadata(zf, destination_eggdir, dist_info, egg_info) + self._move_data_entries(destination_eggdir, dist_data) + self._fix_namespace_packages(egg_info, destination_eggdir) + + @staticmethod + def _convert_metadata(zf, destination_eggdir, dist_info, egg_info): + def get_metadata(name): + with zf.open(posixpath.join(dist_info, name)) as fp: + value = fp.read().decode('utf-8') if PY3 else fp.read() + return email.parser.Parser().parsestr(value) + + wheel_metadata = get_metadata('WHEEL') + # Check wheel format version is supported. + wheel_version = parse_version(wheel_metadata.get('Wheel-Version')) + wheel_v1 = ( + parse_version('1.0') <= wheel_version < parse_version('2.0dev0') + ) + if not wheel_v1: + raise ValueError( + 'unsupported wheel format version: %s' % wheel_version) + # Extract to target directory. + os.mkdir(destination_eggdir) + zf.extractall(destination_eggdir) + # Convert metadata. + dist_info = os.path.join(destination_eggdir, dist_info) + dist = pkg_resources.Distribution.from_location( + destination_eggdir, dist_info, + metadata=pkg_resources.PathMetadata(destination_eggdir, dist_info), + ) + + # Note: Evaluate and strip markers now, + # as it's difficult to convert back from the syntax: + # foobar; "linux" in sys_platform and extra == 'test' + def raw_req(req): + req.marker = None + return str(req) + install_requires = list(sorted(map(raw_req, dist.requires()))) + extras_require = { + extra: sorted( + req + for req in map(raw_req, dist.requires((extra,))) + if req not in install_requires + ) + for extra in dist.extras + } + os.rename(dist_info, egg_info) + os.rename( + os.path.join(egg_info, 'METADATA'), + os.path.join(egg_info, 'PKG-INFO'), + ) + setup_dist = setuptools.Distribution( + attrs=dict( + install_requires=install_requires, + extras_require=extras_require, + ), + ) + # Temporarily disable info traces. + log_threshold = log._global_log.threshold + log.set_threshold(log.WARN) + try: + write_requirements( + setup_dist.get_command_obj('egg_info'), + None, + os.path.join(egg_info, 'requires.txt'), + ) + finally: + log.set_threshold(log_threshold) + + @staticmethod + def _move_data_entries(destination_eggdir, dist_data): + """Move data entries to their correct location.""" + dist_data = os.path.join(destination_eggdir, dist_data) + dist_data_scripts = os.path.join(dist_data, 'scripts') + if os.path.exists(dist_data_scripts): + egg_info_scripts = os.path.join( + destination_eggdir, 'EGG-INFO', 'scripts') + os.mkdir(egg_info_scripts) + for entry in os.listdir(dist_data_scripts): + # Remove bytecode, as it's not properly handled + # during easy_install scripts install phase. + if entry.endswith('.pyc'): + os.unlink(os.path.join(dist_data_scripts, entry)) + else: + os.rename( + os.path.join(dist_data_scripts, entry), + os.path.join(egg_info_scripts, entry), + ) + os.rmdir(dist_data_scripts) + for subdir in filter(os.path.exists, ( + os.path.join(dist_data, d) + for d in ('data', 'headers', 'purelib', 'platlib') + )): + unpack(subdir, destination_eggdir) + if os.path.exists(dist_data): + os.rmdir(dist_data) + + @staticmethod + def _fix_namespace_packages(egg_info, destination_eggdir): + namespace_packages = os.path.join( + egg_info, 'namespace_packages.txt') + if os.path.exists(namespace_packages): + with open(namespace_packages) as fp: + namespace_packages = fp.read().split() + for mod in namespace_packages: + mod_dir = os.path.join(destination_eggdir, *mod.split('.')) + mod_init = os.path.join(mod_dir, '__init__.py') + if not os.path.exists(mod_dir): + os.mkdir(mod_dir) + if not os.path.exists(mod_init): + with open(mod_init, 'w') as fp: + fp.write(NAMESPACE_PACKAGE_INIT) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/windows_support.py b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/windows_support.py new file mode 100644 index 0000000000..cb977cff95 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/setuptools/windows_support.py @@ -0,0 +1,29 @@ +import platform +import ctypes + + +def windows_only(func): + if platform.system() != 'Windows': + return lambda *args, **kwargs: None + return func + + +@windows_only +def hide_file(path): + """ + Set the hidden attribute on a file or directory. + + From http://stackoverflow.com/questions/19622133/ + + `path` must be text. + """ + __import__('ctypes.wintypes') + SetFileAttributes = ctypes.windll.kernel32.SetFileAttributesW + SetFileAttributes.argtypes = ctypes.wintypes.LPWSTR, ctypes.wintypes.DWORD + SetFileAttributes.restype = ctypes.wintypes.BOOL + + FILE_ATTRIBUTE_HIDDEN = 0x02 + + ret = SetFileAttributes(path, FILE_ATTRIBUTE_HIDDEN) + if not ret: + raise ctypes.WinError() diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/EGG-INFO/PKG-INFO b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/EGG-INFO/PKG-INFO new file mode 100644 index 0000000000..646cf705a2 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/EGG-INFO/PKG-INFO @@ -0,0 +1,32 @@ +Metadata-Version: 2.1 +Name: unicorn +Version: 2.0.1.post1 +Summary: Unicorn CPU emulator engine +Home-page: http://www.unicorn-engine.org +Author: Nguyen Anh Quynh +Author-email: aquynh@gmail.com +License: UNKNOWN +Description: + Unicorn is a lightweight, multi-platform, multi-architecture CPU emulator framework + based on [QEMU](http://qemu.org). + + Unicorn offers some unparalleled features: + + - Multi-architecture: ARM, ARM64 (ARMv8), M68K, MIPS, PowerPC, RISCV, SPARC, S390X, TriCore and X86 (16, 32, 64-bit) + - Clean/simple/lightweight/intuitive architecture-neutral API + - Implemented in pure C language, with bindings for Crystal, Clojure, Visual Basic, Perl, Rust, Ruby, Python, Java, .NET, Go, Delphi/Free Pascal, Haskell, Pharo, and Lua. + - Native support for Windows & *nix (with Mac OSX, Linux, *BSD & Solaris confirmed) + - High performance via Just-In-Time compilation + - Support for fine-grained instrumentation at various levels + - Thread-safety by design + - Distributed under free software license GPLv2 + + Further information is available at http://www.unicorn-engine.org + +Platform: UNKNOWN +Classifier: License :: OSI Approved :: BSD License +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 3 +Requires: ctypes +Provides: unicorn +Description-Content-Type: text/markdown diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/EGG-INFO/SOURCES.txt b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/EGG-INFO/SOURCES.txt new file mode 100644 index 0000000000..3829bdc444 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/EGG-INFO/SOURCES.txt @@ -0,0 +1,24 @@ +MANIFEST.in +README.TXT +setup.cfg +setup.py +prebuilt/.gitkeep +unicorn/__init__.py +unicorn/arm64_const.py +unicorn/arm_const.py +unicorn/m68k_const.py +unicorn/mips_const.py +unicorn/ppc_const.py +unicorn/rh850_const.py +unicorn/riscv_const.py +unicorn/s390x_const.py +unicorn/sparc_const.py +unicorn/tricore_const.py +unicorn/unicorn.py +unicorn/unicorn_const.py +unicorn/x86_const.py +unicorn.egg-info/PKG-INFO +unicorn.egg-info/SOURCES.txt +unicorn.egg-info/dependency_links.txt +unicorn.egg-info/not-zip-safe +unicorn.egg-info/top_level.txt \ No newline at end of file diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/EGG-INFO/dependency_links.txt b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/EGG-INFO/dependency_links.txt new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/EGG-INFO/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/EGG-INFO/not-zip-safe b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/EGG-INFO/not-zip-safe new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/EGG-INFO/not-zip-safe @@ -0,0 +1 @@ + diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/EGG-INFO/top_level.txt b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/EGG-INFO/top_level.txt new file mode 100644 index 0000000000..bb2056dd5d --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/EGG-INFO/top_level.txt @@ -0,0 +1 @@ +unicorn diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/__init__.py b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/__init__.py new file mode 100644 index 0000000000..9d2b717cee --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/__init__.py @@ -0,0 +1,4 @@ +# Unicorn Python bindings, by Nguyen Anh Quynnh +from . import arm_const, arm64_const, mips_const, sparc_const, m68k_const, x86_const +from .unicorn_const import * +from .unicorn import Uc, uc_version, uc_arch_supported, version_bind, debug, UcError, __version__ diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/arm64_const.py b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/arm64_const.py new file mode 100644 index 0000000000..53135ab839 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/arm64_const.py @@ -0,0 +1,333 @@ +# For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT [arm64_const.py] + +# ARM64 CPU + +UC_CPU_ARM64_A57 = 0 +UC_CPU_ARM64_A53 = 1 +UC_CPU_ARM64_A72 = 2 +UC_CPU_ARM64_MAX = 3 +UC_CPU_ARM64_ENDING = 4 + +# ARM64 registers + +UC_ARM64_REG_INVALID = 0 +UC_ARM64_REG_X29 = 1 +UC_ARM64_REG_X30 = 2 +UC_ARM64_REG_NZCV = 3 +UC_ARM64_REG_SP = 4 +UC_ARM64_REG_WSP = 5 +UC_ARM64_REG_WZR = 6 +UC_ARM64_REG_XZR = 7 +UC_ARM64_REG_B0 = 8 +UC_ARM64_REG_B1 = 9 +UC_ARM64_REG_B2 = 10 +UC_ARM64_REG_B3 = 11 +UC_ARM64_REG_B4 = 12 +UC_ARM64_REG_B5 = 13 +UC_ARM64_REG_B6 = 14 +UC_ARM64_REG_B7 = 15 +UC_ARM64_REG_B8 = 16 +UC_ARM64_REG_B9 = 17 +UC_ARM64_REG_B10 = 18 +UC_ARM64_REG_B11 = 19 +UC_ARM64_REG_B12 = 20 +UC_ARM64_REG_B13 = 21 +UC_ARM64_REG_B14 = 22 +UC_ARM64_REG_B15 = 23 +UC_ARM64_REG_B16 = 24 +UC_ARM64_REG_B17 = 25 +UC_ARM64_REG_B18 = 26 +UC_ARM64_REG_B19 = 27 +UC_ARM64_REG_B20 = 28 +UC_ARM64_REG_B21 = 29 +UC_ARM64_REG_B22 = 30 +UC_ARM64_REG_B23 = 31 +UC_ARM64_REG_B24 = 32 +UC_ARM64_REG_B25 = 33 +UC_ARM64_REG_B26 = 34 +UC_ARM64_REG_B27 = 35 +UC_ARM64_REG_B28 = 36 +UC_ARM64_REG_B29 = 37 +UC_ARM64_REG_B30 = 38 +UC_ARM64_REG_B31 = 39 +UC_ARM64_REG_D0 = 40 +UC_ARM64_REG_D1 = 41 +UC_ARM64_REG_D2 = 42 +UC_ARM64_REG_D3 = 43 +UC_ARM64_REG_D4 = 44 +UC_ARM64_REG_D5 = 45 +UC_ARM64_REG_D6 = 46 +UC_ARM64_REG_D7 = 47 +UC_ARM64_REG_D8 = 48 +UC_ARM64_REG_D9 = 49 +UC_ARM64_REG_D10 = 50 +UC_ARM64_REG_D11 = 51 +UC_ARM64_REG_D12 = 52 +UC_ARM64_REG_D13 = 53 +UC_ARM64_REG_D14 = 54 +UC_ARM64_REG_D15 = 55 +UC_ARM64_REG_D16 = 56 +UC_ARM64_REG_D17 = 57 +UC_ARM64_REG_D18 = 58 +UC_ARM64_REG_D19 = 59 +UC_ARM64_REG_D20 = 60 +UC_ARM64_REG_D21 = 61 +UC_ARM64_REG_D22 = 62 +UC_ARM64_REG_D23 = 63 +UC_ARM64_REG_D24 = 64 +UC_ARM64_REG_D25 = 65 +UC_ARM64_REG_D26 = 66 +UC_ARM64_REG_D27 = 67 +UC_ARM64_REG_D28 = 68 +UC_ARM64_REG_D29 = 69 +UC_ARM64_REG_D30 = 70 +UC_ARM64_REG_D31 = 71 +UC_ARM64_REG_H0 = 72 +UC_ARM64_REG_H1 = 73 +UC_ARM64_REG_H2 = 74 +UC_ARM64_REG_H3 = 75 +UC_ARM64_REG_H4 = 76 +UC_ARM64_REG_H5 = 77 +UC_ARM64_REG_H6 = 78 +UC_ARM64_REG_H7 = 79 +UC_ARM64_REG_H8 = 80 +UC_ARM64_REG_H9 = 81 +UC_ARM64_REG_H10 = 82 +UC_ARM64_REG_H11 = 83 +UC_ARM64_REG_H12 = 84 +UC_ARM64_REG_H13 = 85 +UC_ARM64_REG_H14 = 86 +UC_ARM64_REG_H15 = 87 +UC_ARM64_REG_H16 = 88 +UC_ARM64_REG_H17 = 89 +UC_ARM64_REG_H18 = 90 +UC_ARM64_REG_H19 = 91 +UC_ARM64_REG_H20 = 92 +UC_ARM64_REG_H21 = 93 +UC_ARM64_REG_H22 = 94 +UC_ARM64_REG_H23 = 95 +UC_ARM64_REG_H24 = 96 +UC_ARM64_REG_H25 = 97 +UC_ARM64_REG_H26 = 98 +UC_ARM64_REG_H27 = 99 +UC_ARM64_REG_H28 = 100 +UC_ARM64_REG_H29 = 101 +UC_ARM64_REG_H30 = 102 +UC_ARM64_REG_H31 = 103 +UC_ARM64_REG_Q0 = 104 +UC_ARM64_REG_Q1 = 105 +UC_ARM64_REG_Q2 = 106 +UC_ARM64_REG_Q3 = 107 +UC_ARM64_REG_Q4 = 108 +UC_ARM64_REG_Q5 = 109 +UC_ARM64_REG_Q6 = 110 +UC_ARM64_REG_Q7 = 111 +UC_ARM64_REG_Q8 = 112 +UC_ARM64_REG_Q9 = 113 +UC_ARM64_REG_Q10 = 114 +UC_ARM64_REG_Q11 = 115 +UC_ARM64_REG_Q12 = 116 +UC_ARM64_REG_Q13 = 117 +UC_ARM64_REG_Q14 = 118 +UC_ARM64_REG_Q15 = 119 +UC_ARM64_REG_Q16 = 120 +UC_ARM64_REG_Q17 = 121 +UC_ARM64_REG_Q18 = 122 +UC_ARM64_REG_Q19 = 123 +UC_ARM64_REG_Q20 = 124 +UC_ARM64_REG_Q21 = 125 +UC_ARM64_REG_Q22 = 126 +UC_ARM64_REG_Q23 = 127 +UC_ARM64_REG_Q24 = 128 +UC_ARM64_REG_Q25 = 129 +UC_ARM64_REG_Q26 = 130 +UC_ARM64_REG_Q27 = 131 +UC_ARM64_REG_Q28 = 132 +UC_ARM64_REG_Q29 = 133 +UC_ARM64_REG_Q30 = 134 +UC_ARM64_REG_Q31 = 135 +UC_ARM64_REG_S0 = 136 +UC_ARM64_REG_S1 = 137 +UC_ARM64_REG_S2 = 138 +UC_ARM64_REG_S3 = 139 +UC_ARM64_REG_S4 = 140 +UC_ARM64_REG_S5 = 141 +UC_ARM64_REG_S6 = 142 +UC_ARM64_REG_S7 = 143 +UC_ARM64_REG_S8 = 144 +UC_ARM64_REG_S9 = 145 +UC_ARM64_REG_S10 = 146 +UC_ARM64_REG_S11 = 147 +UC_ARM64_REG_S12 = 148 +UC_ARM64_REG_S13 = 149 +UC_ARM64_REG_S14 = 150 +UC_ARM64_REG_S15 = 151 +UC_ARM64_REG_S16 = 152 +UC_ARM64_REG_S17 = 153 +UC_ARM64_REG_S18 = 154 +UC_ARM64_REG_S19 = 155 +UC_ARM64_REG_S20 = 156 +UC_ARM64_REG_S21 = 157 +UC_ARM64_REG_S22 = 158 +UC_ARM64_REG_S23 = 159 +UC_ARM64_REG_S24 = 160 +UC_ARM64_REG_S25 = 161 +UC_ARM64_REG_S26 = 162 +UC_ARM64_REG_S27 = 163 +UC_ARM64_REG_S28 = 164 +UC_ARM64_REG_S29 = 165 +UC_ARM64_REG_S30 = 166 +UC_ARM64_REG_S31 = 167 +UC_ARM64_REG_W0 = 168 +UC_ARM64_REG_W1 = 169 +UC_ARM64_REG_W2 = 170 +UC_ARM64_REG_W3 = 171 +UC_ARM64_REG_W4 = 172 +UC_ARM64_REG_W5 = 173 +UC_ARM64_REG_W6 = 174 +UC_ARM64_REG_W7 = 175 +UC_ARM64_REG_W8 = 176 +UC_ARM64_REG_W9 = 177 +UC_ARM64_REG_W10 = 178 +UC_ARM64_REG_W11 = 179 +UC_ARM64_REG_W12 = 180 +UC_ARM64_REG_W13 = 181 +UC_ARM64_REG_W14 = 182 +UC_ARM64_REG_W15 = 183 +UC_ARM64_REG_W16 = 184 +UC_ARM64_REG_W17 = 185 +UC_ARM64_REG_W18 = 186 +UC_ARM64_REG_W19 = 187 +UC_ARM64_REG_W20 = 188 +UC_ARM64_REG_W21 = 189 +UC_ARM64_REG_W22 = 190 +UC_ARM64_REG_W23 = 191 +UC_ARM64_REG_W24 = 192 +UC_ARM64_REG_W25 = 193 +UC_ARM64_REG_W26 = 194 +UC_ARM64_REG_W27 = 195 +UC_ARM64_REG_W28 = 196 +UC_ARM64_REG_W29 = 197 +UC_ARM64_REG_W30 = 198 +UC_ARM64_REG_X0 = 199 +UC_ARM64_REG_X1 = 200 +UC_ARM64_REG_X2 = 201 +UC_ARM64_REG_X3 = 202 +UC_ARM64_REG_X4 = 203 +UC_ARM64_REG_X5 = 204 +UC_ARM64_REG_X6 = 205 +UC_ARM64_REG_X7 = 206 +UC_ARM64_REG_X8 = 207 +UC_ARM64_REG_X9 = 208 +UC_ARM64_REG_X10 = 209 +UC_ARM64_REG_X11 = 210 +UC_ARM64_REG_X12 = 211 +UC_ARM64_REG_X13 = 212 +UC_ARM64_REG_X14 = 213 +UC_ARM64_REG_X15 = 214 +UC_ARM64_REG_X16 = 215 +UC_ARM64_REG_X17 = 216 +UC_ARM64_REG_X18 = 217 +UC_ARM64_REG_X19 = 218 +UC_ARM64_REG_X20 = 219 +UC_ARM64_REG_X21 = 220 +UC_ARM64_REG_X22 = 221 +UC_ARM64_REG_X23 = 222 +UC_ARM64_REG_X24 = 223 +UC_ARM64_REG_X25 = 224 +UC_ARM64_REG_X26 = 225 +UC_ARM64_REG_X27 = 226 +UC_ARM64_REG_X28 = 227 +UC_ARM64_REG_V0 = 228 +UC_ARM64_REG_V1 = 229 +UC_ARM64_REG_V2 = 230 +UC_ARM64_REG_V3 = 231 +UC_ARM64_REG_V4 = 232 +UC_ARM64_REG_V5 = 233 +UC_ARM64_REG_V6 = 234 +UC_ARM64_REG_V7 = 235 +UC_ARM64_REG_V8 = 236 +UC_ARM64_REG_V9 = 237 +UC_ARM64_REG_V10 = 238 +UC_ARM64_REG_V11 = 239 +UC_ARM64_REG_V12 = 240 +UC_ARM64_REG_V13 = 241 +UC_ARM64_REG_V14 = 242 +UC_ARM64_REG_V15 = 243 +UC_ARM64_REG_V16 = 244 +UC_ARM64_REG_V17 = 245 +UC_ARM64_REG_V18 = 246 +UC_ARM64_REG_V19 = 247 +UC_ARM64_REG_V20 = 248 +UC_ARM64_REG_V21 = 249 +UC_ARM64_REG_V22 = 250 +UC_ARM64_REG_V23 = 251 +UC_ARM64_REG_V24 = 252 +UC_ARM64_REG_V25 = 253 +UC_ARM64_REG_V26 = 254 +UC_ARM64_REG_V27 = 255 +UC_ARM64_REG_V28 = 256 +UC_ARM64_REG_V29 = 257 +UC_ARM64_REG_V30 = 258 +UC_ARM64_REG_V31 = 259 + +# pseudo registers +UC_ARM64_REG_PC = 260 +UC_ARM64_REG_CPACR_EL1 = 261 + +# thread registers, depreciated, use UC_ARM64_REG_CP_REG instead +UC_ARM64_REG_TPIDR_EL0 = 262 +UC_ARM64_REG_TPIDRRO_EL0 = 263 +UC_ARM64_REG_TPIDR_EL1 = 264 +UC_ARM64_REG_PSTATE = 265 + +# exception link registers, depreciated, use UC_ARM64_REG_CP_REG instead +UC_ARM64_REG_ELR_EL0 = 266 +UC_ARM64_REG_ELR_EL1 = 267 +UC_ARM64_REG_ELR_EL2 = 268 +UC_ARM64_REG_ELR_EL3 = 269 + +# stack pointers registers, depreciated, use UC_ARM64_REG_CP_REG instead +UC_ARM64_REG_SP_EL0 = 270 +UC_ARM64_REG_SP_EL1 = 271 +UC_ARM64_REG_SP_EL2 = 272 +UC_ARM64_REG_SP_EL3 = 273 + +# other CP15 registers, depreciated, use UC_ARM64_REG_CP_REG instead +UC_ARM64_REG_TTBR0_EL1 = 274 +UC_ARM64_REG_TTBR1_EL1 = 275 +UC_ARM64_REG_ESR_EL0 = 276 +UC_ARM64_REG_ESR_EL1 = 277 +UC_ARM64_REG_ESR_EL2 = 278 +UC_ARM64_REG_ESR_EL3 = 279 +UC_ARM64_REG_FAR_EL0 = 280 +UC_ARM64_REG_FAR_EL1 = 281 +UC_ARM64_REG_FAR_EL2 = 282 +UC_ARM64_REG_FAR_EL3 = 283 +UC_ARM64_REG_PAR_EL1 = 284 +UC_ARM64_REG_MAIR_EL1 = 285 +UC_ARM64_REG_VBAR_EL0 = 286 +UC_ARM64_REG_VBAR_EL1 = 287 +UC_ARM64_REG_VBAR_EL2 = 288 +UC_ARM64_REG_VBAR_EL3 = 289 +UC_ARM64_REG_CP_REG = 290 + +# floating point control and status registers +UC_ARM64_REG_FPCR = 291 +UC_ARM64_REG_FPSR = 292 +UC_ARM64_REG_ENDING = 293 + +# alias registers +UC_ARM64_REG_IP0 = 215 +UC_ARM64_REG_IP1 = 216 +UC_ARM64_REG_FP = 1 +UC_ARM64_REG_LR = 2 + +# ARM64 instructions + +UC_ARM64_INS_INVALID = 0 +UC_ARM64_INS_MRS = 1 +UC_ARM64_INS_MSR = 2 +UC_ARM64_INS_SYS = 3 +UC_ARM64_INS_SYSL = 4 +UC_ARM64_INS_ENDING = 5 diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/arm_const.py b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/arm_const.py new file mode 100644 index 0000000000..e48825b088 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/arm_const.py @@ -0,0 +1,192 @@ +# For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT [arm_const.py] + +# ARM CPU + +UC_CPU_ARM_926 = 0 +UC_CPU_ARM_946 = 1 +UC_CPU_ARM_1026 = 2 +UC_CPU_ARM_1136_R2 = 3 +UC_CPU_ARM_1136 = 4 +UC_CPU_ARM_1176 = 5 +UC_CPU_ARM_11MPCORE = 6 +UC_CPU_ARM_CORTEX_M0 = 7 +UC_CPU_ARM_CORTEX_M3 = 8 +UC_CPU_ARM_CORTEX_M4 = 9 +UC_CPU_ARM_CORTEX_M7 = 10 +UC_CPU_ARM_CORTEX_M33 = 11 +UC_CPU_ARM_CORTEX_R5 = 12 +UC_CPU_ARM_CORTEX_R5F = 13 +UC_CPU_ARM_CORTEX_A7 = 14 +UC_CPU_ARM_CORTEX_A8 = 15 +UC_CPU_ARM_CORTEX_A9 = 16 +UC_CPU_ARM_CORTEX_A15 = 17 +UC_CPU_ARM_TI925T = 18 +UC_CPU_ARM_SA1100 = 19 +UC_CPU_ARM_SA1110 = 20 +UC_CPU_ARM_PXA250 = 21 +UC_CPU_ARM_PXA255 = 22 +UC_CPU_ARM_PXA260 = 23 +UC_CPU_ARM_PXA261 = 24 +UC_CPU_ARM_PXA262 = 25 +UC_CPU_ARM_PXA270 = 26 +UC_CPU_ARM_PXA270A0 = 27 +UC_CPU_ARM_PXA270A1 = 28 +UC_CPU_ARM_PXA270B0 = 29 +UC_CPU_ARM_PXA270B1 = 30 +UC_CPU_ARM_PXA270C0 = 31 +UC_CPU_ARM_PXA270C5 = 32 +UC_CPU_ARM_MAX = 33 +UC_CPU_ARM_ENDING = 34 + +# ARM registers + +UC_ARM_REG_INVALID = 0 +UC_ARM_REG_APSR = 1 +UC_ARM_REG_APSR_NZCV = 2 +UC_ARM_REG_CPSR = 3 +UC_ARM_REG_FPEXC = 4 +UC_ARM_REG_FPINST = 5 +UC_ARM_REG_FPSCR = 6 +UC_ARM_REG_FPSCR_NZCV = 7 +UC_ARM_REG_FPSID = 8 +UC_ARM_REG_ITSTATE = 9 +UC_ARM_REG_LR = 10 +UC_ARM_REG_PC = 11 +UC_ARM_REG_SP = 12 +UC_ARM_REG_SPSR = 13 +UC_ARM_REG_D0 = 14 +UC_ARM_REG_D1 = 15 +UC_ARM_REG_D2 = 16 +UC_ARM_REG_D3 = 17 +UC_ARM_REG_D4 = 18 +UC_ARM_REG_D5 = 19 +UC_ARM_REG_D6 = 20 +UC_ARM_REG_D7 = 21 +UC_ARM_REG_D8 = 22 +UC_ARM_REG_D9 = 23 +UC_ARM_REG_D10 = 24 +UC_ARM_REG_D11 = 25 +UC_ARM_REG_D12 = 26 +UC_ARM_REG_D13 = 27 +UC_ARM_REG_D14 = 28 +UC_ARM_REG_D15 = 29 +UC_ARM_REG_D16 = 30 +UC_ARM_REG_D17 = 31 +UC_ARM_REG_D18 = 32 +UC_ARM_REG_D19 = 33 +UC_ARM_REG_D20 = 34 +UC_ARM_REG_D21 = 35 +UC_ARM_REG_D22 = 36 +UC_ARM_REG_D23 = 37 +UC_ARM_REG_D24 = 38 +UC_ARM_REG_D25 = 39 +UC_ARM_REG_D26 = 40 +UC_ARM_REG_D27 = 41 +UC_ARM_REG_D28 = 42 +UC_ARM_REG_D29 = 43 +UC_ARM_REG_D30 = 44 +UC_ARM_REG_D31 = 45 +UC_ARM_REG_FPINST2 = 46 +UC_ARM_REG_MVFR0 = 47 +UC_ARM_REG_MVFR1 = 48 +UC_ARM_REG_MVFR2 = 49 +UC_ARM_REG_Q0 = 50 +UC_ARM_REG_Q1 = 51 +UC_ARM_REG_Q2 = 52 +UC_ARM_REG_Q3 = 53 +UC_ARM_REG_Q4 = 54 +UC_ARM_REG_Q5 = 55 +UC_ARM_REG_Q6 = 56 +UC_ARM_REG_Q7 = 57 +UC_ARM_REG_Q8 = 58 +UC_ARM_REG_Q9 = 59 +UC_ARM_REG_Q10 = 60 +UC_ARM_REG_Q11 = 61 +UC_ARM_REG_Q12 = 62 +UC_ARM_REG_Q13 = 63 +UC_ARM_REG_Q14 = 64 +UC_ARM_REG_Q15 = 65 +UC_ARM_REG_R0 = 66 +UC_ARM_REG_R1 = 67 +UC_ARM_REG_R2 = 68 +UC_ARM_REG_R3 = 69 +UC_ARM_REG_R4 = 70 +UC_ARM_REG_R5 = 71 +UC_ARM_REG_R6 = 72 +UC_ARM_REG_R7 = 73 +UC_ARM_REG_R8 = 74 +UC_ARM_REG_R9 = 75 +UC_ARM_REG_R10 = 76 +UC_ARM_REG_R11 = 77 +UC_ARM_REG_R12 = 78 +UC_ARM_REG_S0 = 79 +UC_ARM_REG_S1 = 80 +UC_ARM_REG_S2 = 81 +UC_ARM_REG_S3 = 82 +UC_ARM_REG_S4 = 83 +UC_ARM_REG_S5 = 84 +UC_ARM_REG_S6 = 85 +UC_ARM_REG_S7 = 86 +UC_ARM_REG_S8 = 87 +UC_ARM_REG_S9 = 88 +UC_ARM_REG_S10 = 89 +UC_ARM_REG_S11 = 90 +UC_ARM_REG_S12 = 91 +UC_ARM_REG_S13 = 92 +UC_ARM_REG_S14 = 93 +UC_ARM_REG_S15 = 94 +UC_ARM_REG_S16 = 95 +UC_ARM_REG_S17 = 96 +UC_ARM_REG_S18 = 97 +UC_ARM_REG_S19 = 98 +UC_ARM_REG_S20 = 99 +UC_ARM_REG_S21 = 100 +UC_ARM_REG_S22 = 101 +UC_ARM_REG_S23 = 102 +UC_ARM_REG_S24 = 103 +UC_ARM_REG_S25 = 104 +UC_ARM_REG_S26 = 105 +UC_ARM_REG_S27 = 106 +UC_ARM_REG_S28 = 107 +UC_ARM_REG_S29 = 108 +UC_ARM_REG_S30 = 109 +UC_ARM_REG_S31 = 110 +UC_ARM_REG_C1_C0_2 = 111 +UC_ARM_REG_C13_C0_2 = 112 +UC_ARM_REG_C13_C0_3 = 113 +UC_ARM_REG_IPSR = 114 +UC_ARM_REG_MSP = 115 +UC_ARM_REG_PSP = 116 +UC_ARM_REG_CONTROL = 117 +UC_ARM_REG_IAPSR = 118 +UC_ARM_REG_EAPSR = 119 +UC_ARM_REG_XPSR = 120 +UC_ARM_REG_EPSR = 121 +UC_ARM_REG_IEPSR = 122 +UC_ARM_REG_PRIMASK = 123 +UC_ARM_REG_BASEPRI = 124 +UC_ARM_REG_BASEPRI_MAX = 125 +UC_ARM_REG_FAULTMASK = 126 +UC_ARM_REG_APSR_NZCVQ = 127 +UC_ARM_REG_APSR_G = 128 +UC_ARM_REG_APSR_NZCVQG = 129 +UC_ARM_REG_IAPSR_NZCVQ = 130 +UC_ARM_REG_IAPSR_G = 131 +UC_ARM_REG_IAPSR_NZCVQG = 132 +UC_ARM_REG_EAPSR_NZCVQ = 133 +UC_ARM_REG_EAPSR_G = 134 +UC_ARM_REG_EAPSR_NZCVQG = 135 +UC_ARM_REG_XPSR_NZCVQ = 136 +UC_ARM_REG_XPSR_G = 137 +UC_ARM_REG_XPSR_NZCVQG = 138 +UC_ARM_REG_CP_REG = 139 +UC_ARM_REG_ENDING = 140 + +# alias registers +UC_ARM_REG_R13 = 12 +UC_ARM_REG_R14 = 10 +UC_ARM_REG_R15 = 11 +UC_ARM_REG_SB = 75 +UC_ARM_REG_SL = 76 +UC_ARM_REG_FP = 77 +UC_ARM_REG_IP = 78 diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/arm.h b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/arm.h new file mode 100644 index 0000000000..7b3686ade7 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/arm.h @@ -0,0 +1,232 @@ +/* Unicorn Engine */ +/* By Nguyen Anh Quynh , 2015-2017 */ +/* This file is released under LGPL2. + See COPYING.LGPL2 in root directory for more details +*/ + +#ifndef UNICORN_ARM_H +#define UNICORN_ARM_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _MSC_VER +#pragma warning(disable : 4201) +#endif + +//> ARM CPU +typedef enum uc_cpu_arm { + UC_CPU_ARM_926 = 0, + UC_CPU_ARM_946, + UC_CPU_ARM_1026, + UC_CPU_ARM_1136_R2, + UC_CPU_ARM_1136, + UC_CPU_ARM_1176, + UC_CPU_ARM_11MPCORE, + UC_CPU_ARM_CORTEX_M0, + UC_CPU_ARM_CORTEX_M3, + UC_CPU_ARM_CORTEX_M4, + UC_CPU_ARM_CORTEX_M7, + UC_CPU_ARM_CORTEX_M33, + UC_CPU_ARM_CORTEX_R5, + UC_CPU_ARM_CORTEX_R5F, + UC_CPU_ARM_CORTEX_A7, + UC_CPU_ARM_CORTEX_A8, + UC_CPU_ARM_CORTEX_A9, + UC_CPU_ARM_CORTEX_A15, + UC_CPU_ARM_TI925T, + UC_CPU_ARM_SA1100, + UC_CPU_ARM_SA1110, + UC_CPU_ARM_PXA250, + UC_CPU_ARM_PXA255, + UC_CPU_ARM_PXA260, + UC_CPU_ARM_PXA261, + UC_CPU_ARM_PXA262, + UC_CPU_ARM_PXA270, + UC_CPU_ARM_PXA270A0, + UC_CPU_ARM_PXA270A1, + UC_CPU_ARM_PXA270B0, + UC_CPU_ARM_PXA270B1, + UC_CPU_ARM_PXA270C0, + UC_CPU_ARM_PXA270C5, + UC_CPU_ARM_MAX, + + UC_CPU_ARM_ENDING +} uc_cpu_arm; + +// ARM coprocessor registers, use this with UC_ARM_REG_CP_REG to +// in call to uc_reg_write/read() to access the registers. +typedef struct uc_arm_cp_reg { + uint32_t cp; // The coprocessor identifier + uint32_t is64; // Is it a 64 bit control register + uint32_t sec; // Security state + uint32_t crn; // Coprocessor register number + uint32_t crm; // Coprocessor register number + uint32_t opc1; // Opcode1 + uint32_t opc2; // Opcode2 + uint64_t val; // The value to read/write +} uc_arm_cp_reg; + +//> ARM registers +typedef enum uc_arm_reg { + UC_ARM_REG_INVALID = 0, + UC_ARM_REG_APSR, + UC_ARM_REG_APSR_NZCV, + UC_ARM_REG_CPSR, + UC_ARM_REG_FPEXC, + UC_ARM_REG_FPINST, + UC_ARM_REG_FPSCR, + UC_ARM_REG_FPSCR_NZCV, + UC_ARM_REG_FPSID, + UC_ARM_REG_ITSTATE, + UC_ARM_REG_LR, + UC_ARM_REG_PC, + UC_ARM_REG_SP, + UC_ARM_REG_SPSR, + UC_ARM_REG_D0, + UC_ARM_REG_D1, + UC_ARM_REG_D2, + UC_ARM_REG_D3, + UC_ARM_REG_D4, + UC_ARM_REG_D5, + UC_ARM_REG_D6, + UC_ARM_REG_D7, + UC_ARM_REG_D8, + UC_ARM_REG_D9, + UC_ARM_REG_D10, + UC_ARM_REG_D11, + UC_ARM_REG_D12, + UC_ARM_REG_D13, + UC_ARM_REG_D14, + UC_ARM_REG_D15, + UC_ARM_REG_D16, + UC_ARM_REG_D17, + UC_ARM_REG_D18, + UC_ARM_REG_D19, + UC_ARM_REG_D20, + UC_ARM_REG_D21, + UC_ARM_REG_D22, + UC_ARM_REG_D23, + UC_ARM_REG_D24, + UC_ARM_REG_D25, + UC_ARM_REG_D26, + UC_ARM_REG_D27, + UC_ARM_REG_D28, + UC_ARM_REG_D29, + UC_ARM_REG_D30, + UC_ARM_REG_D31, + UC_ARM_REG_FPINST2, + UC_ARM_REG_MVFR0, + UC_ARM_REG_MVFR1, + UC_ARM_REG_MVFR2, + UC_ARM_REG_Q0, + UC_ARM_REG_Q1, + UC_ARM_REG_Q2, + UC_ARM_REG_Q3, + UC_ARM_REG_Q4, + UC_ARM_REG_Q5, + UC_ARM_REG_Q6, + UC_ARM_REG_Q7, + UC_ARM_REG_Q8, + UC_ARM_REG_Q9, + UC_ARM_REG_Q10, + UC_ARM_REG_Q11, + UC_ARM_REG_Q12, + UC_ARM_REG_Q13, + UC_ARM_REG_Q14, + UC_ARM_REG_Q15, + UC_ARM_REG_R0, + UC_ARM_REG_R1, + UC_ARM_REG_R2, + UC_ARM_REG_R3, + UC_ARM_REG_R4, + UC_ARM_REG_R5, + UC_ARM_REG_R6, + UC_ARM_REG_R7, + UC_ARM_REG_R8, + UC_ARM_REG_R9, + UC_ARM_REG_R10, + UC_ARM_REG_R11, + UC_ARM_REG_R12, + UC_ARM_REG_S0, + UC_ARM_REG_S1, + UC_ARM_REG_S2, + UC_ARM_REG_S3, + UC_ARM_REG_S4, + UC_ARM_REG_S5, + UC_ARM_REG_S6, + UC_ARM_REG_S7, + UC_ARM_REG_S8, + UC_ARM_REG_S9, + UC_ARM_REG_S10, + UC_ARM_REG_S11, + UC_ARM_REG_S12, + UC_ARM_REG_S13, + UC_ARM_REG_S14, + UC_ARM_REG_S15, + UC_ARM_REG_S16, + UC_ARM_REG_S17, + UC_ARM_REG_S18, + UC_ARM_REG_S19, + UC_ARM_REG_S20, + UC_ARM_REG_S21, + UC_ARM_REG_S22, + UC_ARM_REG_S23, + UC_ARM_REG_S24, + UC_ARM_REG_S25, + UC_ARM_REG_S26, + UC_ARM_REG_S27, + UC_ARM_REG_S28, + UC_ARM_REG_S29, + UC_ARM_REG_S30, + UC_ARM_REG_S31, + + UC_ARM_REG_C1_C0_2, // Depreciated, use UC_ARM_REG_CP_REG instead + UC_ARM_REG_C13_C0_2, // Depreciated, use UC_ARM_REG_CP_REG instead + UC_ARM_REG_C13_C0_3, // Depreciated, use UC_ARM_REG_CP_REG instead + + UC_ARM_REG_IPSR, + UC_ARM_REG_MSP, + UC_ARM_REG_PSP, + UC_ARM_REG_CONTROL, + UC_ARM_REG_IAPSR, + UC_ARM_REG_EAPSR, + UC_ARM_REG_XPSR, + UC_ARM_REG_EPSR, + UC_ARM_REG_IEPSR, + UC_ARM_REG_PRIMASK, + UC_ARM_REG_BASEPRI, + UC_ARM_REG_BASEPRI_MAX, + UC_ARM_REG_FAULTMASK, + UC_ARM_REG_APSR_NZCVQ, + UC_ARM_REG_APSR_G, + UC_ARM_REG_APSR_NZCVQG, + UC_ARM_REG_IAPSR_NZCVQ, + UC_ARM_REG_IAPSR_G, + UC_ARM_REG_IAPSR_NZCVQG, + UC_ARM_REG_EAPSR_NZCVQ, + UC_ARM_REG_EAPSR_G, + UC_ARM_REG_EAPSR_NZCVQG, + UC_ARM_REG_XPSR_NZCVQ, + UC_ARM_REG_XPSR_G, + UC_ARM_REG_XPSR_NZCVQG, + UC_ARM_REG_CP_REG, + UC_ARM_REG_ENDING, // <-- mark the end of the list or registers + + //> alias registers + UC_ARM_REG_R13 = UC_ARM_REG_SP, + UC_ARM_REG_R14 = UC_ARM_REG_LR, + UC_ARM_REG_R15 = UC_ARM_REG_PC, + + UC_ARM_REG_SB = UC_ARM_REG_R9, + UC_ARM_REG_SL = UC_ARM_REG_R10, + UC_ARM_REG_FP = UC_ARM_REG_R11, + UC_ARM_REG_IP = UC_ARM_REG_R12, +} uc_arm_reg; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/arm64.h b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/arm64.h new file mode 100644 index 0000000000..fd8192fb47 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/arm64.h @@ -0,0 +1,393 @@ +/* Unicorn Emulator Engine */ +/* By Nguyen Anh Quynh , 2015-2017 */ +/* This file is released under LGPL2. + See COPYING.LGPL2 in root directory for more details +*/ + +#ifndef UNICORN_ARM64_H +#define UNICORN_ARM64_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _MSC_VER +#pragma warning(disable : 4201) +#endif + +//> ARM64 CPU +typedef enum uc_cpu_arm64 { + UC_CPU_ARM64_A57 = 0, + UC_CPU_ARM64_A53, + UC_CPU_ARM64_A72, + UC_CPU_ARM64_MAX, + + UC_CPU_ARM64_ENDING +} uc_cpu_arm64; + +// ARM64 coprocessor registers, use this with UC_ARM64_REG_CP_REG to +// in call to uc_reg_write/read() to access the registers. +typedef struct uc_arm64_cp_reg { + uint32_t crn; // Coprocessor register number + uint32_t crm; // Coprocessor register number + uint32_t op0; // Opcode0 + uint32_t op1; // Opcode1 + uint32_t op2; // Opcode2 + uint64_t val; // The value to read/write +} uc_arm64_cp_reg; + +//> ARM64 registers +typedef enum uc_arm64_reg { + UC_ARM64_REG_INVALID = 0, + + UC_ARM64_REG_X29, + UC_ARM64_REG_X30, + UC_ARM64_REG_NZCV, + UC_ARM64_REG_SP, + UC_ARM64_REG_WSP, + UC_ARM64_REG_WZR, + UC_ARM64_REG_XZR, + UC_ARM64_REG_B0, + UC_ARM64_REG_B1, + UC_ARM64_REG_B2, + UC_ARM64_REG_B3, + UC_ARM64_REG_B4, + UC_ARM64_REG_B5, + UC_ARM64_REG_B6, + UC_ARM64_REG_B7, + UC_ARM64_REG_B8, + UC_ARM64_REG_B9, + UC_ARM64_REG_B10, + UC_ARM64_REG_B11, + UC_ARM64_REG_B12, + UC_ARM64_REG_B13, + UC_ARM64_REG_B14, + UC_ARM64_REG_B15, + UC_ARM64_REG_B16, + UC_ARM64_REG_B17, + UC_ARM64_REG_B18, + UC_ARM64_REG_B19, + UC_ARM64_REG_B20, + UC_ARM64_REG_B21, + UC_ARM64_REG_B22, + UC_ARM64_REG_B23, + UC_ARM64_REG_B24, + UC_ARM64_REG_B25, + UC_ARM64_REG_B26, + UC_ARM64_REG_B27, + UC_ARM64_REG_B28, + UC_ARM64_REG_B29, + UC_ARM64_REG_B30, + UC_ARM64_REG_B31, + UC_ARM64_REG_D0, + UC_ARM64_REG_D1, + UC_ARM64_REG_D2, + UC_ARM64_REG_D3, + UC_ARM64_REG_D4, + UC_ARM64_REG_D5, + UC_ARM64_REG_D6, + UC_ARM64_REG_D7, + UC_ARM64_REG_D8, + UC_ARM64_REG_D9, + UC_ARM64_REG_D10, + UC_ARM64_REG_D11, + UC_ARM64_REG_D12, + UC_ARM64_REG_D13, + UC_ARM64_REG_D14, + UC_ARM64_REG_D15, + UC_ARM64_REG_D16, + UC_ARM64_REG_D17, + UC_ARM64_REG_D18, + UC_ARM64_REG_D19, + UC_ARM64_REG_D20, + UC_ARM64_REG_D21, + UC_ARM64_REG_D22, + UC_ARM64_REG_D23, + UC_ARM64_REG_D24, + UC_ARM64_REG_D25, + UC_ARM64_REG_D26, + UC_ARM64_REG_D27, + UC_ARM64_REG_D28, + UC_ARM64_REG_D29, + UC_ARM64_REG_D30, + UC_ARM64_REG_D31, + UC_ARM64_REG_H0, + UC_ARM64_REG_H1, + UC_ARM64_REG_H2, + UC_ARM64_REG_H3, + UC_ARM64_REG_H4, + UC_ARM64_REG_H5, + UC_ARM64_REG_H6, + UC_ARM64_REG_H7, + UC_ARM64_REG_H8, + UC_ARM64_REG_H9, + UC_ARM64_REG_H10, + UC_ARM64_REG_H11, + UC_ARM64_REG_H12, + UC_ARM64_REG_H13, + UC_ARM64_REG_H14, + UC_ARM64_REG_H15, + UC_ARM64_REG_H16, + UC_ARM64_REG_H17, + UC_ARM64_REG_H18, + UC_ARM64_REG_H19, + UC_ARM64_REG_H20, + UC_ARM64_REG_H21, + UC_ARM64_REG_H22, + UC_ARM64_REG_H23, + UC_ARM64_REG_H24, + UC_ARM64_REG_H25, + UC_ARM64_REG_H26, + UC_ARM64_REG_H27, + UC_ARM64_REG_H28, + UC_ARM64_REG_H29, + UC_ARM64_REG_H30, + UC_ARM64_REG_H31, + UC_ARM64_REG_Q0, + UC_ARM64_REG_Q1, + UC_ARM64_REG_Q2, + UC_ARM64_REG_Q3, + UC_ARM64_REG_Q4, + UC_ARM64_REG_Q5, + UC_ARM64_REG_Q6, + UC_ARM64_REG_Q7, + UC_ARM64_REG_Q8, + UC_ARM64_REG_Q9, + UC_ARM64_REG_Q10, + UC_ARM64_REG_Q11, + UC_ARM64_REG_Q12, + UC_ARM64_REG_Q13, + UC_ARM64_REG_Q14, + UC_ARM64_REG_Q15, + UC_ARM64_REG_Q16, + UC_ARM64_REG_Q17, + UC_ARM64_REG_Q18, + UC_ARM64_REG_Q19, + UC_ARM64_REG_Q20, + UC_ARM64_REG_Q21, + UC_ARM64_REG_Q22, + UC_ARM64_REG_Q23, + UC_ARM64_REG_Q24, + UC_ARM64_REG_Q25, + UC_ARM64_REG_Q26, + UC_ARM64_REG_Q27, + UC_ARM64_REG_Q28, + UC_ARM64_REG_Q29, + UC_ARM64_REG_Q30, + UC_ARM64_REG_Q31, + UC_ARM64_REG_S0, + UC_ARM64_REG_S1, + UC_ARM64_REG_S2, + UC_ARM64_REG_S3, + UC_ARM64_REG_S4, + UC_ARM64_REG_S5, + UC_ARM64_REG_S6, + UC_ARM64_REG_S7, + UC_ARM64_REG_S8, + UC_ARM64_REG_S9, + UC_ARM64_REG_S10, + UC_ARM64_REG_S11, + UC_ARM64_REG_S12, + UC_ARM64_REG_S13, + UC_ARM64_REG_S14, + UC_ARM64_REG_S15, + UC_ARM64_REG_S16, + UC_ARM64_REG_S17, + UC_ARM64_REG_S18, + UC_ARM64_REG_S19, + UC_ARM64_REG_S20, + UC_ARM64_REG_S21, + UC_ARM64_REG_S22, + UC_ARM64_REG_S23, + UC_ARM64_REG_S24, + UC_ARM64_REG_S25, + UC_ARM64_REG_S26, + UC_ARM64_REG_S27, + UC_ARM64_REG_S28, + UC_ARM64_REG_S29, + UC_ARM64_REG_S30, + UC_ARM64_REG_S31, + UC_ARM64_REG_W0, + UC_ARM64_REG_W1, + UC_ARM64_REG_W2, + UC_ARM64_REG_W3, + UC_ARM64_REG_W4, + UC_ARM64_REG_W5, + UC_ARM64_REG_W6, + UC_ARM64_REG_W7, + UC_ARM64_REG_W8, + UC_ARM64_REG_W9, + UC_ARM64_REG_W10, + UC_ARM64_REG_W11, + UC_ARM64_REG_W12, + UC_ARM64_REG_W13, + UC_ARM64_REG_W14, + UC_ARM64_REG_W15, + UC_ARM64_REG_W16, + UC_ARM64_REG_W17, + UC_ARM64_REG_W18, + UC_ARM64_REG_W19, + UC_ARM64_REG_W20, + UC_ARM64_REG_W21, + UC_ARM64_REG_W22, + UC_ARM64_REG_W23, + UC_ARM64_REG_W24, + UC_ARM64_REG_W25, + UC_ARM64_REG_W26, + UC_ARM64_REG_W27, + UC_ARM64_REG_W28, + UC_ARM64_REG_W29, + UC_ARM64_REG_W30, + UC_ARM64_REG_X0, + UC_ARM64_REG_X1, + UC_ARM64_REG_X2, + UC_ARM64_REG_X3, + UC_ARM64_REG_X4, + UC_ARM64_REG_X5, + UC_ARM64_REG_X6, + UC_ARM64_REG_X7, + UC_ARM64_REG_X8, + UC_ARM64_REG_X9, + UC_ARM64_REG_X10, + UC_ARM64_REG_X11, + UC_ARM64_REG_X12, + UC_ARM64_REG_X13, + UC_ARM64_REG_X14, + UC_ARM64_REG_X15, + UC_ARM64_REG_X16, + UC_ARM64_REG_X17, + UC_ARM64_REG_X18, + UC_ARM64_REG_X19, + UC_ARM64_REG_X20, + UC_ARM64_REG_X21, + UC_ARM64_REG_X22, + UC_ARM64_REG_X23, + UC_ARM64_REG_X24, + UC_ARM64_REG_X25, + UC_ARM64_REG_X26, + UC_ARM64_REG_X27, + UC_ARM64_REG_X28, + + UC_ARM64_REG_V0, + UC_ARM64_REG_V1, + UC_ARM64_REG_V2, + UC_ARM64_REG_V3, + UC_ARM64_REG_V4, + UC_ARM64_REG_V5, + UC_ARM64_REG_V6, + UC_ARM64_REG_V7, + UC_ARM64_REG_V8, + UC_ARM64_REG_V9, + UC_ARM64_REG_V10, + UC_ARM64_REG_V11, + UC_ARM64_REG_V12, + UC_ARM64_REG_V13, + UC_ARM64_REG_V14, + UC_ARM64_REG_V15, + UC_ARM64_REG_V16, + UC_ARM64_REG_V17, + UC_ARM64_REG_V18, + UC_ARM64_REG_V19, + UC_ARM64_REG_V20, + UC_ARM64_REG_V21, + UC_ARM64_REG_V22, + UC_ARM64_REG_V23, + UC_ARM64_REG_V24, + UC_ARM64_REG_V25, + UC_ARM64_REG_V26, + UC_ARM64_REG_V27, + UC_ARM64_REG_V28, + UC_ARM64_REG_V29, + UC_ARM64_REG_V30, + UC_ARM64_REG_V31, + + //> pseudo registers + UC_ARM64_REG_PC, // program counter register + + UC_ARM64_REG_CPACR_EL1, + + //> thread registers, depreciated, use UC_ARM64_REG_CP_REG instead + UC_ARM64_REG_TPIDR_EL0, + UC_ARM64_REG_TPIDRRO_EL0, + UC_ARM64_REG_TPIDR_EL1, + + UC_ARM64_REG_PSTATE, + + //> exception link registers, depreciated, use UC_ARM64_REG_CP_REG instead + UC_ARM64_REG_ELR_EL0, + UC_ARM64_REG_ELR_EL1, + UC_ARM64_REG_ELR_EL2, + UC_ARM64_REG_ELR_EL3, + + //> stack pointers registers, depreciated, use UC_ARM64_REG_CP_REG instead + UC_ARM64_REG_SP_EL0, + UC_ARM64_REG_SP_EL1, + UC_ARM64_REG_SP_EL2, + UC_ARM64_REG_SP_EL3, + + //> other CP15 registers, depreciated, use UC_ARM64_REG_CP_REG instead + UC_ARM64_REG_TTBR0_EL1, + UC_ARM64_REG_TTBR1_EL1, + + UC_ARM64_REG_ESR_EL0, + UC_ARM64_REG_ESR_EL1, + UC_ARM64_REG_ESR_EL2, + UC_ARM64_REG_ESR_EL3, + + UC_ARM64_REG_FAR_EL0, + UC_ARM64_REG_FAR_EL1, + UC_ARM64_REG_FAR_EL2, + UC_ARM64_REG_FAR_EL3, + + UC_ARM64_REG_PAR_EL1, + + UC_ARM64_REG_MAIR_EL1, + + UC_ARM64_REG_VBAR_EL0, + UC_ARM64_REG_VBAR_EL1, + UC_ARM64_REG_VBAR_EL2, + UC_ARM64_REG_VBAR_EL3, + + UC_ARM64_REG_CP_REG, + + //> floating point control and status registers + UC_ARM64_REG_FPCR, + UC_ARM64_REG_FPSR, + + UC_ARM64_REG_ENDING, // <-- mark the end of the list of registers + + //> alias registers + + UC_ARM64_REG_IP0 = UC_ARM64_REG_X16, + UC_ARM64_REG_IP1 = UC_ARM64_REG_X17, + UC_ARM64_REG_FP = UC_ARM64_REG_X29, + UC_ARM64_REG_LR = UC_ARM64_REG_X30, +} uc_arm64_reg; + +// Callback function for tracing MRS/MSR/SYS/SYSL. If this callback returns +// true, the read/write to system registers would be skipped (even though it may +// cause exceptions!). Note one callback per instruction is allowed. +// @reg: The source/destination register. +// @cp_reg: The source/destincation system register. +// @user_data: The user data. +typedef uint32_t (*uc_cb_insn_sys_t)(uc_engine *uc, uc_arm64_reg reg, + const uc_arm64_cp_reg *cp_reg, + void *user_data); + +//> ARM64 instructions +typedef enum uc_arm64_insn { + UC_ARM64_INS_INVALID = 0, + + UC_ARM64_INS_MRS, + UC_ARM64_INS_MSR, + UC_ARM64_INS_SYS, + UC_ARM64_INS_SYSL, + + UC_ARM64_INS_ENDING +} uc_arm64_insn; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/m68k.h b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/m68k.h new file mode 100644 index 0000000000..c4a16ee88d --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/m68k.h @@ -0,0 +1,65 @@ +/* Unicorn Emulator Engine */ +/* By Nguyen Anh Quynh , 2014-2017 */ +/* This file is released under LGPL2. + See COPYING.LGPL2 in root directory for more details +*/ + +#ifndef UNICORN_M68K_H +#define UNICORN_M68K_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _MSC_VER +#pragma warning(disable : 4201) +#endif + +//> M68K CPU +typedef enum uc_cpu_m68k { + UC_CPU_M68K_M5206 = 0, + UC_CPU_M68K_M68000, + UC_CPU_M68K_M68020, + UC_CPU_M68K_M68030, + UC_CPU_M68K_M68040, + UC_CPU_M68K_M68060, + UC_CPU_M68K_M5208, + UC_CPU_M68K_CFV4E, + UC_CPU_M68K_ANY, + + UC_CPU_M68K_ENDING +} uc_cpu_m68k; + +//> M68K registers +typedef enum uc_m68k_reg { + UC_M68K_REG_INVALID = 0, + + UC_M68K_REG_A0, + UC_M68K_REG_A1, + UC_M68K_REG_A2, + UC_M68K_REG_A3, + UC_M68K_REG_A4, + UC_M68K_REG_A5, + UC_M68K_REG_A6, + UC_M68K_REG_A7, + + UC_M68K_REG_D0, + UC_M68K_REG_D1, + UC_M68K_REG_D2, + UC_M68K_REG_D3, + UC_M68K_REG_D4, + UC_M68K_REG_D5, + UC_M68K_REG_D6, + UC_M68K_REG_D7, + + UC_M68K_REG_SR, + UC_M68K_REG_PC, + + UC_M68K_REG_ENDING, // <-- mark the end of the list of registers +} uc_m68k_reg; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/mips.h b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/mips.h new file mode 100644 index 0000000000..a1240a2ac4 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/mips.h @@ -0,0 +1,275 @@ +/* Unicorn Emulator Engine */ +/* By Nguyen Anh Quynh , 2015-2017 */ +/* This file is released under LGPL2. + See COPYING.LGPL2 in root directory for more details +*/ + +#ifndef UNICORN_MIPS_H +#define UNICORN_MIPS_H + +#ifdef __cplusplus +extern "C" { +#endif + +// GCC MIPS toolchain has a default macro called "mips" which breaks +// compilation +#undef mips + +#ifdef _MSC_VER +#pragma warning(disable : 4201) +#endif + +//> MIPS32 CPUS +typedef enum uc_cpu_mips32 { + UC_CPU_MIPS32_4KC = 0, + UC_CPU_MIPS32_4KM, + UC_CPU_MIPS32_4KECR1, + UC_CPU_MIPS32_4KEMR1, + UC_CPU_MIPS32_4KEC, + UC_CPU_MIPS32_4KEM, + UC_CPU_MIPS32_24KC, + UC_CPU_MIPS32_24KEC, + UC_CPU_MIPS32_24KF, + UC_CPU_MIPS32_34KF, + UC_CPU_MIPS32_74KF, + UC_CPU_MIPS32_M14K, + UC_CPU_MIPS32_M14KC, + UC_CPU_MIPS32_P5600, + UC_CPU_MIPS32_MIPS32R6_GENERIC, + UC_CPU_MIPS32_I7200, + + UC_CPU_MIPS32_ENDING +} uc_cpu_mips32; + +//> MIPS64 CPUS +typedef enum uc_cpu_mips64 { + UC_CPU_MIPS64_R4000 = 0, + UC_CPU_MIPS64_VR5432, + UC_CPU_MIPS64_5KC, + UC_CPU_MIPS64_5KF, + UC_CPU_MIPS64_20KC, + UC_CPU_MIPS64_MIPS64R2_GENERIC, + UC_CPU_MIPS64_5KEC, + UC_CPU_MIPS64_5KEF, + UC_CPU_MIPS64_I6400, + UC_CPU_MIPS64_I6500, + UC_CPU_MIPS64_LOONGSON_2E, + UC_CPU_MIPS64_LOONGSON_2F, + UC_CPU_MIPS64_MIPS64DSPR2, + + UC_CPU_MIPS64_ENDING +} uc_cpu_mips64; + +//> MIPS registers +typedef enum UC_MIPS_REG { + UC_MIPS_REG_INVALID = 0, + //> General purpose registers + UC_MIPS_REG_PC, + + UC_MIPS_REG_0, + UC_MIPS_REG_1, + UC_MIPS_REG_2, + UC_MIPS_REG_3, + UC_MIPS_REG_4, + UC_MIPS_REG_5, + UC_MIPS_REG_6, + UC_MIPS_REG_7, + UC_MIPS_REG_8, + UC_MIPS_REG_9, + UC_MIPS_REG_10, + UC_MIPS_REG_11, + UC_MIPS_REG_12, + UC_MIPS_REG_13, + UC_MIPS_REG_14, + UC_MIPS_REG_15, + UC_MIPS_REG_16, + UC_MIPS_REG_17, + UC_MIPS_REG_18, + UC_MIPS_REG_19, + UC_MIPS_REG_20, + UC_MIPS_REG_21, + UC_MIPS_REG_22, + UC_MIPS_REG_23, + UC_MIPS_REG_24, + UC_MIPS_REG_25, + UC_MIPS_REG_26, + UC_MIPS_REG_27, + UC_MIPS_REG_28, + UC_MIPS_REG_29, + UC_MIPS_REG_30, + UC_MIPS_REG_31, + + //> DSP registers + UC_MIPS_REG_DSPCCOND, + UC_MIPS_REG_DSPCARRY, + UC_MIPS_REG_DSPEFI, + UC_MIPS_REG_DSPOUTFLAG, + UC_MIPS_REG_DSPOUTFLAG16_19, + UC_MIPS_REG_DSPOUTFLAG20, + UC_MIPS_REG_DSPOUTFLAG21, + UC_MIPS_REG_DSPOUTFLAG22, + UC_MIPS_REG_DSPOUTFLAG23, + UC_MIPS_REG_DSPPOS, + UC_MIPS_REG_DSPSCOUNT, + + //> ACC registers + UC_MIPS_REG_AC0, + UC_MIPS_REG_AC1, + UC_MIPS_REG_AC2, + UC_MIPS_REG_AC3, + + //> COP registers + UC_MIPS_REG_CC0, + UC_MIPS_REG_CC1, + UC_MIPS_REG_CC2, + UC_MIPS_REG_CC3, + UC_MIPS_REG_CC4, + UC_MIPS_REG_CC5, + UC_MIPS_REG_CC6, + UC_MIPS_REG_CC7, + + //> FPU registers + UC_MIPS_REG_F0, + UC_MIPS_REG_F1, + UC_MIPS_REG_F2, + UC_MIPS_REG_F3, + UC_MIPS_REG_F4, + UC_MIPS_REG_F5, + UC_MIPS_REG_F6, + UC_MIPS_REG_F7, + UC_MIPS_REG_F8, + UC_MIPS_REG_F9, + UC_MIPS_REG_F10, + UC_MIPS_REG_F11, + UC_MIPS_REG_F12, + UC_MIPS_REG_F13, + UC_MIPS_REG_F14, + UC_MIPS_REG_F15, + UC_MIPS_REG_F16, + UC_MIPS_REG_F17, + UC_MIPS_REG_F18, + UC_MIPS_REG_F19, + UC_MIPS_REG_F20, + UC_MIPS_REG_F21, + UC_MIPS_REG_F22, + UC_MIPS_REG_F23, + UC_MIPS_REG_F24, + UC_MIPS_REG_F25, + UC_MIPS_REG_F26, + UC_MIPS_REG_F27, + UC_MIPS_REG_F28, + UC_MIPS_REG_F29, + UC_MIPS_REG_F30, + UC_MIPS_REG_F31, + + UC_MIPS_REG_FCC0, + UC_MIPS_REG_FCC1, + UC_MIPS_REG_FCC2, + UC_MIPS_REG_FCC3, + UC_MIPS_REG_FCC4, + UC_MIPS_REG_FCC5, + UC_MIPS_REG_FCC6, + UC_MIPS_REG_FCC7, + + //> AFPR128 + UC_MIPS_REG_W0, + UC_MIPS_REG_W1, + UC_MIPS_REG_W2, + UC_MIPS_REG_W3, + UC_MIPS_REG_W4, + UC_MIPS_REG_W5, + UC_MIPS_REG_W6, + UC_MIPS_REG_W7, + UC_MIPS_REG_W8, + UC_MIPS_REG_W9, + UC_MIPS_REG_W10, + UC_MIPS_REG_W11, + UC_MIPS_REG_W12, + UC_MIPS_REG_W13, + UC_MIPS_REG_W14, + UC_MIPS_REG_W15, + UC_MIPS_REG_W16, + UC_MIPS_REG_W17, + UC_MIPS_REG_W18, + UC_MIPS_REG_W19, + UC_MIPS_REG_W20, + UC_MIPS_REG_W21, + UC_MIPS_REG_W22, + UC_MIPS_REG_W23, + UC_MIPS_REG_W24, + UC_MIPS_REG_W25, + UC_MIPS_REG_W26, + UC_MIPS_REG_W27, + UC_MIPS_REG_W28, + UC_MIPS_REG_W29, + UC_MIPS_REG_W30, + UC_MIPS_REG_W31, + + UC_MIPS_REG_HI, + UC_MIPS_REG_LO, + + UC_MIPS_REG_P0, + UC_MIPS_REG_P1, + UC_MIPS_REG_P2, + + UC_MIPS_REG_MPL0, + UC_MIPS_REG_MPL1, + UC_MIPS_REG_MPL2, + + UC_MIPS_REG_CP0_CONFIG3, + UC_MIPS_REG_CP0_USERLOCAL, + UC_MIPS_REG_CP0_STATUS, + + UC_MIPS_REG_ENDING, // <-- mark the end of the list or registers + + // alias registers + UC_MIPS_REG_ZERO = UC_MIPS_REG_0, + UC_MIPS_REG_AT = UC_MIPS_REG_1, + UC_MIPS_REG_V0 = UC_MIPS_REG_2, + UC_MIPS_REG_V1 = UC_MIPS_REG_3, + UC_MIPS_REG_A0 = UC_MIPS_REG_4, + UC_MIPS_REG_A1 = UC_MIPS_REG_5, + UC_MIPS_REG_A2 = UC_MIPS_REG_6, + UC_MIPS_REG_A3 = UC_MIPS_REG_7, + UC_MIPS_REG_T0 = UC_MIPS_REG_8, + UC_MIPS_REG_T1 = UC_MIPS_REG_9, + UC_MIPS_REG_T2 = UC_MIPS_REG_10, + UC_MIPS_REG_T3 = UC_MIPS_REG_11, + UC_MIPS_REG_T4 = UC_MIPS_REG_12, + UC_MIPS_REG_T5 = UC_MIPS_REG_13, + UC_MIPS_REG_T6 = UC_MIPS_REG_14, + UC_MIPS_REG_T7 = UC_MIPS_REG_15, + UC_MIPS_REG_S0 = UC_MIPS_REG_16, + UC_MIPS_REG_S1 = UC_MIPS_REG_17, + UC_MIPS_REG_S2 = UC_MIPS_REG_18, + UC_MIPS_REG_S3 = UC_MIPS_REG_19, + UC_MIPS_REG_S4 = UC_MIPS_REG_20, + UC_MIPS_REG_S5 = UC_MIPS_REG_21, + UC_MIPS_REG_S6 = UC_MIPS_REG_22, + UC_MIPS_REG_S7 = UC_MIPS_REG_23, + UC_MIPS_REG_T8 = UC_MIPS_REG_24, + UC_MIPS_REG_T9 = UC_MIPS_REG_25, + UC_MIPS_REG_K0 = UC_MIPS_REG_26, + UC_MIPS_REG_K1 = UC_MIPS_REG_27, + UC_MIPS_REG_GP = UC_MIPS_REG_28, + UC_MIPS_REG_SP = UC_MIPS_REG_29, + UC_MIPS_REG_FP = UC_MIPS_REG_30, + UC_MIPS_REG_S8 = UC_MIPS_REG_30, + UC_MIPS_REG_RA = UC_MIPS_REG_31, + + UC_MIPS_REG_HI0 = UC_MIPS_REG_AC0, + UC_MIPS_REG_HI1 = UC_MIPS_REG_AC1, + UC_MIPS_REG_HI2 = UC_MIPS_REG_AC2, + UC_MIPS_REG_HI3 = UC_MIPS_REG_AC3, + + UC_MIPS_REG_LO0 = UC_MIPS_REG_HI0, + UC_MIPS_REG_LO1 = UC_MIPS_REG_HI1, + UC_MIPS_REG_LO2 = UC_MIPS_REG_HI2, + UC_MIPS_REG_LO3 = UC_MIPS_REG_HI3, +} UC_MIPS_REG; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/platform.h b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/platform.h new file mode 100644 index 0000000000..25d3562eca --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/platform.h @@ -0,0 +1,263 @@ +/* This file is released under LGPL2. + See COPYING.LGPL2 in root directory for more details +*/ + +/* + This file is to support header files that are missing in MSVC and + other non-standard compilers. +*/ +#ifndef UNICORN_PLATFORM_H +#define UNICORN_PLATFORM_H + +/* +These are the various MSVC versions as given by _MSC_VER: +MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015) +MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013) +MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012) +MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010) +MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008) +MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005) +MSVC++ 7.1 _MSC_VER == 1310 (Visual Studio 2003) +MSVC++ 7.0 _MSC_VER == 1300 +MSVC++ 6.0 _MSC_VER == 1200 +MSVC++ 5.0 _MSC_VER == 1100 +*/ +#define MSC_VER_VS2003 1310 +#define MSC_VER_VS2005 1400 +#define MSC_VER_VS2008 1500 +#define MSC_VER_VS2010 1600 +#define MSC_VER_VS2012 1700 +#define MSC_VER_VS2013 1800 +#define MSC_VER_VS2015 1900 + +// handle stdbool.h compatibility +#if !defined(__CYGWIN__) && !defined(__MINGW32__) && !defined(__MINGW64__) && \ + (defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64)) +// MSVC + +// stdbool.h +#if (_MSC_VER < MSC_VER_VS2013) || defined(_KERNEL_MODE) +// this system does not have stdbool.h +#ifndef __cplusplus +typedef unsigned char bool; +#define false 0 +#define true 1 +#endif // __cplusplus + +#else +// VisualStudio 2013+ -> C99 is supported +#include +#endif // (_MSC_VER < MSC_VER_VS2013) || defined(_KERNEL_MODE) + +#else +// not MSVC -> C99 is supported +#include +#endif // !defined(__CYGWIN__) && !defined(__MINGW32__) && !defined(__MINGW64__) + // && (defined (WIN32) || defined (WIN64) || defined (_WIN32) || defined + // (_WIN64)) + +#if (defined(_MSC_VER) && (_MSC_VER < MSC_VER_VS2010)) || defined(_KERNEL_MODE) +// this system does not have stdint.h +typedef signed char int8_t; +typedef signed short int16_t; +typedef signed int int32_t; +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef signed long long int64_t; +typedef unsigned long long uint64_t; + +typedef signed char int_fast8_t; +typedef int int_fast16_t; +typedef int int_fast32_t; +typedef long long int_fast64_t; +typedef unsigned char uint_fast8_t; +typedef unsigned int uint_fast16_t; +typedef unsigned int uint_fast32_t; +typedef unsigned long long uint_fast64_t; + +#if !defined(_W64) +#if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 +#define _W64 __w64 +#else +#define _W64 +#endif +#endif + +#ifndef _INTPTR_T_DEFINED +#define _INTPTR_T_DEFINED +#ifdef _WIN64 +typedef long long intptr_t; +#else /* _WIN64 */ +typedef _W64 int intptr_t; +#endif /* _WIN64 */ +#endif /* _INTPTR_T_DEFINED */ + +#ifndef _UINTPTR_T_DEFINED +#define _UINTPTR_T_DEFINED +#ifdef _WIN64 +typedef unsigned long long uintptr_t; +#else /* _WIN64 */ +typedef _W64 unsigned int uintptr_t; +#endif /* _WIN64 */ +#endif /* _UINTPTR_T_DEFINED */ + +#define INT8_MIN (-127i8 - 1) +#define INT16_MIN (-32767i16 - 1) +#define INT32_MIN (-2147483647i32 - 1) +#define INT64_MIN (-9223372036854775807i64 - 1) +#define INT8_MAX 127i8 +#define INT16_MAX 32767i16 +#define INT32_MAX 2147483647i32 +#define INT64_MAX 9223372036854775807i64 +#define UINT8_MAX 0xffui8 +#define UINT16_MAX 0xffffui16 +#define UINT32_MAX 0xffffffffui32 +#define UINT64_MAX 0xffffffffffffffffui64 + +#define INT_FAST8_MIN INT8_MIN +#define INT_FAST16_MIN INT32_MIN +#define INT_FAST32_MIN INT32_MIN +#define INT_FAST64_MIN INT64_MIN +#define INT_FAST8_MAX INT8_MAX +#define INT_FAST16_MAX INT32_MAX +#define INT_FAST32_MAX INT32_MAX +#define INT_FAST64_MAX INT64_MAX +#define UINT_FAST8_MAX UINT8_MAX +#define UINT_FAST16_MAX UINT32_MAX +#define UINT_FAST32_MAX UINT32_MAX +#define UINT_FAST64_MAX UINT64_MAX + +#ifdef _WIN64 +#define INTPTR_MIN INT64_MIN +#define INTPTR_MAX INT64_MAX +#define UINTPTR_MAX UINT64_MAX +#else /* _WIN64 */ +#define INTPTR_MIN INT32_MIN +#define INTPTR_MAX INT32_MAX +#define UINTPTR_MAX UINT32_MAX +#endif /* _WIN64 */ + +#else // this system has stdint.h + +#if defined(_MSC_VER) && (_MSC_VER == MSC_VER_VS2010) +#define _INTPTR 2 +#endif + +#include +#endif // (defined(_MSC_VER) && (_MSC_VER < MSC_VER_VS2010)) || + // defined(_KERNEL_MODE) + +// handle inttypes.h compatibility +#if (defined(_MSC_VER) && (_MSC_VER < MSC_VER_VS2013)) || defined(_KERNEL_MODE) +// this system does not have inttypes.h + +#define __PRI_8_LENGTH_MODIFIER__ "hh" +#define __PRI_64_LENGTH_MODIFIER__ "ll" + +#define PRId8 __PRI_8_LENGTH_MODIFIER__ "d" +#define PRIi8 __PRI_8_LENGTH_MODIFIER__ "i" +#define PRIo8 __PRI_8_LENGTH_MODIFIER__ "o" +#define PRIu8 __PRI_8_LENGTH_MODIFIER__ "u" +#define PRIx8 __PRI_8_LENGTH_MODIFIER__ "x" +#define PRIX8 __PRI_8_LENGTH_MODIFIER__ "X" + +#define PRId16 "hd" +#define PRIi16 "hi" +#define PRIo16 "ho" +#define PRIu16 "hu" +#define PRIx16 "hx" +#define PRIX16 "hX" + +#if defined(_MSC_VER) && (_MSC_VER <= MSC_VER_VS2012) +#define PRId32 "ld" +#define PRIi32 "li" +#define PRIo32 "lo" +#define PRIu32 "lu" +#define PRIx32 "lx" +#define PRIX32 "lX" +#else // OSX +#define PRId32 "d" +#define PRIi32 "i" +#define PRIo32 "o" +#define PRIu32 "u" +#define PRIx32 "x" +#define PRIX32 "X" +#endif // defined(_MSC_VER) && (_MSC_VER <= MSC_VER_VS2012) + +#if defined(_MSC_VER) && (_MSC_VER <= MSC_VER_VS2012) +// redefine functions from inttypes.h used in cstool +#define strtoull _strtoui64 +#endif + +#define PRId64 __PRI_64_LENGTH_MODIFIER__ "d" +#define PRIi64 __PRI_64_LENGTH_MODIFIER__ "i" +#define PRIo64 __PRI_64_LENGTH_MODIFIER__ "o" +#define PRIu64 __PRI_64_LENGTH_MODIFIER__ "u" +#define PRIx64 __PRI_64_LENGTH_MODIFIER__ "x" +#define PRIX64 __PRI_64_LENGTH_MODIFIER__ "X" + +#else +// this system has inttypes.h by default +#include +#endif // #if defined(_MSC_VER) && (_MSC_VER < MSC_VER_VS2013) || + // defined(_KERNEL_MODE) + +// sys/time.h compatibility +#if defined(_MSC_VER) +#include +#include +#include + +#else +#include +#endif + +// unistd.h compatibility +#if defined(_MSC_VER) + +static int usleep(uint32_t usec) +{ + HANDLE timer; + LARGE_INTEGER due; + + timer = CreateWaitableTimer(NULL, TRUE, NULL); + if (!timer) + return -1; + + due.QuadPart = (-((int64_t)usec)) * 10LL; + if (!SetWaitableTimer(timer, &due, 0, NULL, NULL, 0)) { + CloseHandle(timer); + return -1; + } + WaitForSingleObject(timer, INFINITE); + CloseHandle(timer); + + return 0; +} + +#else +#include +#endif + +// misc support +#if defined(_MSC_VER) +#ifdef _WIN64 +typedef signed __int64 ssize_t; +#else +typedef _W64 signed int ssize_t; +#endif + +#ifndef va_copy +#define va_copy(d, s) ((d) = (s)) +#endif +#define strcasecmp _stricmp +#if (_MSC_VER < MSC_VER_VS2015) +#define snprintf _snprintf +#endif +#if (_MSC_VER <= MSC_VER_VS2013) +#define strtoll _strtoi64 +#endif +#endif + +#endif // UNICORN_PLATFORM_H diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/ppc.h b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/ppc.h new file mode 100644 index 0000000000..f421e5e683 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/ppc.h @@ -0,0 +1,434 @@ +/* Unicorn Engine */ +/* By Nguyen Anh Quynh , 2015-2017 */ +/* This file is released under LGPL2. + See COPYING.LGPL2 in root directory for more details +*/ + +#ifndef UNICORN_PPC_H +#define UNICORN_PPC_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _MSC_VER +#pragma warning(disable : 4201) +#endif + +//> PPC CPU +typedef enum uc_cpu_ppc { + UC_CPU_PPC32_401 = 0, + UC_CPU_PPC32_401A1, + UC_CPU_PPC32_401B2, + UC_CPU_PPC32_401C2, + UC_CPU_PPC32_401D2, + UC_CPU_PPC32_401E2, + UC_CPU_PPC32_401F2, + UC_CPU_PPC32_401G2, + UC_CPU_PPC32_IOP480, + UC_CPU_PPC32_COBRA, + UC_CPU_PPC32_403GA, + UC_CPU_PPC32_403GB, + UC_CPU_PPC32_403GC, + UC_CPU_PPC32_403GCX, + UC_CPU_PPC32_405D2, + UC_CPU_PPC32_405D4, + UC_CPU_PPC32_405CRA, + UC_CPU_PPC32_405CRB, + UC_CPU_PPC32_405CRC, + UC_CPU_PPC32_405EP, + UC_CPU_PPC32_405EZ, + UC_CPU_PPC32_405GPA, + UC_CPU_PPC32_405GPB, + UC_CPU_PPC32_405GPC, + UC_CPU_PPC32_405GPD, + UC_CPU_PPC32_405GPR, + UC_CPU_PPC32_405LP, + UC_CPU_PPC32_NPE405H, + UC_CPU_PPC32_NPE405H2, + UC_CPU_PPC32_NPE405L, + UC_CPU_PPC32_NPE4GS3, + UC_CPU_PPC32_STB03, + UC_CPU_PPC32_STB04, + UC_CPU_PPC32_STB25, + UC_CPU_PPC32_X2VP4, + UC_CPU_PPC32_X2VP20, + UC_CPU_PPC32_440_XILINX, + UC_CPU_PPC32_440_XILINX_W_DFPU, + UC_CPU_PPC32_440EPA, + UC_CPU_PPC32_440EPB, + UC_CPU_PPC32_440EPX, + UC_CPU_PPC32_460EXB, + UC_CPU_PPC32_G2, + UC_CPU_PPC32_G2H4, + UC_CPU_PPC32_G2GP, + UC_CPU_PPC32_G2LS, + UC_CPU_PPC32_G2HIP3, + UC_CPU_PPC32_G2HIP4, + UC_CPU_PPC32_MPC603, + UC_CPU_PPC32_G2LE, + UC_CPU_PPC32_G2LEGP, + UC_CPU_PPC32_G2LELS, + UC_CPU_PPC32_G2LEGP1, + UC_CPU_PPC32_G2LEGP3, + UC_CPU_PPC32_MPC5200_V10, + UC_CPU_PPC32_MPC5200_V11, + UC_CPU_PPC32_MPC5200_V12, + UC_CPU_PPC32_MPC5200B_V20, + UC_CPU_PPC32_MPC5200B_V21, + UC_CPU_PPC32_E200Z5, + UC_CPU_PPC32_E200Z6, + UC_CPU_PPC32_E300C1, + UC_CPU_PPC32_E300C2, + UC_CPU_PPC32_E300C3, + UC_CPU_PPC32_E300C4, + UC_CPU_PPC32_MPC8343, + UC_CPU_PPC32_MPC8343A, + UC_CPU_PPC32_MPC8343E, + UC_CPU_PPC32_MPC8343EA, + UC_CPU_PPC32_MPC8347T, + UC_CPU_PPC32_MPC8347P, + UC_CPU_PPC32_MPC8347AT, + UC_CPU_PPC32_MPC8347AP, + UC_CPU_PPC32_MPC8347ET, + UC_CPU_PPC32_MPC8347EP, + UC_CPU_PPC32_MPC8347EAT, + UC_CPU_PPC32_MPC8347EAP, + UC_CPU_PPC32_MPC8349, + UC_CPU_PPC32_MPC8349A, + UC_CPU_PPC32_MPC8349E, + UC_CPU_PPC32_MPC8349EA, + UC_CPU_PPC32_MPC8377, + UC_CPU_PPC32_MPC8377E, + UC_CPU_PPC32_MPC8378, + UC_CPU_PPC32_MPC8378E, + UC_CPU_PPC32_MPC8379, + UC_CPU_PPC32_MPC8379E, + UC_CPU_PPC32_E500_V10, + UC_CPU_PPC32_E500_V20, + UC_CPU_PPC32_E500V2_V10, + UC_CPU_PPC32_E500V2_V20, + UC_CPU_PPC32_E500V2_V21, + UC_CPU_PPC32_E500V2_V22, + UC_CPU_PPC32_E500V2_V30, + UC_CPU_PPC32_E500MC, + UC_CPU_PPC32_MPC8533_V10, + UC_CPU_PPC32_MPC8533_V11, + UC_CPU_PPC32_MPC8533E_V10, + UC_CPU_PPC32_MPC8533E_V11, + UC_CPU_PPC32_MPC8540_V10, + UC_CPU_PPC32_MPC8540_V20, + UC_CPU_PPC32_MPC8540_V21, + UC_CPU_PPC32_MPC8541_V10, + UC_CPU_PPC32_MPC8541_V11, + UC_CPU_PPC32_MPC8541E_V10, + UC_CPU_PPC32_MPC8541E_V11, + UC_CPU_PPC32_MPC8543_V10, + UC_CPU_PPC32_MPC8543_V11, + UC_CPU_PPC32_MPC8543_V20, + UC_CPU_PPC32_MPC8543_V21, + UC_CPU_PPC32_MPC8543E_V10, + UC_CPU_PPC32_MPC8543E_V11, + UC_CPU_PPC32_MPC8543E_V20, + UC_CPU_PPC32_MPC8543E_V21, + UC_CPU_PPC32_MPC8544_V10, + UC_CPU_PPC32_MPC8544_V11, + UC_CPU_PPC32_MPC8544E_V10, + UC_CPU_PPC32_MPC8544E_V11, + UC_CPU_PPC32_MPC8545_V20, + UC_CPU_PPC32_MPC8545_V21, + UC_CPU_PPC32_MPC8545E_V20, + UC_CPU_PPC32_MPC8545E_V21, + UC_CPU_PPC32_MPC8547E_V20, + UC_CPU_PPC32_MPC8547E_V21, + UC_CPU_PPC32_MPC8548_V10, + UC_CPU_PPC32_MPC8548_V11, + UC_CPU_PPC32_MPC8548_V20, + UC_CPU_PPC32_MPC8548_V21, + UC_CPU_PPC32_MPC8548E_V10, + UC_CPU_PPC32_MPC8548E_V11, + UC_CPU_PPC32_MPC8548E_V20, + UC_CPU_PPC32_MPC8548E_V21, + UC_CPU_PPC32_MPC8555_V10, + UC_CPU_PPC32_MPC8555_V11, + UC_CPU_PPC32_MPC8555E_V10, + UC_CPU_PPC32_MPC8555E_V11, + UC_CPU_PPC32_MPC8560_V10, + UC_CPU_PPC32_MPC8560_V20, + UC_CPU_PPC32_MPC8560_V21, + UC_CPU_PPC32_MPC8567, + UC_CPU_PPC32_MPC8567E, + UC_CPU_PPC32_MPC8568, + UC_CPU_PPC32_MPC8568E, + UC_CPU_PPC32_MPC8572, + UC_CPU_PPC32_MPC8572E, + UC_CPU_PPC32_E600, + UC_CPU_PPC32_MPC8610, + UC_CPU_PPC32_MPC8641, + UC_CPU_PPC32_MPC8641D, + UC_CPU_PPC32_601_V0, + UC_CPU_PPC32_601_V1, + UC_CPU_PPC32_601_V2, + UC_CPU_PPC32_602, + UC_CPU_PPC32_603, + UC_CPU_PPC32_603E_V1_1, + UC_CPU_PPC32_603E_V1_2, + UC_CPU_PPC32_603E_V1_3, + UC_CPU_PPC32_603E_V1_4, + UC_CPU_PPC32_603E_V2_2, + UC_CPU_PPC32_603E_V3, + UC_CPU_PPC32_603E_V4, + UC_CPU_PPC32_603E_V4_1, + UC_CPU_PPC32_603E7, + UC_CPU_PPC32_603E7T, + UC_CPU_PPC32_603E7V, + UC_CPU_PPC32_603E7V1, + UC_CPU_PPC32_603E7V2, + UC_CPU_PPC32_603P, + UC_CPU_PPC32_604, + UC_CPU_PPC32_604E_V1_0, + UC_CPU_PPC32_604E_V2_2, + UC_CPU_PPC32_604E_V2_4, + UC_CPU_PPC32_604R, + UC_CPU_PPC32_740_V1_0, + UC_CPU_PPC32_750_V1_0, + UC_CPU_PPC32_740_V2_0, + UC_CPU_PPC32_750_V2_0, + UC_CPU_PPC32_740_V2_1, + UC_CPU_PPC32_750_V2_1, + UC_CPU_PPC32_740_V2_2, + UC_CPU_PPC32_750_V2_2, + UC_CPU_PPC32_740_V3_0, + UC_CPU_PPC32_750_V3_0, + UC_CPU_PPC32_740_V3_1, + UC_CPU_PPC32_750_V3_1, + UC_CPU_PPC32_740E, + UC_CPU_PPC32_750E, + UC_CPU_PPC32_740P, + UC_CPU_PPC32_750P, + UC_CPU_PPC32_750CL_V1_0, + UC_CPU_PPC32_750CL_V2_0, + UC_CPU_PPC32_750CX_V1_0, + UC_CPU_PPC32_750CX_V2_0, + UC_CPU_PPC32_750CX_V2_1, + UC_CPU_PPC32_750CX_V2_2, + UC_CPU_PPC32_750CXE_V2_1, + UC_CPU_PPC32_750CXE_V2_2, + UC_CPU_PPC32_750CXE_V2_3, + UC_CPU_PPC32_750CXE_V2_4, + UC_CPU_PPC32_750CXE_V2_4B, + UC_CPU_PPC32_750CXE_V3_0, + UC_CPU_PPC32_750CXE_V3_1, + UC_CPU_PPC32_750CXE_V3_1B, + UC_CPU_PPC32_750CXR, + UC_CPU_PPC32_750FL, + UC_CPU_PPC32_750FX_V1_0, + UC_CPU_PPC32_750FX_V2_0, + UC_CPU_PPC32_750FX_V2_1, + UC_CPU_PPC32_750FX_V2_2, + UC_CPU_PPC32_750FX_V2_3, + UC_CPU_PPC32_750GL, + UC_CPU_PPC32_750GX_V1_0, + UC_CPU_PPC32_750GX_V1_1, + UC_CPU_PPC32_750GX_V1_2, + UC_CPU_PPC32_750L_V2_0, + UC_CPU_PPC32_750L_V2_1, + UC_CPU_PPC32_750L_V2_2, + UC_CPU_PPC32_750L_V3_0, + UC_CPU_PPC32_750L_V3_2, + UC_CPU_PPC32_745_V1_0, + UC_CPU_PPC32_755_V1_0, + UC_CPU_PPC32_745_V1_1, + UC_CPU_PPC32_755_V1_1, + UC_CPU_PPC32_745_V2_0, + UC_CPU_PPC32_755_V2_0, + UC_CPU_PPC32_745_V2_1, + UC_CPU_PPC32_755_V2_1, + UC_CPU_PPC32_745_V2_2, + UC_CPU_PPC32_755_V2_2, + UC_CPU_PPC32_745_V2_3, + UC_CPU_PPC32_755_V2_3, + UC_CPU_PPC32_745_V2_4, + UC_CPU_PPC32_755_V2_4, + UC_CPU_PPC32_745_V2_5, + UC_CPU_PPC32_755_V2_5, + UC_CPU_PPC32_745_V2_6, + UC_CPU_PPC32_755_V2_6, + UC_CPU_PPC32_745_V2_7, + UC_CPU_PPC32_755_V2_7, + UC_CPU_PPC32_745_V2_8, + UC_CPU_PPC32_755_V2_8, + UC_CPU_PPC32_7400_V1_0, + UC_CPU_PPC32_7400_V1_1, + UC_CPU_PPC32_7400_V2_0, + UC_CPU_PPC32_7400_V2_1, + UC_CPU_PPC32_7400_V2_2, + UC_CPU_PPC32_7400_V2_6, + UC_CPU_PPC32_7400_V2_7, + UC_CPU_PPC32_7400_V2_8, + UC_CPU_PPC32_7400_V2_9, + UC_CPU_PPC32_7410_V1_0, + UC_CPU_PPC32_7410_V1_1, + UC_CPU_PPC32_7410_V1_2, + UC_CPU_PPC32_7410_V1_3, + UC_CPU_PPC32_7410_V1_4, + UC_CPU_PPC32_7448_V1_0, + UC_CPU_PPC32_7448_V1_1, + UC_CPU_PPC32_7448_V2_0, + UC_CPU_PPC32_7448_V2_1, + UC_CPU_PPC32_7450_V1_0, + UC_CPU_PPC32_7450_V1_1, + UC_CPU_PPC32_7450_V1_2, + UC_CPU_PPC32_7450_V2_0, + UC_CPU_PPC32_7450_V2_1, + UC_CPU_PPC32_7441_V2_1, + UC_CPU_PPC32_7441_V2_3, + UC_CPU_PPC32_7451_V2_3, + UC_CPU_PPC32_7441_V2_10, + UC_CPU_PPC32_7451_V2_10, + UC_CPU_PPC32_7445_V1_0, + UC_CPU_PPC32_7455_V1_0, + UC_CPU_PPC32_7445_V2_1, + UC_CPU_PPC32_7455_V2_1, + UC_CPU_PPC32_7445_V3_2, + UC_CPU_PPC32_7455_V3_2, + UC_CPU_PPC32_7445_V3_3, + UC_CPU_PPC32_7455_V3_3, + UC_CPU_PPC32_7445_V3_4, + UC_CPU_PPC32_7455_V3_4, + UC_CPU_PPC32_7447_V1_0, + UC_CPU_PPC32_7457_V1_0, + UC_CPU_PPC32_7447_V1_1, + UC_CPU_PPC32_7457_V1_1, + UC_CPU_PPC32_7457_V1_2, + UC_CPU_PPC32_7447A_V1_0, + UC_CPU_PPC32_7457A_V1_0, + UC_CPU_PPC32_7447A_V1_1, + UC_CPU_PPC32_7457A_V1_1, + UC_CPU_PPC32_7447A_V1_2, + UC_CPU_PPC32_7457A_V1_2, + + UC_CPU_PPC32_ENDING +} uc_cpu_ppc; + +//> PPC64 CPU +typedef enum uc_cpu_ppc64 { + UC_CPU_PPC64_E5500 = 0, + UC_CPU_PPC64_E6500, + UC_CPU_PPC64_970_V2_2, + UC_CPU_PPC64_970FX_V1_0, + UC_CPU_PPC64_970FX_V2_0, + UC_CPU_PPC64_970FX_V2_1, + UC_CPU_PPC64_970FX_V3_0, + UC_CPU_PPC64_970FX_V3_1, + UC_CPU_PPC64_970MP_V1_0, + UC_CPU_PPC64_970MP_V1_1, + UC_CPU_PPC64_POWER5_V2_1, + UC_CPU_PPC64_POWER7_V2_3, + UC_CPU_PPC64_POWER7_V2_1, + UC_CPU_PPC64_POWER8E_V2_1, + UC_CPU_PPC64_POWER8_V2_0, + UC_CPU_PPC64_POWER8NVL_V1_0, + UC_CPU_PPC64_POWER9_V1_0, + UC_CPU_PPC64_POWER9_V2_0, + UC_CPU_PPC64_POWER10_V1_0, + + UC_CPU_PPC64_ENDING +} uc_cpu_ppc64; + +//> PPC registers +typedef enum uc_ppc_reg { + UC_PPC_REG_INVALID = 0, + //> General purpose registers + UC_PPC_REG_PC, + + UC_PPC_REG_0, + UC_PPC_REG_1, + UC_PPC_REG_2, + UC_PPC_REG_3, + UC_PPC_REG_4, + UC_PPC_REG_5, + UC_PPC_REG_6, + UC_PPC_REG_7, + UC_PPC_REG_8, + UC_PPC_REG_9, + UC_PPC_REG_10, + UC_PPC_REG_11, + UC_PPC_REG_12, + UC_PPC_REG_13, + UC_PPC_REG_14, + UC_PPC_REG_15, + UC_PPC_REG_16, + UC_PPC_REG_17, + UC_PPC_REG_18, + UC_PPC_REG_19, + UC_PPC_REG_20, + UC_PPC_REG_21, + UC_PPC_REG_22, + UC_PPC_REG_23, + UC_PPC_REG_24, + UC_PPC_REG_25, + UC_PPC_REG_26, + UC_PPC_REG_27, + UC_PPC_REG_28, + UC_PPC_REG_29, + UC_PPC_REG_30, + UC_PPC_REG_31, + + UC_PPC_REG_CR0, + UC_PPC_REG_CR1, + UC_PPC_REG_CR2, + UC_PPC_REG_CR3, + UC_PPC_REG_CR4, + UC_PPC_REG_CR5, + UC_PPC_REG_CR6, + UC_PPC_REG_CR7, + + UC_PPC_REG_FPR0, + UC_PPC_REG_FPR1, + UC_PPC_REG_FPR2, + UC_PPC_REG_FPR3, + UC_PPC_REG_FPR4, + UC_PPC_REG_FPR5, + UC_PPC_REG_FPR6, + UC_PPC_REG_FPR7, + UC_PPC_REG_FPR8, + UC_PPC_REG_FPR9, + UC_PPC_REG_FPR10, + UC_PPC_REG_FPR11, + UC_PPC_REG_FPR12, + UC_PPC_REG_FPR13, + UC_PPC_REG_FPR14, + UC_PPC_REG_FPR15, + UC_PPC_REG_FPR16, + UC_PPC_REG_FPR17, + UC_PPC_REG_FPR18, + UC_PPC_REG_FPR19, + UC_PPC_REG_FPR20, + UC_PPC_REG_FPR21, + UC_PPC_REG_FPR22, + UC_PPC_REG_FPR23, + UC_PPC_REG_FPR24, + UC_PPC_REG_FPR25, + UC_PPC_REG_FPR26, + UC_PPC_REG_FPR27, + UC_PPC_REG_FPR28, + UC_PPC_REG_FPR29, + UC_PPC_REG_FPR30, + UC_PPC_REG_FPR31, + + UC_PPC_REG_LR, + UC_PPC_REG_XER, + UC_PPC_REG_CTR, + UC_PPC_REG_MSR, + UC_PPC_REG_FPSCR, + UC_PPC_REG_CR, + + UC_PPC_REG_ENDING, // <-- mark the end of the list or registers +} uc_ppc_reg; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/rh850.h b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/rh850.h new file mode 100644 index 0000000000..676d295ab7 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/rh850.h @@ -0,0 +1,111 @@ +/* Unicorn Engine */ +/* By Damien Cauquil , 2023 */ + +#ifndef UNICORN_RH850_H +#define UNICORN_RH850_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _MSC_VER +#pragma warning(disable : 4201) +#endif + +#define UC_RH850_SYSREG_SELID0 32 +#define UC_RH850_SYSREG_SELID1 64 +#define UC_RH850_SYSREG_SELID2 96 +#define UC_RH850_SYSREG_SELID3 128 +#define UC_RH850_SYSREG_SELID4 160 +#define UC_RH850_SYSREG_SELID5 192 +#define UC_RH850_SYSREG_SELID6 224 +#define UC_RH850_SYSREG_SELID7 256 + +//> RH850 global purpose registers +typedef enum uc_rh850_reg { + UC_RH850_REG_R0 = 0, + UC_RH850_REG_R1, + UC_RH850_REG_R2, + UC_RH850_REG_R3, + UC_RH850_REG_R4, + UC_RH850_REG_R5, + UC_RH850_REG_R6, + UC_RH850_REG_R7, + UC_RH850_REG_R8, + UC_RH850_REG_R9, + UC_RH850_REG_R10, + UC_RH850_REG_R11, + UC_RH850_REG_R12, + UC_RH850_REG_R13, + UC_RH850_REG_R14, + UC_RH850_REG_R15, + UC_RH850_REG_R16, + UC_RH850_REG_R17, + UC_RH850_REG_R18, + UC_RH850_REG_R19, + UC_RH850_REG_R20, + UC_RH850_REG_R21, + UC_RH850_REG_R22, + UC_RH850_REG_R23, + UC_RH850_REG_R24, + UC_RH850_REG_R25, + UC_RH850_REG_R26, + UC_RH850_REG_R27, + UC_RH850_REG_R28, + UC_RH850_REG_R29, + UC_RH850_REG_R30, + UC_RH850_REG_R31, + + //> RH850 system registers, selection ID 0 + UC_RH850_REG_EIPC = UC_RH850_SYSREG_SELID0, + UC_RH850_REG_EIPSW, + UC_RH850_REG_FEPC, + UC_RH850_REG_FEPSW, + UC_RH850_REG_ECR, + UC_RH850_REG_PSW, + UC_RH850_REG_FPSR, + UC_RH850_REG_FPEPC, + UC_RH850_REG_FPST, + UC_RH850_REG_FPCC, + UC_RH850_REG_FPCFG, + UC_RH850_REG_FPEC, + UC_RH850_REG_EIIC = UC_RH850_SYSREG_SELID0 + 13, + UC_RH850_REG_FEIC, + UC_RH850_REG_CTPC = UC_RH850_SYSREG_SELID0 + 16, + UC_RH850_REG_CTPSW, + UC_RH850_REG_CTBP = UC_RH850_SYSREG_SELID0 + 20, + UC_RH850_REG_EIWR = UC_RH850_SYSREG_SELID0 + 28, + UC_RH850_REG_FEWR = UC_RH850_SYSREG_SELID0 + 29, + UC_RH850_REG_BSEL = UC_RH850_SYSREG_SELID0 + 31, + + //> RH850 system regusters, selection ID 1 + UC_RH850_REG_MCFG0 = UC_RH850_SYSREG_SELID1, + UC_RH850_REG_RBASE, + UC_RH850_REG_EBASE, + UC_RH850_REG_INTBP, + UC_RH850_REG_MCTL, + UC_RH850_REG_PID, + UC_RH850_REG_SCCFG = UC_RH850_SYSREG_SELID1 + 11, + UC_RH850_REG_SCBP, + + //> RH850 system registers, selection ID 2 + UC_RH850_REG_HTCFG0 = UC_RH850_SYSREG_SELID2, + UC_RH850_REG_MEA = UC_RH850_SYSREG_SELID2 + 6, + UC_RH850_REG_ASID, + UC_RH850_REG_MEI, + + UC_RH850_REG_PC = UC_RH850_SYSREG_SELID7 + 32, + UC_RH850_REG_ENDING +} uc_cpu_rh850; + +//> RH8509 Registers aliases. +#define UC_RH850_REG_ZERO UC_RH850_REG_R0 +#define UC_RH850_REG_SP UC_RH850_REG_R2 +#define UC_RH850_REG_EP UC_RH850_REG_R30 +#define UC_RH850_REG_LP UC_RH850_REG_R31 + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/riscv.h b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/riscv.h new file mode 100644 index 0000000000..c4527a4b75 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/riscv.h @@ -0,0 +1,314 @@ +/* Unicorn Engine */ +/* By Nguyen Anh Quynh , 2015-2020 */ +/* This file is released under LGPL2. + See COPYING.LGPL2 in root directory for more details + */ + +#ifndef UNICORN_RISCV_H +#define UNICORN_RISCV_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _MSC_VER +#pragma warning(disable : 4201) +#endif + +//> RISCV32 CPU +typedef enum uc_cpu_riscv32 { + UC_CPU_RISCV32_ANY = 0, + UC_CPU_RISCV32_BASE32, + UC_CPU_RISCV32_SIFIVE_E31, + UC_CPU_RISCV32_SIFIVE_U34, + + UC_CPU_RISCV32_ENDING +} uc_cpu_riscv32; + +//> RISCV64 CPU +typedef enum uc_cpu_riscv64 { + UC_CPU_RISCV64_ANY = 0, + UC_CPU_RISCV64_BASE64, + UC_CPU_RISCV64_SIFIVE_E51, + UC_CPU_RISCV64_SIFIVE_U54, + + UC_CPU_RISCV64_ENDING +} uc_cpu_riscv64; + +//> RISCV registers +typedef enum uc_riscv_reg { + UC_RISCV_REG_INVALID = 0, + //> General purpose registers + UC_RISCV_REG_X0, + UC_RISCV_REG_X1, + UC_RISCV_REG_X2, + UC_RISCV_REG_X3, + UC_RISCV_REG_X4, + UC_RISCV_REG_X5, + UC_RISCV_REG_X6, + UC_RISCV_REG_X7, + UC_RISCV_REG_X8, + UC_RISCV_REG_X9, + UC_RISCV_REG_X10, + UC_RISCV_REG_X11, + UC_RISCV_REG_X12, + UC_RISCV_REG_X13, + UC_RISCV_REG_X14, + UC_RISCV_REG_X15, + UC_RISCV_REG_X16, + UC_RISCV_REG_X17, + UC_RISCV_REG_X18, + UC_RISCV_REG_X19, + UC_RISCV_REG_X20, + UC_RISCV_REG_X21, + UC_RISCV_REG_X22, + UC_RISCV_REG_X23, + UC_RISCV_REG_X24, + UC_RISCV_REG_X25, + UC_RISCV_REG_X26, + UC_RISCV_REG_X27, + UC_RISCV_REG_X28, + UC_RISCV_REG_X29, + UC_RISCV_REG_X30, + UC_RISCV_REG_X31, + + //> RISCV CSR + UC_RISCV_REG_USTATUS, + UC_RISCV_REG_UIE, + UC_RISCV_REG_UTVEC, + UC_RISCV_REG_USCRATCH, + UC_RISCV_REG_UEPC, + UC_RISCV_REG_UCAUSE, + UC_RISCV_REG_UTVAL, + UC_RISCV_REG_UIP, + UC_RISCV_REG_FFLAGS, + UC_RISCV_REG_FRM, + UC_RISCV_REG_FCSR, + UC_RISCV_REG_CYCLE, + UC_RISCV_REG_TIME, + UC_RISCV_REG_INSTRET, + UC_RISCV_REG_HPMCOUNTER3, + UC_RISCV_REG_HPMCOUNTER4, + UC_RISCV_REG_HPMCOUNTER5, + UC_RISCV_REG_HPMCOUNTER6, + UC_RISCV_REG_HPMCOUNTER7, + UC_RISCV_REG_HPMCOUNTER8, + UC_RISCV_REG_HPMCOUNTER9, + UC_RISCV_REG_HPMCOUNTER10, + UC_RISCV_REG_HPMCOUNTER11, + UC_RISCV_REG_HPMCOUNTER12, + UC_RISCV_REG_HPMCOUNTER13, + UC_RISCV_REG_HPMCOUNTER14, + UC_RISCV_REG_HPMCOUNTER15, + UC_RISCV_REG_HPMCOUNTER16, + UC_RISCV_REG_HPMCOUNTER17, + UC_RISCV_REG_HPMCOUNTER18, + UC_RISCV_REG_HPMCOUNTER19, + UC_RISCV_REG_HPMCOUNTER20, + UC_RISCV_REG_HPMCOUNTER21, + UC_RISCV_REG_HPMCOUNTER22, + UC_RISCV_REG_HPMCOUNTER23, + UC_RISCV_REG_HPMCOUNTER24, + UC_RISCV_REG_HPMCOUNTER25, + UC_RISCV_REG_HPMCOUNTER26, + UC_RISCV_REG_HPMCOUNTER27, + UC_RISCV_REG_HPMCOUNTER28, + UC_RISCV_REG_HPMCOUNTER29, + UC_RISCV_REG_HPMCOUNTER30, + UC_RISCV_REG_HPMCOUNTER31, + UC_RISCV_REG_CYCLEH, + UC_RISCV_REG_TIMEH, + UC_RISCV_REG_INSTRETH, + UC_RISCV_REG_HPMCOUNTER3H, + UC_RISCV_REG_HPMCOUNTER4H, + UC_RISCV_REG_HPMCOUNTER5H, + UC_RISCV_REG_HPMCOUNTER6H, + UC_RISCV_REG_HPMCOUNTER7H, + UC_RISCV_REG_HPMCOUNTER8H, + UC_RISCV_REG_HPMCOUNTER9H, + UC_RISCV_REG_HPMCOUNTER10H, + UC_RISCV_REG_HPMCOUNTER11H, + UC_RISCV_REG_HPMCOUNTER12H, + UC_RISCV_REG_HPMCOUNTER13H, + UC_RISCV_REG_HPMCOUNTER14H, + UC_RISCV_REG_HPMCOUNTER15H, + UC_RISCV_REG_HPMCOUNTER16H, + UC_RISCV_REG_HPMCOUNTER17H, + UC_RISCV_REG_HPMCOUNTER18H, + UC_RISCV_REG_HPMCOUNTER19H, + UC_RISCV_REG_HPMCOUNTER20H, + UC_RISCV_REG_HPMCOUNTER21H, + UC_RISCV_REG_HPMCOUNTER22H, + UC_RISCV_REG_HPMCOUNTER23H, + UC_RISCV_REG_HPMCOUNTER24H, + UC_RISCV_REG_HPMCOUNTER25H, + UC_RISCV_REG_HPMCOUNTER26H, + UC_RISCV_REG_HPMCOUNTER27H, + UC_RISCV_REG_HPMCOUNTER28H, + UC_RISCV_REG_HPMCOUNTER29H, + UC_RISCV_REG_HPMCOUNTER30H, + UC_RISCV_REG_HPMCOUNTER31H, + UC_RISCV_REG_MCYCLE, + UC_RISCV_REG_MINSTRET, + UC_RISCV_REG_MCYCLEH, + UC_RISCV_REG_MINSTRETH, + UC_RISCV_REG_MVENDORID, + UC_RISCV_REG_MARCHID, + UC_RISCV_REG_MIMPID, + UC_RISCV_REG_MHARTID, + UC_RISCV_REG_MSTATUS, + UC_RISCV_REG_MISA, + UC_RISCV_REG_MEDELEG, + UC_RISCV_REG_MIDELEG, + UC_RISCV_REG_MIE, + UC_RISCV_REG_MTVEC, + UC_RISCV_REG_MCOUNTEREN, + UC_RISCV_REG_MSTATUSH, + UC_RISCV_REG_MUCOUNTEREN, + UC_RISCV_REG_MSCOUNTEREN, + UC_RISCV_REG_MHCOUNTEREN, + UC_RISCV_REG_MSCRATCH, + UC_RISCV_REG_MEPC, + UC_RISCV_REG_MCAUSE, + UC_RISCV_REG_MTVAL, + UC_RISCV_REG_MIP, + UC_RISCV_REG_MBADADDR, + UC_RISCV_REG_SSTATUS, + UC_RISCV_REG_SEDELEG, + UC_RISCV_REG_SIDELEG, + UC_RISCV_REG_SIE, + UC_RISCV_REG_STVEC, + UC_RISCV_REG_SCOUNTEREN, + UC_RISCV_REG_SSCRATCH, + UC_RISCV_REG_SEPC, + UC_RISCV_REG_SCAUSE, + UC_RISCV_REG_STVAL, + UC_RISCV_REG_SIP, + UC_RISCV_REG_SBADADDR, + UC_RISCV_REG_SPTBR, + UC_RISCV_REG_SATP, + UC_RISCV_REG_HSTATUS, + UC_RISCV_REG_HEDELEG, + UC_RISCV_REG_HIDELEG, + UC_RISCV_REG_HIE, + UC_RISCV_REG_HCOUNTEREN, + UC_RISCV_REG_HTVAL, + UC_RISCV_REG_HIP, + UC_RISCV_REG_HTINST, + UC_RISCV_REG_HGATP, + UC_RISCV_REG_HTIMEDELTA, + UC_RISCV_REG_HTIMEDELTAH, + + //> Floating-point registers + UC_RISCV_REG_F0, // "ft0" + UC_RISCV_REG_F1, // "ft1" + UC_RISCV_REG_F2, // "ft2" + UC_RISCV_REG_F3, // "ft3" + UC_RISCV_REG_F4, // "ft4" + UC_RISCV_REG_F5, // "ft5" + UC_RISCV_REG_F6, // "ft6" + UC_RISCV_REG_F7, // "ft7" + UC_RISCV_REG_F8, // "fs0" + UC_RISCV_REG_F9, // "fs1" + UC_RISCV_REG_F10, // "fa0" + UC_RISCV_REG_F11, // "fa1" + UC_RISCV_REG_F12, // "fa2" + UC_RISCV_REG_F13, // "fa3" + UC_RISCV_REG_F14, // "fa4" + UC_RISCV_REG_F15, // "fa5" + UC_RISCV_REG_F16, // "fa6" + UC_RISCV_REG_F17, // "fa7" + UC_RISCV_REG_F18, // "fs2" + UC_RISCV_REG_F19, // "fs3" + UC_RISCV_REG_F20, // "fs4" + UC_RISCV_REG_F21, // "fs5" + UC_RISCV_REG_F22, // "fs6" + UC_RISCV_REG_F23, // "fs7" + UC_RISCV_REG_F24, // "fs8" + UC_RISCV_REG_F25, // "fs9" + UC_RISCV_REG_F26, // "fs10" + UC_RISCV_REG_F27, // "fs11" + UC_RISCV_REG_F28, // "ft8" + UC_RISCV_REG_F29, // "ft9" + UC_RISCV_REG_F30, // "ft10" + UC_RISCV_REG_F31, // "ft11" + + UC_RISCV_REG_PC, // PC register + + UC_RISCV_REG_ENDING, // <-- mark the end of the list or registers + + //> Alias registers + UC_RISCV_REG_ZERO = UC_RISCV_REG_X0, // "zero" + UC_RISCV_REG_RA = UC_RISCV_REG_X1, // "ra" + UC_RISCV_REG_SP = UC_RISCV_REG_X2, // "sp" + UC_RISCV_REG_GP = UC_RISCV_REG_X3, // "gp" + UC_RISCV_REG_TP = UC_RISCV_REG_X4, // "tp" + UC_RISCV_REG_T0 = UC_RISCV_REG_X5, // "t0" + UC_RISCV_REG_T1 = UC_RISCV_REG_X6, // "t1" + UC_RISCV_REG_T2 = UC_RISCV_REG_X7, // "t2" + UC_RISCV_REG_S0 = UC_RISCV_REG_X8, // "s0" + UC_RISCV_REG_FP = UC_RISCV_REG_X8, // "fp" + UC_RISCV_REG_S1 = UC_RISCV_REG_X9, // "s1" + UC_RISCV_REG_A0 = UC_RISCV_REG_X10, // "a0" + UC_RISCV_REG_A1 = UC_RISCV_REG_X11, // "a1" + UC_RISCV_REG_A2 = UC_RISCV_REG_X12, // "a2" + UC_RISCV_REG_A3 = UC_RISCV_REG_X13, // "a3" + UC_RISCV_REG_A4 = UC_RISCV_REG_X14, // "a4" + UC_RISCV_REG_A5 = UC_RISCV_REG_X15, // "a5" + UC_RISCV_REG_A6 = UC_RISCV_REG_X16, // "a6" + UC_RISCV_REG_A7 = UC_RISCV_REG_X17, // "a7" + UC_RISCV_REG_S2 = UC_RISCV_REG_X18, // "s2" + UC_RISCV_REG_S3 = UC_RISCV_REG_X19, // "s3" + UC_RISCV_REG_S4 = UC_RISCV_REG_X20, // "s4" + UC_RISCV_REG_S5 = UC_RISCV_REG_X21, // "s5" + UC_RISCV_REG_S6 = UC_RISCV_REG_X22, // "s6" + UC_RISCV_REG_S7 = UC_RISCV_REG_X23, // "s7" + UC_RISCV_REG_S8 = UC_RISCV_REG_X24, // "s8" + UC_RISCV_REG_S9 = UC_RISCV_REG_X25, // "s9" + UC_RISCV_REG_S10 = UC_RISCV_REG_X26, // "s10" + UC_RISCV_REG_S11 = UC_RISCV_REG_X27, // "s11" + UC_RISCV_REG_T3 = UC_RISCV_REG_X28, // "t3" + UC_RISCV_REG_T4 = UC_RISCV_REG_X29, // "t4" + UC_RISCV_REG_T5 = UC_RISCV_REG_X30, // "t5" + UC_RISCV_REG_T6 = UC_RISCV_REG_X31, // "t6" + + UC_RISCV_REG_FT0 = UC_RISCV_REG_F0, // "ft0" + UC_RISCV_REG_FT1 = UC_RISCV_REG_F1, // "ft1" + UC_RISCV_REG_FT2 = UC_RISCV_REG_F2, // "ft2" + UC_RISCV_REG_FT3 = UC_RISCV_REG_F3, // "ft3" + UC_RISCV_REG_FT4 = UC_RISCV_REG_F4, // "ft4" + UC_RISCV_REG_FT5 = UC_RISCV_REG_F5, // "ft5" + UC_RISCV_REG_FT6 = UC_RISCV_REG_F6, // "ft6" + UC_RISCV_REG_FT7 = UC_RISCV_REG_F7, // "ft7" + UC_RISCV_REG_FS0 = UC_RISCV_REG_F8, // "fs0" + UC_RISCV_REG_FS1 = UC_RISCV_REG_F9, // "fs1" + + UC_RISCV_REG_FA0 = UC_RISCV_REG_F10, // "fa0" + UC_RISCV_REG_FA1 = UC_RISCV_REG_F11, // "fa1" + UC_RISCV_REG_FA2 = UC_RISCV_REG_F12, // "fa2" + UC_RISCV_REG_FA3 = UC_RISCV_REG_F13, // "fa3" + UC_RISCV_REG_FA4 = UC_RISCV_REG_F14, // "fa4" + UC_RISCV_REG_FA5 = UC_RISCV_REG_F15, // "fa5" + UC_RISCV_REG_FA6 = UC_RISCV_REG_F16, // "fa6" + UC_RISCV_REG_FA7 = UC_RISCV_REG_F17, // "fa7" + UC_RISCV_REG_FS2 = UC_RISCV_REG_F18, // "fs2" + UC_RISCV_REG_FS3 = UC_RISCV_REG_F19, // "fs3" + UC_RISCV_REG_FS4 = UC_RISCV_REG_F20, // "fs4" + UC_RISCV_REG_FS5 = UC_RISCV_REG_F21, // "fs5" + UC_RISCV_REG_FS6 = UC_RISCV_REG_F22, // "fs6" + UC_RISCV_REG_FS7 = UC_RISCV_REG_F23, // "fs7" + UC_RISCV_REG_FS8 = UC_RISCV_REG_F24, // "fs8" + UC_RISCV_REG_FS9 = UC_RISCV_REG_F25, // "fs9" + UC_RISCV_REG_FS10 = UC_RISCV_REG_F26, // "fs10" + UC_RISCV_REG_FS11 = UC_RISCV_REG_F27, // "fs11" + UC_RISCV_REG_FT8 = UC_RISCV_REG_F28, // "ft8" + UC_RISCV_REG_FT9 = UC_RISCV_REG_F29, // "ft9" + UC_RISCV_REG_FT10 = UC_RISCV_REG_F30, // "ft10" + UC_RISCV_REG_FT11 = UC_RISCV_REG_F31, // "ft11" +} uc_riscv_reg; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/s390x.h b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/s390x.h new file mode 100644 index 0000000000..9d88ffa5e0 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/s390x.h @@ -0,0 +1,144 @@ +/* Unicorn Engine */ +/* By Nguyen Anh Quynh , 2015-2021 */ + +#ifndef UNICORN_S390X_H +#define UNICORN_S390X_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _MSC_VER +#pragma warning(disable : 4201) +#endif + +//> S390X CPU +typedef enum uc_cpu_s390x { + UC_CPU_S390X_Z900 = 0, + UC_CPU_S390X_Z900_2, + UC_CPU_S390X_Z900_3, + UC_CPU_S390X_Z800, + UC_CPU_S390X_Z990, + UC_CPU_S390X_Z990_2, + UC_CPU_S390X_Z990_3, + UC_CPU_S390X_Z890, + UC_CPU_S390X_Z990_4, + UC_CPU_S390X_Z890_2, + UC_CPU_S390X_Z990_5, + UC_CPU_S390X_Z890_3, + UC_CPU_S390X_Z9EC, + UC_CPU_S390X_Z9EC_2, + UC_CPU_S390X_Z9BC, + UC_CPU_S390X_Z9EC_3, + UC_CPU_S390X_Z9BC_2, + UC_CPU_S390X_Z10EC, + UC_CPU_S390X_Z10EC_2, + UC_CPU_S390X_Z10BC, + UC_CPU_S390X_Z10EC_3, + UC_CPU_S390X_Z10BC_2, + UC_CPU_S390X_Z196, + UC_CPU_S390X_Z196_2, + UC_CPU_S390X_Z114, + UC_CPU_S390X_ZEC12, + UC_CPU_S390X_ZEC12_2, + UC_CPU_S390X_ZBC12, + UC_CPU_S390X_Z13, + UC_CPU_S390X_Z13_2, + UC_CPU_S390X_Z13S, + UC_CPU_S390X_Z14, + UC_CPU_S390X_Z14_2, + UC_CPU_S390X_Z14ZR1, + UC_CPU_S390X_GEN15A, + UC_CPU_S390X_GEN15B, + UC_CPU_S390X_QEMU, + UC_CPU_S390X_MAX, + + UC_CPU_S390X_ENDING +} uc_cpu_s390x; + +//> S390X registers +typedef enum uc_s390x_reg { + UC_S390X_REG_INVALID = 0, + //> General purpose registers + UC_S390X_REG_R0, + UC_S390X_REG_R1, + UC_S390X_REG_R2, + UC_S390X_REG_R3, + UC_S390X_REG_R4, + UC_S390X_REG_R5, + UC_S390X_REG_R6, + UC_S390X_REG_R7, + UC_S390X_REG_R8, + UC_S390X_REG_R9, + UC_S390X_REG_R10, + UC_S390X_REG_R11, + UC_S390X_REG_R12, + UC_S390X_REG_R13, + UC_S390X_REG_R14, + UC_S390X_REG_R15, + + //> Floating point registers + UC_S390X_REG_F0, + UC_S390X_REG_F1, + UC_S390X_REG_F2, + UC_S390X_REG_F3, + UC_S390X_REG_F4, + UC_S390X_REG_F5, + UC_S390X_REG_F6, + UC_S390X_REG_F7, + UC_S390X_REG_F8, + UC_S390X_REG_F9, + UC_S390X_REG_F10, + UC_S390X_REG_F11, + UC_S390X_REG_F12, + UC_S390X_REG_F13, + UC_S390X_REG_F14, + UC_S390X_REG_F15, + UC_S390X_REG_F16, + UC_S390X_REG_F17, + UC_S390X_REG_F18, + UC_S390X_REG_F19, + UC_S390X_REG_F20, + UC_S390X_REG_F21, + UC_S390X_REG_F22, + UC_S390X_REG_F23, + UC_S390X_REG_F24, + UC_S390X_REG_F25, + UC_S390X_REG_F26, + UC_S390X_REG_F27, + UC_S390X_REG_F28, + UC_S390X_REG_F29, + UC_S390X_REG_F30, + UC_S390X_REG_F31, + + //> Access registers + UC_S390X_REG_A0, + UC_S390X_REG_A1, + UC_S390X_REG_A2, + UC_S390X_REG_A3, + UC_S390X_REG_A4, + UC_S390X_REG_A5, + UC_S390X_REG_A6, + UC_S390X_REG_A7, + UC_S390X_REG_A8, + UC_S390X_REG_A9, + UC_S390X_REG_A10, + UC_S390X_REG_A11, + UC_S390X_REG_A12, + UC_S390X_REG_A13, + UC_S390X_REG_A14, + UC_S390X_REG_A15, + + UC_S390X_REG_PC, // PC register + UC_S390X_REG_PSWM, + + UC_S390X_REG_ENDING, // <-- mark the end of the list or registers + + //> Alias registers +} uc_s390x_reg; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/sparc.h b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/sparc.h new file mode 100644 index 0000000000..776cd2cdae --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/sparc.h @@ -0,0 +1,172 @@ +/* Unicorn Emulator Engine */ +/* By Nguyen Anh Quynh , 2014-2017 */ +/* This file is released under LGPL2. + See COPYING.LGPL2 in root directory for more details +*/ + +#ifndef UNICORN_SPARC_H +#define UNICORN_SPARC_H + +#ifdef __cplusplus +extern "C" { +#endif + +// GCC SPARC toolchain has a default macro called "sparc" which breaks +// compilation +#undef sparc + +#ifdef _MSC_VER +#pragma warning(disable : 4201) +#endif + +//> SPARC32 CPU +typedef enum uc_cpu_sparc32 { + UC_CPU_SPARC32_FUJITSU_MB86904 = 0, + UC_CPU_SPARC32_FUJITSU_MB86907, + UC_CPU_SPARC32_TI_MICROSPARC_I, + UC_CPU_SPARC32_TI_MICROSPARC_II, + UC_CPU_SPARC32_TI_MICROSPARC_IIEP, + UC_CPU_SPARC32_TI_SUPERSPARC_40, + UC_CPU_SPARC32_TI_SUPERSPARC_50, + UC_CPU_SPARC32_TI_SUPERSPARC_51, + UC_CPU_SPARC32_TI_SUPERSPARC_60, + UC_CPU_SPARC32_TI_SUPERSPARC_61, + UC_CPU_SPARC32_TI_SUPERSPARC_II, + UC_CPU_SPARC32_LEON2, + UC_CPU_SPARC32_LEON3, + + UC_CPU_SPARC32_ENDING +} uc_cpu_sparc32; + +//> SPARC64 CPU +typedef enum uc_cpu_sparc64 { + UC_CPU_SPARC64_FUJITSU = 0, + UC_CPU_SPARC64_FUJITSU_III, + UC_CPU_SPARC64_FUJITSU_IV, + UC_CPU_SPARC64_FUJITSU_V, + UC_CPU_SPARC64_TI_ULTRASPARC_I, + UC_CPU_SPARC64_TI_ULTRASPARC_II, + UC_CPU_SPARC64_TI_ULTRASPARC_III, + UC_CPU_SPARC64_TI_ULTRASPARC_IIE, + UC_CPU_SPARC64_SUN_ULTRASPARC_III, + UC_CPU_SPARC64_SUN_ULTRASPARC_III_CU, + UC_CPU_SPARC64_SUN_ULTRASPARC_IIII, + UC_CPU_SPARC64_SUN_ULTRASPARC_IV, + UC_CPU_SPARC64_SUN_ULTRASPARC_IV_PLUS, + UC_CPU_SPARC64_SUN_ULTRASPARC_IIII_PLUS, + UC_CPU_SPARC64_SUN_ULTRASPARC_T1, + UC_CPU_SPARC64_SUN_ULTRASPARC_T2, + UC_CPU_SPARC64_NEC_ULTRASPARC_I, + + UC_CPU_SPARC64_ENDING +} uc_cpu_sparc64; + +//> SPARC registers +typedef enum uc_sparc_reg { + UC_SPARC_REG_INVALID = 0, + + UC_SPARC_REG_F0, + UC_SPARC_REG_F1, + UC_SPARC_REG_F2, + UC_SPARC_REG_F3, + UC_SPARC_REG_F4, + UC_SPARC_REG_F5, + UC_SPARC_REG_F6, + UC_SPARC_REG_F7, + UC_SPARC_REG_F8, + UC_SPARC_REG_F9, + UC_SPARC_REG_F10, + UC_SPARC_REG_F11, + UC_SPARC_REG_F12, + UC_SPARC_REG_F13, + UC_SPARC_REG_F14, + UC_SPARC_REG_F15, + UC_SPARC_REG_F16, + UC_SPARC_REG_F17, + UC_SPARC_REG_F18, + UC_SPARC_REG_F19, + UC_SPARC_REG_F20, + UC_SPARC_REG_F21, + UC_SPARC_REG_F22, + UC_SPARC_REG_F23, + UC_SPARC_REG_F24, + UC_SPARC_REG_F25, + UC_SPARC_REG_F26, + UC_SPARC_REG_F27, + UC_SPARC_REG_F28, + UC_SPARC_REG_F29, + UC_SPARC_REG_F30, + UC_SPARC_REG_F31, + UC_SPARC_REG_F32, + UC_SPARC_REG_F34, + UC_SPARC_REG_F36, + UC_SPARC_REG_F38, + UC_SPARC_REG_F40, + UC_SPARC_REG_F42, + UC_SPARC_REG_F44, + UC_SPARC_REG_F46, + UC_SPARC_REG_F48, + UC_SPARC_REG_F50, + UC_SPARC_REG_F52, + UC_SPARC_REG_F54, + UC_SPARC_REG_F56, + UC_SPARC_REG_F58, + UC_SPARC_REG_F60, + UC_SPARC_REG_F62, + UC_SPARC_REG_FCC0, // Floating condition codes + UC_SPARC_REG_FCC1, + UC_SPARC_REG_FCC2, + UC_SPARC_REG_FCC3, + UC_SPARC_REG_G0, + UC_SPARC_REG_G1, + UC_SPARC_REG_G2, + UC_SPARC_REG_G3, + UC_SPARC_REG_G4, + UC_SPARC_REG_G5, + UC_SPARC_REG_G6, + UC_SPARC_REG_G7, + UC_SPARC_REG_I0, + UC_SPARC_REG_I1, + UC_SPARC_REG_I2, + UC_SPARC_REG_I3, + UC_SPARC_REG_I4, + UC_SPARC_REG_I5, + UC_SPARC_REG_FP, + UC_SPARC_REG_I7, + UC_SPARC_REG_ICC, // Integer condition codes + UC_SPARC_REG_L0, + UC_SPARC_REG_L1, + UC_SPARC_REG_L2, + UC_SPARC_REG_L3, + UC_SPARC_REG_L4, + UC_SPARC_REG_L5, + UC_SPARC_REG_L6, + UC_SPARC_REG_L7, + UC_SPARC_REG_O0, + UC_SPARC_REG_O1, + UC_SPARC_REG_O2, + UC_SPARC_REG_O3, + UC_SPARC_REG_O4, + UC_SPARC_REG_O5, + UC_SPARC_REG_SP, + UC_SPARC_REG_O7, + UC_SPARC_REG_Y, + + // special register + UC_SPARC_REG_XCC, + + // pseudo register + UC_SPARC_REG_PC, // program counter register + + UC_SPARC_REG_ENDING, // <-- mark the end of the list of registers + + // extras + UC_SPARC_REG_O6 = UC_SPARC_REG_SP, + UC_SPARC_REG_I6 = UC_SPARC_REG_FP, +} uc_sparc_reg; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/tricore.h b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/tricore.h new file mode 100644 index 0000000000..bb9aa9d158 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/tricore.h @@ -0,0 +1,174 @@ +/* This file is released under LGPL2. + See COPYING.LGPL2 in root directory for more details +*/ + +/* + Created for Unicorn Engine by Eric Poole , 2022 + Copyright 2022 Aptiv +*/ + +#ifndef UNICORN_TRICORE_H +#define UNICORN_TRICORE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _MSC_VER +#pragma warning(disable : 4201) +#endif + +//> TRICORE CPU +typedef enum uc_cpu_tricore { + UC_CPU_TRICORE_TC1796, + UC_CPU_TRICORE_TC1797, + UC_CPU_TRICORE_TC27X, + + UC_CPU_TRICORE_ENDING +} uc_cpu_tricore; + +//> TRICORE registers +typedef enum uc_tricore_reg { + UC_TRICORE_REG_INVALID = 0, + + // General purpose registers (GPR) + // Address GPR + UC_TRICORE_REG_A0, + UC_TRICORE_REG_A1, + UC_TRICORE_REG_A2, + UC_TRICORE_REG_A3, + UC_TRICORE_REG_A4, + UC_TRICORE_REG_A5, + UC_TRICORE_REG_A6, + UC_TRICORE_REG_A7, + UC_TRICORE_REG_A8, + UC_TRICORE_REG_A9, + UC_TRICORE_REG_A10, + UC_TRICORE_REG_A11, + UC_TRICORE_REG_A12, + UC_TRICORE_REG_A13, + UC_TRICORE_REG_A14, + UC_TRICORE_REG_A15, + // Data GPR + UC_TRICORE_REG_D0, + UC_TRICORE_REG_D1, + UC_TRICORE_REG_D2, + UC_TRICORE_REG_D3, + UC_TRICORE_REG_D4, + UC_TRICORE_REG_D5, + UC_TRICORE_REG_D6, + UC_TRICORE_REG_D7, + UC_TRICORE_REG_D8, + UC_TRICORE_REG_D9, + UC_TRICORE_REG_D10, + UC_TRICORE_REG_D11, + UC_TRICORE_REG_D12, + UC_TRICORE_REG_D13, + UC_TRICORE_REG_D14, + UC_TRICORE_REG_D15, + + /* CSFR Register */ + UC_TRICORE_REG_PCXI, + + UC_TRICORE_REG_PSW, + + /* PSW flag cache for faster execution */ + UC_TRICORE_REG_PSW_USB_C, + UC_TRICORE_REG_PSW_USB_V, + UC_TRICORE_REG_PSW_USB_SV, + UC_TRICORE_REG_PSW_USB_AV, + UC_TRICORE_REG_PSW_USB_SAV, + + UC_TRICORE_REG_PC, + UC_TRICORE_REG_SYSCON, + UC_TRICORE_REG_CPU_ID, + UC_TRICORE_REG_BIV, + UC_TRICORE_REG_BTV, + UC_TRICORE_REG_ISP, + UC_TRICORE_REG_ICR, + UC_TRICORE_REG_FCX, + UC_TRICORE_REG_LCX, + UC_TRICORE_REG_COMPAT, + + UC_TRICORE_REG_DPR0_U, + UC_TRICORE_REG_DPR1_U, + UC_TRICORE_REG_DPR2_U, + UC_TRICORE_REG_DPR3_U, + UC_TRICORE_REG_DPR0_L, + UC_TRICORE_REG_DPR1_L, + UC_TRICORE_REG_DPR2_L, + UC_TRICORE_REG_DPR3_L, + + UC_TRICORE_REG_CPR0_U, + UC_TRICORE_REG_CPR1_U, + UC_TRICORE_REG_CPR2_U, + UC_TRICORE_REG_CPR3_U, + UC_TRICORE_REG_CPR0_L, + UC_TRICORE_REG_CPR1_L, + UC_TRICORE_REG_CPR2_L, + UC_TRICORE_REG_CPR3_L, + + UC_TRICORE_REG_DPM0, + UC_TRICORE_REG_DPM1, + UC_TRICORE_REG_DPM2, + UC_TRICORE_REG_DPM3, + + UC_TRICORE_REG_CPM0, + UC_TRICORE_REG_CPM1, + UC_TRICORE_REG_CPM2, + UC_TRICORE_REG_CPM3, + + /* Memory Management Registers */ + UC_TRICORE_REG_MMU_CON, + UC_TRICORE_REG_MMU_ASI, + UC_TRICORE_REG_MMU_TVA, + UC_TRICORE_REG_MMU_TPA, + UC_TRICORE_REG_MMU_TPX, + UC_TRICORE_REG_MMU_TFA, + + // 1.3.1 Only + UC_TRICORE_REG_BMACON, + UC_TRICORE_REG_SMACON, + UC_TRICORE_REG_DIEAR, + UC_TRICORE_REG_DIETR, + UC_TRICORE_REG_CCDIER, + UC_TRICORE_REG_MIECON, + UC_TRICORE_REG_PIEAR, + UC_TRICORE_REG_PIETR, + UC_TRICORE_REG_CCPIER, + + /* Debug Registers */ + UC_TRICORE_REG_DBGSR, + UC_TRICORE_REG_EXEVT, + UC_TRICORE_REG_CREVT, + UC_TRICORE_REG_SWEVT, + UC_TRICORE_REG_TR0EVT, + UC_TRICORE_REG_TR1EVT, + UC_TRICORE_REG_DMS, + UC_TRICORE_REG_DCX, + UC_TRICORE_REG_DBGTCR, + UC_TRICORE_REG_CCTRL, + UC_TRICORE_REG_CCNT, + UC_TRICORE_REG_ICNT, + UC_TRICORE_REG_M1CNT, + UC_TRICORE_REG_M2CNT, + UC_TRICORE_REG_M3CNT, + + UC_TRICORE_REG_ENDING, // <-- mark the end of the list of registers + + // alias registers + UC_TRICORE_REG_GA0 = UC_TRICORE_REG_A0, + UC_TRICORE_REG_GA1 = UC_TRICORE_REG_A1, + UC_TRICORE_REG_GA8 = UC_TRICORE_REG_A8, + UC_TRICORE_REG_GA9 = UC_TRICORE_REG_A9, + UC_TRICORE_REG_SP = UC_TRICORE_REG_A10, + UC_TRICORE_REG_LR = UC_TRICORE_REG_A11, + UC_TRICORE_REG_IA = UC_TRICORE_REG_A15, + UC_TRICORE_REG_ID = UC_TRICORE_REG_D15, +} uc_tricore_reg; + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/unicorn.h b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/unicorn.h new file mode 100644 index 0000000000..ade720409c --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/unicorn.h @@ -0,0 +1,1176 @@ +/* Unicorn Emulator Engine */ +/* By Nguyen Anh Quynh , 2015-2017 */ +/* This file is released under LGPL2. + See COPYING.LGPL2 in root directory for more details +*/ + +#ifndef UNICORN_ENGINE_H +#define UNICORN_ENGINE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "platform.h" +#include + +#if defined(UNICORN_HAS_OSXKERNEL) +#include +#else +#include +#include +#endif + +struct uc_struct; +typedef struct uc_struct uc_engine; + +typedef size_t uc_hook; + +#include "m68k.h" +#include "x86.h" +#include "arm.h" +#include "arm64.h" +#include "mips.h" +#include "sparc.h" +#include "ppc.h" +#include "rh850.h" +#include "riscv.h" +#include "s390x.h" +#include "tricore.h" + +#ifdef __GNUC__ +#define DEFAULT_VISIBILITY __attribute__((visibility("default"))) +#else +#define DEFAULT_VISIBILITY +#endif + +#ifdef _MSC_VER +#pragma warning(disable : 4201) +#pragma warning(disable : 4100) +#ifdef UNICORN_SHARED +#define UNICORN_EXPORT __declspec(dllexport) +#else // defined(UNICORN_STATIC) +#define UNICORN_EXPORT +#endif +#else +#ifdef __GNUC__ +#define UNICORN_EXPORT __attribute__((visibility("default"))) +#else +#define UNICORN_EXPORT +#endif +#endif + +#ifdef __GNUC__ +#define UNICORN_DEPRECATED __attribute__((deprecated)) +#elif defined(_MSC_VER) +#define UNICORN_DEPRECATED __declspec(deprecated) +#else +#pragma message( \ + "WARNING: You need to implement UNICORN_DEPRECATED for this compiler") +#define UNICORN_DEPRECATED +#endif + +// Unicorn API version +#define UC_API_MAJOR 2 +#define UC_API_MINOR 0 +#define UC_API_PATCH 1 +// Release candidate version, 255 means the official release. +#define UC_API_EXTRA 255 + +// Unicorn package version +#define UC_VERSION_MAJOR UC_API_MAJOR +#define UC_VERSION_MINOR UC_API_MINOR +#define UC_VERSION_PATCH UC_API_PATCH +#define UC_VERSION_EXTRA UC_API_EXTRA + +/* + Macro to create combined version which can be compared to + result of uc_version() API. +*/ +#define UC_MAKE_VERSION(major, minor) ((major << 8) + minor) + +// Scales to calculate timeout on microsecond unit +// 1 second = 1000,000 microseconds +#define UC_SECOND_SCALE 1000000 +// 1 milisecond = 1000 nanoseconds +#define UC_MILISECOND_SCALE 1000 + +// Architecture type +typedef enum uc_arch { + UC_ARCH_ARM = 1, // ARM architecture (including Thumb, Thumb-2) + UC_ARCH_ARM64, // ARM-64, also called AArch64 + UC_ARCH_MIPS, // Mips architecture + UC_ARCH_X86, // X86 architecture (including x86 & x86-64) + UC_ARCH_PPC, // PowerPC architecture + UC_ARCH_SPARC, // Sparc architecture + UC_ARCH_M68K, // M68K architecture + UC_ARCH_RISCV, // RISCV architecture + UC_ARCH_S390X, // S390X architecture + UC_ARCH_TRICORE, // TriCore architecture + UC_ARCH_RH850, // Renesas RH850 architecture (V850e3v2) + UC_ARCH_MAX, +} uc_arch; + +// Mode type +typedef enum uc_mode { + UC_MODE_LITTLE_ENDIAN = 0, // little-endian mode (default mode) + UC_MODE_BIG_ENDIAN = 1 << 30, // big-endian mode + + // arm / arm64 + UC_MODE_ARM = 0, // ARM mode + UC_MODE_THUMB = 1 << 4, // THUMB mode (including Thumb-2) + // Depreciated, use UC_ARM_CPU_* with uc_ctl instead. + UC_MODE_MCLASS = 1 << 5, // ARM's Cortex-M series. + UC_MODE_V8 = 1 << 6, // ARMv8 A32 encodings for ARM + UC_MODE_ARMBE8 = 1 << 10, // Big-endian data and Little-endian code. + // Legacy support for UC1 only. + + // arm (32bit) cpu types + // Depreciated, use UC_ARM_CPU_* with uc_ctl instead. + UC_MODE_ARM926 = 1 << 7, // ARM926 CPU type + UC_MODE_ARM946 = 1 << 8, // ARM946 CPU type + UC_MODE_ARM1176 = 1 << 9, // ARM1176 CPU type + + // mips + UC_MODE_MICRO = 1 << 4, // MicroMips mode (currently unsupported) + UC_MODE_MIPS3 = 1 << 5, // Mips III ISA (currently unsupported) + UC_MODE_MIPS32R6 = 1 << 6, // Mips32r6 ISA (currently unsupported) + UC_MODE_MIPS32 = 1 << 2, // Mips32 ISA + UC_MODE_MIPS64 = 1 << 3, // Mips64 ISA + + // x86 / x64 + UC_MODE_16 = 1 << 1, // 16-bit mode + UC_MODE_32 = 1 << 2, // 32-bit mode + UC_MODE_64 = 1 << 3, // 64-bit mode + + // ppc + UC_MODE_PPC32 = 1 << 2, // 32-bit mode + UC_MODE_PPC64 = 1 << 3, // 64-bit mode (currently unsupported) + UC_MODE_QPX = + 1 << 4, // Quad Processing eXtensions mode (currently unsupported) + + // sparc + UC_MODE_SPARC32 = 1 << 2, // 32-bit mode + UC_MODE_SPARC64 = 1 << 3, // 64-bit mode + UC_MODE_V9 = 1 << 4, // SparcV9 mode (currently unsupported) + + // rh850 + UC_MODE_RH850 = 1 << 2, // 32-bit mode + + // riscv + UC_MODE_RISCV32 = 1 << 2, // 32-bit mode + UC_MODE_RISCV64 = 1 << 3, // 64-bit mode + + // m68k +} uc_mode; + +// All type of errors encountered by Unicorn API. +// These are values returned by uc_errno() +typedef enum uc_err { + UC_ERR_OK = 0, // No error: everything was fine + UC_ERR_NOMEM, // Out-Of-Memory error: uc_open(), uc_emulate() + UC_ERR_ARCH, // Unsupported architecture: uc_open() + UC_ERR_HANDLE, // Invalid handle + UC_ERR_MODE, // Invalid/unsupported mode: uc_open() + UC_ERR_VERSION, // Unsupported version (bindings) + UC_ERR_READ_UNMAPPED, // Quit emulation due to READ on unmapped memory: + // uc_emu_start() + UC_ERR_WRITE_UNMAPPED, // Quit emulation due to WRITE on unmapped memory: + // uc_emu_start() + UC_ERR_FETCH_UNMAPPED, // Quit emulation due to FETCH on unmapped memory: + // uc_emu_start() + UC_ERR_HOOK, // Invalid hook type: uc_hook_add() + UC_ERR_INSN_INVALID, // Quit emulation due to invalid instruction: + // uc_emu_start() + UC_ERR_MAP, // Invalid memory mapping: uc_mem_map() + UC_ERR_WRITE_PROT, // Quit emulation due to UC_MEM_WRITE_PROT violation: + // uc_emu_start() + UC_ERR_READ_PROT, // Quit emulation due to UC_MEM_READ_PROT violation: + // uc_emu_start() + UC_ERR_FETCH_PROT, // Quit emulation due to UC_MEM_FETCH_PROT violation: + // uc_emu_start() + UC_ERR_ARG, // Inavalid argument provided to uc_xxx function (See specific + // function API) + UC_ERR_READ_UNALIGNED, // Unaligned read + UC_ERR_WRITE_UNALIGNED, // Unaligned write + UC_ERR_FETCH_UNALIGNED, // Unaligned fetch + UC_ERR_HOOK_EXIST, // hook for this event already existed + UC_ERR_RESOURCE, // Insufficient resource: uc_emu_start() + UC_ERR_EXCEPTION, // Unhandled CPU exception +} uc_err; + +/* + Callback function for tracing code (UC_HOOK_CODE & UC_HOOK_BLOCK) + + @address: address where the code is being executed + @size: size of machine instruction(s) being executed, or 0 when size is + unknown + @user_data: user data passed to tracing APIs. +*/ +typedef void (*uc_cb_hookcode_t)(uc_engine *uc, uint64_t address, uint32_t size, + void *user_data); + +/* + Callback function for tracing interrupts (for uc_hook_intr()) + + @intno: interrupt number + @user_data: user data passed to tracing APIs. +*/ +typedef void (*uc_cb_hookintr_t)(uc_engine *uc, uint32_t intno, + void *user_data); + +/* + Callback function for tracing invalid instructions + + @user_data: user data passed to tracing APIs. + + @return: return true to continue, or false to stop program (due to invalid + instruction). +*/ +typedef bool (*uc_cb_hookinsn_invalid_t)(uc_engine *uc, void *user_data); + +/* + Callback function for tracing IN instruction of X86 + + @port: port number + @size: data size (1/2/4) to be read from this port + @user_data: user data passed to tracing APIs. +*/ +typedef uint32_t (*uc_cb_insn_in_t)(uc_engine *uc, uint32_t port, int size, + void *user_data); + +/* + Callback function for OUT instruction of X86 + + @port: port number + @size: data size (1/2/4) to be written to this port + @value: data value to be written to this port +*/ +typedef void (*uc_cb_insn_out_t)(uc_engine *uc, uint32_t port, int size, + uint32_t value, void *user_data); + +// Represent a TranslationBlock. +typedef struct uc_tb { + uint64_t pc; + uint16_t icount; + uint16_t size; +} uc_tb; + +/* + Callback function for new edges between translation blocks. + + @cur_tb: Current TB which is to be generated. + @prev_tb: The previous TB. +*/ +typedef void (*uc_hook_edge_gen_t)(uc_engine *uc, uc_tb *cur_tb, uc_tb *prev_tb, + void *user_data); + +/* + Callback function for tcg opcodes that fits in two arguments. + + @address: Current pc. + @arg1: The first argument. + @arg2: The second argument. +*/ +typedef void (*uc_hook_tcg_op_2)(uc_engine *uc, uint64_t address, uint64_t arg1, + uint64_t arg2, uint32_t size, void *user_data); + +typedef uc_hook_tcg_op_2 uc_hook_tcg_sub_t; + +/* + Callback function for MMIO read + + @offset: offset to the base address of the IO memory. + @size: data size to read + @user_data: user data passed to uc_mmio_map() +*/ +typedef uint64_t (*uc_cb_mmio_read_t)(uc_engine *uc, uint64_t offset, + unsigned size, void *user_data); + +/* + Callback function for MMIO write + + @offset: offset to the base address of the IO memory. + @size: data size to write + @value: data value to be written + @user_data: user data passed to uc_mmio_map() +*/ +typedef void (*uc_cb_mmio_write_t)(uc_engine *uc, uint64_t offset, + unsigned size, uint64_t value, + void *user_data); + +// All type of memory accesses for UC_HOOK_MEM_* +typedef enum uc_mem_type { + UC_MEM_READ = 16, // Memory is read from + UC_MEM_WRITE, // Memory is written to + UC_MEM_FETCH, // Memory is fetched + UC_MEM_READ_UNMAPPED, // Unmapped memory is read from + UC_MEM_WRITE_UNMAPPED, // Unmapped memory is written to + UC_MEM_FETCH_UNMAPPED, // Unmapped memory is fetched + UC_MEM_WRITE_PROT, // Write to write protected, but mapped, memory + UC_MEM_READ_PROT, // Read from read protected, but mapped, memory + UC_MEM_FETCH_PROT, // Fetch from non-executable, but mapped, memory + UC_MEM_READ_AFTER, // Memory is read from (successful access) +} uc_mem_type; + +// These are all op codes we support to hook for UC_HOOK_TCG_OP_CODE. +// Be cautious since it may bring much more overhead than UC_HOOK_CODE without +// proper flags. +// TODO: Tracing UC_TCG_OP_CALL should be interesting. +typedef enum uc_tcg_op_code { + UC_TCG_OP_SUB = 0, // Both sub_i32 and sub_i64 +} uc_tcg_op_code; + +// These are extra flags to be paired with uc_tcg_op_code which is helpful to +// instrument in some certain cases. +typedef enum uc_tcg_op_flag { + // Only instrument opcode if it would set cc_dst, i.e. cmp instruction. + UC_TCG_OP_FLAG_CMP = 1 << 0, + // Only instrument opcode which is directly translated. + // i.e. x86 sub/subc -> tcg sub_i32/64 + UC_TCG_OP_FLAG_DIRECT = 1 << 1 +} uc_tcg_op_flag; + +// All type of hooks for uc_hook_add() API. +typedef enum uc_hook_type { + // Hook all interrupt/syscall events + UC_HOOK_INTR = 1 << 0, + // Hook a particular instruction - only a very small subset of instructions + // supported here + UC_HOOK_INSN = 1 << 1, + // Hook a range of code + UC_HOOK_CODE = 1 << 2, + // Hook basic blocks + UC_HOOK_BLOCK = 1 << 3, + // Hook for memory read on unmapped memory + UC_HOOK_MEM_READ_UNMAPPED = 1 << 4, + // Hook for invalid memory write events + UC_HOOK_MEM_WRITE_UNMAPPED = 1 << 5, + // Hook for invalid memory fetch for execution events + UC_HOOK_MEM_FETCH_UNMAPPED = 1 << 6, + // Hook for memory read on read-protected memory + UC_HOOK_MEM_READ_PROT = 1 << 7, + // Hook for memory write on write-protected memory + UC_HOOK_MEM_WRITE_PROT = 1 << 8, + // Hook for memory fetch on non-executable memory + UC_HOOK_MEM_FETCH_PROT = 1 << 9, + // Hook memory read events. + UC_HOOK_MEM_READ = 1 << 10, + // Hook memory write events. + UC_HOOK_MEM_WRITE = 1 << 11, + // Hook memory fetch for execution events + UC_HOOK_MEM_FETCH = 1 << 12, + // Hook memory read events, but only successful access. + // The callback will be triggered after successful read. + UC_HOOK_MEM_READ_AFTER = 1 << 13, + // Hook invalid instructions exceptions. + UC_HOOK_INSN_INVALID = 1 << 14, + // Hook on new edge generation. Could be useful in program analysis. + // + // NOTE: This is different from UC_HOOK_BLOCK in 2 ways: + // 1. The hook is called before executing code. + // 2. The hook is only called when generation is triggered. + UC_HOOK_EDGE_GENERATED = 1 << 15, + // Hook on specific tcg op code. The usage of this hook is similar to + // UC_HOOK_INSN. + UC_HOOK_TCG_OPCODE = 1 << 16, +} uc_hook_type; + +// Hook type for all events of unmapped memory access +#define UC_HOOK_MEM_UNMAPPED \ + (UC_HOOK_MEM_READ_UNMAPPED + UC_HOOK_MEM_WRITE_UNMAPPED + \ + UC_HOOK_MEM_FETCH_UNMAPPED) +// Hook type for all events of illegal protected memory access +#define UC_HOOK_MEM_PROT \ + (UC_HOOK_MEM_READ_PROT + UC_HOOK_MEM_WRITE_PROT + UC_HOOK_MEM_FETCH_PROT) +// Hook type for all events of illegal read memory access +#define UC_HOOK_MEM_READ_INVALID \ + (UC_HOOK_MEM_READ_PROT + UC_HOOK_MEM_READ_UNMAPPED) +// Hook type for all events of illegal write memory access +#define UC_HOOK_MEM_WRITE_INVALID \ + (UC_HOOK_MEM_WRITE_PROT + UC_HOOK_MEM_WRITE_UNMAPPED) +// Hook type for all events of illegal fetch memory access +#define UC_HOOK_MEM_FETCH_INVALID \ + (UC_HOOK_MEM_FETCH_PROT + UC_HOOK_MEM_FETCH_UNMAPPED) +// Hook type for all events of illegal memory access +#define UC_HOOK_MEM_INVALID (UC_HOOK_MEM_UNMAPPED + UC_HOOK_MEM_PROT) +// Hook type for all events of valid memory access +// NOTE: UC_HOOK_MEM_READ is triggered before UC_HOOK_MEM_READ_PROT and +// UC_HOOK_MEM_READ_UNMAPPED, so +// this hook may technically trigger on some invalid reads. +#define UC_HOOK_MEM_VALID \ + (UC_HOOK_MEM_READ + UC_HOOK_MEM_WRITE + UC_HOOK_MEM_FETCH) + +/* + Callback function for hooking memory (READ, WRITE & FETCH) + + @type: this memory is being READ, or WRITE + @address: address where the code is being executed + @size: size of data being read or written + @value: value of data being written to memory, or irrelevant if type = READ. + @user_data: user data passed to tracing APIs +*/ +typedef void (*uc_cb_hookmem_t)(uc_engine *uc, uc_mem_type type, + uint64_t address, int size, int64_t value, + void *user_data); + +/* + Callback function for handling invalid memory access events (UNMAPPED and + PROT events) + + @type: this memory is being READ, or WRITE + @address: address where the code is being executed + @size: size of data being read or written + @value: value of data being written to memory, or irrelevant if type = READ. + @user_data: user data passed to tracing APIs + + @return: return true to continue, or false to stop program (due to invalid + memory). NOTE: returning true to continue execution will only work if the + accessed memory is made accessible with the correct permissions during the + hook. + + In the event of a UC_MEM_READ_UNMAPPED or UC_MEM_WRITE_UNMAPPED + callback, the memory should be uc_mem_map()-ed with the correct permissions, + and the instruction will then read or write to the address as it was supposed + to. + + In the event of a UC_MEM_FETCH_UNMAPPED callback, the memory can be + mapped in as executable, in which case execution will resume from the fetched + address. The instruction pointer may be written to in order to change where + execution resumes, but the fetch must succeed if execution is to resume. +*/ +typedef bool (*uc_cb_eventmem_t)(uc_engine *uc, uc_mem_type type, + uint64_t address, int size, int64_t value, + void *user_data); + +/* + Memory region mapped by uc_mem_map() and uc_mem_map_ptr() + Retrieve the list of memory regions with uc_mem_regions() +*/ +typedef struct uc_mem_region { + uint64_t begin; // begin address of the region (inclusive) + uint64_t end; // end address of the region (inclusive) + uint32_t perms; // memory permissions of the region +} uc_mem_region; + +// All type of queries for uc_query() API. +typedef enum uc_query_type { + // Dynamically query current hardware mode. + UC_QUERY_MODE = 1, + UC_QUERY_PAGE_SIZE, // query pagesize of engine + UC_QUERY_ARCH, // query architecture of engine (for ARM to query Thumb mode) + UC_QUERY_TIMEOUT, // query if emulation stops due to timeout (indicated if + // result = True) +} uc_query_type; + +// The implementation of uc_ctl is like what Linux ioctl does but slightly +// different. +// +// A uc_control_type passed to uc_ctl is constructed as: +// +// R/W NR Reserved Type +// [ ] [ ] [ ] [ ] +// 31 30 29 26 25 16 15 0 +// +// @R/W: Whether the operation is a read or write access. +// @NR: Number of arguments. +// @Reserved: Should be zero, reserved for future extension. +// @Type: Taken from uc_control_type enum. +// +// See the helper macros below. + +// No input and output arguments. +#define UC_CTL_IO_NONE (0) +// Only input arguments for a write operation. +#define UC_CTL_IO_WRITE (1) +// Only output arguments for a read operation. +#define UC_CTL_IO_READ (2) +// The arguments include both input and output arugments. +#define UC_CTL_IO_READ_WRITE (UC_CTL_IO_WRITE | UC_CTL_IO_READ) + +#define UC_CTL(type, nr, rw) \ + (uc_control_type)((type) | ((nr) << 26) | ((rw) << 30)) +#define UC_CTL_NONE(type, nr) UC_CTL(type, nr, UC_CTL_IO_NONE) +#define UC_CTL_READ(type, nr) UC_CTL(type, nr, UC_CTL_IO_READ) +#define UC_CTL_WRITE(type, nr) UC_CTL(type, nr, UC_CTL_IO_WRITE) +#define UC_CTL_READ_WRITE(type, nr) UC_CTL(type, nr, UC_CTL_IO_READ_WRITE) + +// All type of controls for uc_ctl API. +// The controls are organized in a tree level. +// If a control don't have `Set` or `Get` for @args, it means it's r/o or w/o. +typedef enum uc_control_type { + // Current mode. + // Read: @args = (int*) + UC_CTL_UC_MODE = 0, + // Curent page size. + // Write: @args = (uint32_t) + // Read: @args = (uint32_t*) + UC_CTL_UC_PAGE_SIZE, + // Current arch. + // Read: @args = (int*) + UC_CTL_UC_ARCH, + // Current timeout. + // Read: @args = (uint64_t*) + UC_CTL_UC_TIMEOUT, + // Enable multiple exits. + // Without this control, reading/setting exits won't work. + // This is for API backward compatibility. + // Write: @args = (int) + UC_CTL_UC_USE_EXITS, + // The number of current exits. + // Read: @args = (size_t*) + UC_CTL_UC_EXITS_CNT, + // Current exits. + // Write: @args = (uint64_t* exits, size_t len) + // @len = UC_CTL_UC_EXITS_CNT + // Read: @args = (uint64_t* exits, size_t len) + // @len = UC_CTL_UC_EXITS_CNT + UC_CTL_UC_EXITS, + + // Set the cpu model of uc. + // Note this option can only be set before any Unicorn + // API is called except for uc_open. + // Write: @args = (int) + // Read: @args = (int*) + UC_CTL_CPU_MODEL, + // Request a tb cache at a specific address + // Read: @args = (uint64_t, uc_tb*) + UC_CTL_TB_REQUEST_CACHE, + // Invalidate a tb cache at a specific address + // Write: @args = (uint64_t, uint64_t) + UC_CTL_TB_REMOVE_CACHE, + // Invalidate all translation blocks. + // No arguments. + UC_CTL_TB_FLUSH + +} uc_control_type; + +/* + +Exits Mechanism + +In some cases, users may have multiple exits and the @until parameter of +uc_emu_start is not sufficient to control the emulation. The exits mechanism is +designed to solve this problem. Note that using hooks is aslo feasible, but the +exits could be slightly more efficient and easy to implement. + +By default, the exits mechanism is disabled to keep backward compatibility. That +is to say, calling uc_ctl_set/get_exits would return an error. Thus, to enable +the exits firstly, call: + + uc_ctl_exits_enable(uc) + +After this call, the @until parameter of uc_emu_start would have no effect on +the emulation, so: + + uc_emu_start(uc, 0x1000, 0 ...) + uc_emu_start(uc, 0x1000, 0x1000 ...) + uc_emu_start(uc, 0x1000, -1 ...) + +The three calls are totally equavelent since the @until is ignored. + +To setup the exits, users may call: + + uc_ctl_set/get_exits(uc, exits, len) + +For example, with an exits array [0x1000, 0x2000], uc_emu_start would stop at +either 0x1000 and 0x2000. With an exits array [], uc_emu_start won't stop unless +some hooks request a stop. + +If users would like to restore the default behavior of @until parameter, users +may call: + + uc_ctl_exits_disable(uc) + +After that, all exits setup previously would be cleared and @until parameter +would take effect again. + +See sample_ctl.c for a detailed example. + +*/ +#define uc_ctl_get_mode(uc, mode) \ + uc_ctl(uc, UC_CTL_READ(UC_CTL_UC_MODE, 1), (mode)) +#define uc_ctl_get_page_size(uc, ptr) \ + uc_ctl(uc, UC_CTL_READ(UC_CTL_UC_PAGE_SIZE, 1), (ptr)) +#define uc_ctl_set_page_size(uc, page_size) \ + uc_ctl(uc, UC_CTL_WRITE(UC_CTL_UC_PAGE_SIZE, 1), (page_size)) +#define uc_ctl_get_arch(uc, arch) \ + uc_ctl(uc, UC_CTL_READ(UC_CTL_UC_ARCH, 1), (arch)) +#define uc_ctl_get_timeout(uc, ptr) \ + uc_ctl(uc, UC_CTL_READ(UC_CTL_UC_TIMEOUT, 1), (ptr)) +#define uc_ctl_exits_enable(uc) \ + uc_ctl(uc, UC_CTL_WRITE(UC_CTL_UC_USE_EXITS, 1), 1) +#define uc_ctl_exits_disable(uc) \ + uc_ctl(uc, UC_CTL_WRITE(UC_CTL_UC_USE_EXITS, 1), 0) +#define uc_ctl_get_exits_cnt(uc, ptr) \ + uc_ctl(uc, UC_CTL_READ(UC_CTL_UC_EXITS_CNT, 1), (ptr)) +#define uc_ctl_get_exits(uc, buffer, len) \ + uc_ctl(uc, UC_CTL_READ(UC_CTL_UC_EXITS, 2), (buffer), (len)) +#define uc_ctl_set_exits(uc, buffer, len) \ + uc_ctl(uc, UC_CTL_WRITE(UC_CTL_UC_EXITS, 2), (buffer), (len)) +#define uc_ctl_get_cpu_model(uc, model) \ + uc_ctl(uc, UC_CTL_READ(UC_CTL_CPU_MODEL, 1), (model)) +#define uc_ctl_set_cpu_model(uc, model) \ + uc_ctl(uc, UC_CTL_WRITE(UC_CTL_CPU_MODEL, 1), (model)) +#define uc_ctl_remove_cache(uc, address, end) \ + uc_ctl(uc, UC_CTL_WRITE(UC_CTL_TB_REMOVE_CACHE, 2), (address), (end)) +#define uc_ctl_request_cache(uc, address, tb) \ + uc_ctl(uc, UC_CTL_READ_WRITE(UC_CTL_TB_REQUEST_CACHE, 2), (address), (tb)) +#define uc_ctl_flush_tlb(uc) uc_ctl(uc, UC_CTL_WRITE(UC_CTL_TB_FLUSH, 0)) +// Opaque storage for CPU context, used with uc_context_*() +struct uc_context; +typedef struct uc_context uc_context; + +/* + Return combined API version & major and minor version numbers. + + @major: major number of API version + @minor: minor number of API version + + @return hexical number as (major << 8 | minor), which encodes both + major & minor versions. + NOTE: This returned value can be compared with version number made + with macro UC_MAKE_VERSION + + For example, second API version would return 1 in @major, and 1 in @minor + The return value would be 0x0101 + + NOTE: if you only care about returned value, but not major and minor values, + set both @major & @minor arguments to NULL. +*/ +UNICORN_EXPORT +unsigned int uc_version(unsigned int *major, unsigned int *minor); + +/* + Determine if the given architecture is supported by this library. + + @arch: architecture type (UC_ARCH_*) + + @return True if this library supports the given arch. +*/ +UNICORN_EXPORT +bool uc_arch_supported(uc_arch arch); + +/* + Create new instance of unicorn engine. + + @arch: architecture type (UC_ARCH_*) + @mode: hardware mode. This is combined of UC_MODE_* + @uc: pointer to uc_engine, which will be updated at return time + + @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum + for detailed error). +*/ +UNICORN_EXPORT +uc_err uc_open(uc_arch arch, uc_mode mode, uc_engine **uc); + +/* + Close a Unicorn engine instance. + NOTE: this must be called only when there is no longer any + usage of @uc. This API releases some of @uc's cached memory, thus + any use of the Unicorn API with @uc after it has been closed may + crash your application. After this, @uc is invalid, and is no + longer usable. + + @uc: pointer to a handle returned by uc_open() + + @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum + for detailed error). +*/ +UNICORN_EXPORT +uc_err uc_close(uc_engine *uc); + +/* + Query internal status of engine. + + @uc: handle returned by uc_open() + @type: query type. See uc_query_type + + @result: save the internal status queried + + @return: error code of uc_err enum type (UC_ERR_*, see above) +*/ +UNICORN_EXPORT +uc_err uc_query(uc_engine *uc, uc_query_type type, size_t *result); + +/* + Control internal states of engine. + + Also see uc_ctl_* macro helpers for easy use. + + @uc: handle returned by uc_open() + @control: the control type. + @args: See uc_control_type for details about variadic arguments. + + @return: error code of uc_err enum type (UC_ERR_*, see above) +*/ +UNICORN_EXPORT +uc_err uc_ctl(uc_engine *uc, uc_control_type control, ...); + +/* + Report the last error number when some API function fails. + Like glibc's errno, uc_errno might not retain its old value once accessed. + + @uc: handle returned by uc_open() + + @return: error code of uc_err enum type (UC_ERR_*, see above) +*/ +UNICORN_EXPORT +uc_err uc_errno(uc_engine *uc); + +/* + Return a string describing given error code. + + @code: error code (see UC_ERR_* above) + + @return: returns a pointer to a string that describes the error code + passed in the argument @code + */ +UNICORN_EXPORT +const char *uc_strerror(uc_err code); + +/* + Write to register. + + @uc: handle returned by uc_open() + @regid: register ID that is to be modified. + @value: pointer to the value that will set to register @regid + + @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum + for detailed error). +*/ +UNICORN_EXPORT +uc_err uc_reg_write(uc_engine *uc, int regid, const void *value); + +/* + Read register value. + + @uc: handle returned by uc_open() + @regid: register ID that is to be retrieved. + @value: pointer to a variable storing the register value. + + @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum + for detailed error). +*/ +UNICORN_EXPORT +uc_err uc_reg_read(uc_engine *uc, int regid, void *value); + +/* + Write multiple register values. + + @uc: handle returned by uc_open() + @rges: array of register IDs to store + @value: pointer to array of register values + @count: length of both *regs and *vals + + @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum + for detailed error). +*/ +UNICORN_EXPORT +uc_err uc_reg_write_batch(uc_engine *uc, int *regs, void *const *vals, + int count); + +/* + Read multiple register values. + + @uc: handle returned by uc_open() + @rges: array of register IDs to retrieve + @value: pointer to array of values to hold registers + @count: length of both *regs and *vals + + @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum + for detailed error). +*/ +UNICORN_EXPORT +uc_err uc_reg_read_batch(uc_engine *uc, int *regs, void **vals, int count); + +/* + Write to a range of bytes in memory. + + @uc: handle returned by uc_open() + @address: starting memory address of bytes to set. + @bytes: pointer to a variable containing data to be written to memory. + @size: size of memory to write to. + + NOTE: @bytes must be big enough to contain @size bytes. + + @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum + for detailed error). +*/ +UNICORN_EXPORT +uc_err uc_mem_write(uc_engine *uc, uint64_t address, const void *bytes, + size_t size); + +/* + Read a range of bytes in memory. + + @uc: handle returned by uc_open() + @address: starting memory address of bytes to get. + @bytes: pointer to a variable containing data copied from memory. + @size: size of memory to read. + + NOTE: @bytes must be big enough to contain @size bytes. + + @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum + for detailed error). +*/ +UNICORN_EXPORT +uc_err uc_mem_read(uc_engine *uc, uint64_t address, void *bytes, size_t size); + +/* + Emulate machine code in a specific duration of time. + + @uc: handle returned by uc_open() + @begin: address where emulation starts + @until: address where emulation stops (i.e. when this address is hit) + @timeout: duration to emulate the code (in microseconds). When this value is 0, + we will emulate the code in infinite time, until the code is finished. + @count: the number of instructions to be emulated. When this value is 0, + we will emulate all the code available, until the code is finished. + + NOTE: The internal states of the engine is guranteed to be correct if and only + if uc_emu_start returns without any errors or errors have been handled in + the callbacks. + + @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum + for detailed error). +*/ +UNICORN_EXPORT +uc_err uc_emu_start(uc_engine *uc, uint64_t begin, uint64_t until, + uint64_t timeout, size_t count); + +/* + Stop emulation (which was started by uc_emu_start() API. + This is typically called from callback functions registered via tracing APIs. + + @uc: handle returned by uc_open() + + @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum + for detailed error). +*/ +UNICORN_EXPORT +uc_err uc_emu_stop(uc_engine *uc); + +/* + Register callback for a hook event. + The callback will be run when the hook event is hit. + + @uc: handle returned by uc_open() + @hh: hook handle returned from this registration. To be used in uc_hook_del() + API + @type: hook type, refer to uc_hook_type enum + @callback: callback to be run when instruction is hit + @user_data: user-defined data. This will be passed to callback function in its + last argument @user_data + @begin: start address of the area where the callback is in effect (inclusive) + @end: end address of the area where the callback is in effect (inclusive) + NOTE 1: the callback is called only if related address is in range [@begin, + @end] NOTE 2: if @begin > @end, callback is called whenever this hook type is + triggered + @...: variable arguments (depending on @type) + NOTE: if @type = UC_HOOK_INSN, this is the instruction ID. + currently, only x86 in, out, syscall, sysenter, cpuid are supported. + NOTE: if @type = UC_HOOK_TCG_OPCODE, arguments are @opcode and @flags. See + @uc_tcg_op_code and @uc_tcg_op_flag for details. + + @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum + for detailed error). +*/ +UNICORN_EXPORT +uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, int type, void *callback, + void *user_data, uint64_t begin, uint64_t end, ...); + +/* + Unregister (remove) a hook callback. + This API removes the hook callback registered by uc_hook_add(). + NOTE: this should be called only when you no longer want to trace. + After this, @hh is invalid, and no longer usable. + + @uc: handle returned by uc_open() + @hh: handle returned by uc_hook_add() + + @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum + for detailed error). +*/ +UNICORN_EXPORT +uc_err uc_hook_del(uc_engine *uc, uc_hook hh); + +typedef enum uc_prot { + UC_PROT_NONE = 0, + UC_PROT_READ = 1, + UC_PROT_WRITE = 2, + UC_PROT_EXEC = 4, + UC_PROT_ALL = 7, +} uc_prot; + +/* + Map memory in for emulation. + This API adds a memory region that can be used by emulation. + + @uc: handle returned by uc_open() + @address: starting address of the new memory region to be mapped in. + This address must be aligned to 4KB, or this will return with UC_ERR_ARG + error. + @size: size of the new memory region to be mapped in. + This size must be a multiple of 4KB, or this will return with UC_ERR_ARG + error. + @perms: Permissions for the newly mapped region. + This must be some combination of UC_PROT_READ | UC_PROT_WRITE | + UC_PROT_EXEC, or this will return with UC_ERR_ARG error. + + @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum + for detailed error). +*/ +UNICORN_EXPORT +uc_err uc_mem_map(uc_engine *uc, uint64_t address, size_t size, uint32_t perms); + +/* + Map existing host memory in for emulation. + This API adds a memory region that can be used by emulation. + + @uc: handle returned by uc_open() + @address: starting address of the new memory region to be mapped in. + This address must be aligned to 4KB, or this will return with UC_ERR_ARG + error. + @size: size of the new memory region to be mapped in. + This size must be a multiple of 4KB, or this will return with UC_ERR_ARG + error. + @perms: Permissions for the newly mapped region. + This must be some combination of UC_PROT_READ | UC_PROT_WRITE | + UC_PROT_EXEC, or this will return with UC_ERR_ARG error. + @ptr: pointer to host memory backing the newly mapped memory. This host memory + is expected to be an equal or larger size than provided, and be mapped with at + least PROT_READ | PROT_WRITE. If it is not, the resulting behavior is + undefined. + + @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum + for detailed error). +*/ +UNICORN_EXPORT +uc_err uc_mem_map_ptr(uc_engine *uc, uint64_t address, size_t size, + uint32_t perms, void *ptr); + +/* + Map MMIO in for emulation. + This API adds a MMIO region that can be used by emulation. + + @uc: handle returned by uc_open() + @address: starting address of the new MMIO region to be mapped in. + This address must be aligned to 4KB, or this will return with UC_ERR_ARG + error. + @size: size of the new MMIO region to be mapped in. + This size must be multiple of 4KB, or this will return with UC_ERR_ARG error. + @read_cb: function for handling reads from this MMIO region. + @user_data_read: user-defined data. This will be passed to @read_cb function in + its last argument @user_data + @write_cb: function for handling writes to this MMIO region. + @user_data_write: user-defined data. This will be passed to @write_cb function + in its last argument @user_data + @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum + for detailed error). + */ +UNICORN_EXPORT +uc_err uc_mmio_map(uc_engine *uc, uint64_t address, size_t size, + uc_cb_mmio_read_t read_cb, void *user_data_read, + uc_cb_mmio_write_t write_cb, void *user_data_write); + +/* + Unmap a region of emulation memory. + This API deletes a memory mapping from the emulation memory space. + + @uc: handle returned by uc_open() + @address: starting address of the memory region to be unmapped. + This address must be aligned to 4KB, or this will return with UC_ERR_ARG + error. + @size: size of the memory region to be modified. + This size must be a multiple of 4KB, or this will return with UC_ERR_ARG + error. + + @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum + for detailed error). +*/ +UNICORN_EXPORT +uc_err uc_mem_unmap(uc_engine *uc, uint64_t address, size_t size); + +/* + Set memory permissions for emulation memory. + This API changes permissions on an existing memory region. + + @uc: handle returned by uc_open() + @address: starting address of the memory region to be modified. + This address must be aligned to 4KB, or this will return with UC_ERR_ARG + error. + @size: size of the memory region to be modified. + This size must be a multiple of 4KB, or this will return with UC_ERR_ARG + error. + @perms: New permissions for the mapped region. + This must be some combination of UC_PROT_READ | UC_PROT_WRITE | + UC_PROT_EXEC, or this will return with UC_ERR_ARG error. + + @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum + for detailed error). +*/ +UNICORN_EXPORT +uc_err uc_mem_protect(uc_engine *uc, uint64_t address, size_t size, + uint32_t perms); + +/* + Retrieve all memory regions mapped by uc_mem_map() and uc_mem_map_ptr() + This API allocates memory for @regions, and user must free this memory later + by uc_free() to avoid leaking memory. + NOTE: memory regions may be split by uc_mem_unmap() + + @uc: handle returned by uc_open() + @regions: pointer to an array of uc_mem_region struct. This is allocated by + Unicorn, and must be freed by user later with uc_free() + @count: pointer to number of struct uc_mem_region contained in @regions + + @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum + for detailed error). +*/ +UNICORN_EXPORT +uc_err uc_mem_regions(uc_engine *uc, uc_mem_region **regions, uint32_t *count); + +/* + Allocate a region that can be used with uc_context_{save,restore} to perform + quick save/rollback of the CPU context, which includes registers and some + internal metadata. Contexts may not be shared across engine instances with + differing arches or modes. + + @uc: handle returned by uc_open() + @context: pointer to a uc_context*. This will be updated with the pointer to + the new context on successful return of this function. + Later, this allocated memory must be freed with uc_context_free(). + + @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum + for detailed error). +*/ +UNICORN_EXPORT +uc_err uc_context_alloc(uc_engine *uc, uc_context **context); + +/* + Free the memory allocated by uc_mem_regions. + WARNING: After Unicorn 1.0.1rc5, the memory allocated by uc_context_alloc + should be freed by uc_context_free(). Calling uc_free() may still work, but + the result is **undefined**. + + @mem: memory allocated by uc_mem_regions (returned in *regions). + + @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum + for detailed error). +*/ +UNICORN_EXPORT +uc_err uc_free(void *mem); + +/* + Save a copy of the internal CPU context. + This API should be used to efficiently make or update a saved copy of the + internal CPU state. + + @uc: handle returned by uc_open() + @context: handle returned by uc_context_alloc() + + @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum + for detailed error). +*/ +UNICORN_EXPORT +uc_err uc_context_save(uc_engine *uc, uc_context *context); + +/* + Write value to a register of a context. + + @ctx: handle returned by uc_context_alloc() + @regid: register ID that is to be modified. + @value: pointer to the value that will set to register @regid + + @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum + for detailed error). +*/ +UNICORN_EXPORT +uc_err uc_context_reg_write(uc_context *ctx, int regid, const void *value); + +/* + Read register value from a context. + + @ctx: handle returned by uc_context_alloc() + @regid: register ID that is to be retrieved. + @value: pointer to a variable storing the register value. + + @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum + for detailed error). +*/ +UNICORN_EXPORT +uc_err uc_context_reg_read(uc_context *ctx, int regid, void *value); + +/* + Write multiple register values to registers of a context. + + @ctx: handle returned by uc_context_alloc() + @regs: array of register IDs to store + @value: pointer to array of register values + @count: length of both *regs and *vals + + @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum + for detailed error). +*/ +UNICORN_EXPORT +uc_err uc_context_reg_write_batch(uc_context *ctx, int *regs, void *const *vals, + int count); + +/* + Read multiple register values from a context. + + @ctx: handle returned by uc_context_alloc() + @regs: array of register IDs to retrieve + @value: pointer to array of values to hold registers + @count: length of both *regs and *vals + + @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum + for detailed error). +*/ +UNICORN_EXPORT +uc_err uc_context_reg_read_batch(uc_context *ctx, int *regs, void **vals, + int count); + +/* + Restore the current CPU context from a saved copy. + This API should be used to roll the CPU context back to a previous + state saved by uc_context_save(). + + @uc: handle returned by uc_open() + @context: handle returned by uc_context_alloc that has been used with + uc_context_save + + @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum + for detailed error). +*/ +UNICORN_EXPORT +uc_err uc_context_restore(uc_engine *uc, uc_context *context); + +/* + Return the size needed to store the cpu context. Can be used to allocate a + buffer to contain the cpu context and directly call uc_context_save. + + @uc: handle returned by uc_open() + + @return the size for needed to store the cpu context as as size_t. +*/ +UNICORN_EXPORT +size_t uc_context_size(uc_engine *uc); + +/* + Free the context allocated by uc_context_alloc(). + + @context: handle returned by uc_context_alloc() + + @return UC_ERR_OK on success, or other value on failure (refer to uc_err enum + for detailed error). +*/ +UNICORN_EXPORT +uc_err uc_context_free(uc_context *context); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/x86.h b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/x86.h new file mode 100644 index 0000000000..5191743133 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/include/unicorn/x86.h @@ -0,0 +1,1681 @@ +/* Unicorn Emulator Engine */ +/* By Nguyen Anh Quynh , 2015-2017 */ +/* This file is released under LGPL2. + See COPYING.LGPL2 in root directory for more details +*/ + +#ifndef UNICORN_X86_H +#define UNICORN_X86_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "platform.h" + +//> X86 CPU +typedef enum uc_cpu_x86 { + UC_CPU_X86_QEMU64 = 0, + UC_CPU_X86_PHENOM, + UC_CPU_X86_CORE2DUO, + UC_CPU_X86_KVM64, + UC_CPU_X86_QEMU32, + UC_CPU_X86_KVM32, + UC_CPU_X86_COREDUO, + UC_CPU_X86_486, + UC_CPU_X86_PENTIUM, + UC_CPU_X86_PENTIUM2, + UC_CPU_X86_PENTIUM3, + UC_CPU_X86_ATHLON, + UC_CPU_X86_N270, + UC_CPU_X86_CONROE, + UC_CPU_X86_PENRYN, + UC_CPU_X86_NEHALEM, + UC_CPU_X86_WESTMERE, + UC_CPU_X86_SANDYBRIDGE, + UC_CPU_X86_IVYBRIDGE, + UC_CPU_X86_HASWELL, + UC_CPU_X86_BROADWELL, + UC_CPU_X86_SKYLAKE_CLIENT, + UC_CPU_X86_SKYLAKE_SERVER, + UC_CPU_X86_CASCADELAKE_SERVER, + UC_CPU_X86_COOPERLAKE, + UC_CPU_X86_ICELAKE_CLIENT, + UC_CPU_X86_ICELAKE_SERVER, + UC_CPU_X86_DENVERTON, + UC_CPU_X86_SNOWRIDGE, + UC_CPU_X86_KNIGHTSMILL, + UC_CPU_X86_OPTERON_G1, + UC_CPU_X86_OPTERON_G2, + UC_CPU_X86_OPTERON_G3, + UC_CPU_X86_OPTERON_G4, + UC_CPU_X86_OPTERON_G5, + UC_CPU_X86_EPYC, + UC_CPU_X86_DHYANA, + UC_CPU_X86_EPYC_ROME, + + UC_CPU_X86_ENDING +} uc_cpu_x86; + +// Memory-Management Register for instructions IDTR, GDTR, LDTR, TR. +// Borrow from SegmentCache in qemu/target-i386/cpu.h +typedef struct uc_x86_mmr { + uint16_t selector; /* not used by GDTR and IDTR */ + uint64_t base; /* handle 32 or 64 bit CPUs */ + uint32_t limit; + uint32_t flags; /* not used by GDTR and IDTR */ +} uc_x86_mmr; + +// Model-Specific Register structure, use this with UC_X86_REG_MSR (as the +// register ID) in call to uc_reg_write/uc_reg_read() to manipulate MSRs. +typedef struct uc_x86_msr { + uint32_t rid; // MSR id + uint64_t value; // MSR value +} uc_x86_msr; + +// Callback function for tracing SYSCALL/SYSENTER (for uc_hook_intr()) +// @user_data: user data passed to tracing APIs. +typedef void (*uc_cb_insn_syscall_t)(struct uc_struct *uc, void *user_data); + +// Callback function for tracing cpuid (for uc_hook_intr()) +// @user_data: user data passed to tracing APIs. +// +// @return: true indicates the callback overwrites the cpuid instruction while +// false +// indicates cpuid instruction will still be executed. +typedef int (*uc_cb_insn_cpuid_t)(struct uc_struct *uc, void *user_data); + +//> X86 registers +typedef enum uc_x86_reg { + UC_X86_REG_INVALID = 0, + UC_X86_REG_AH, + UC_X86_REG_AL, + UC_X86_REG_AX, + UC_X86_REG_BH, + UC_X86_REG_BL, + UC_X86_REG_BP, + UC_X86_REG_BPL, + UC_X86_REG_BX, + UC_X86_REG_CH, + UC_X86_REG_CL, + UC_X86_REG_CS, + UC_X86_REG_CX, + UC_X86_REG_DH, + UC_X86_REG_DI, + UC_X86_REG_DIL, + UC_X86_REG_DL, + UC_X86_REG_DS, + UC_X86_REG_DX, + UC_X86_REG_EAX, + UC_X86_REG_EBP, + UC_X86_REG_EBX, + UC_X86_REG_ECX, + UC_X86_REG_EDI, + UC_X86_REG_EDX, + UC_X86_REG_EFLAGS, + UC_X86_REG_EIP, + UC_X86_REG_ES = UC_X86_REG_EIP + 2, + UC_X86_REG_ESI, + UC_X86_REG_ESP, + UC_X86_REG_FPSW, + UC_X86_REG_FS, + UC_X86_REG_GS, + UC_X86_REG_IP, + UC_X86_REG_RAX, + UC_X86_REG_RBP, + UC_X86_REG_RBX, + UC_X86_REG_RCX, + UC_X86_REG_RDI, + UC_X86_REG_RDX, + UC_X86_REG_RIP, + UC_X86_REG_RSI = UC_X86_REG_RIP + 2, + UC_X86_REG_RSP, + UC_X86_REG_SI, + UC_X86_REG_SIL, + UC_X86_REG_SP, + UC_X86_REG_SPL, + UC_X86_REG_SS, + UC_X86_REG_CR0, + UC_X86_REG_CR1, + UC_X86_REG_CR2, + UC_X86_REG_CR3, + UC_X86_REG_CR4, + UC_X86_REG_CR8 = UC_X86_REG_CR4 + 4, + UC_X86_REG_DR0 = UC_X86_REG_CR8 + 8, + UC_X86_REG_DR1, + UC_X86_REG_DR2, + UC_X86_REG_DR3, + UC_X86_REG_DR4, + UC_X86_REG_DR5, + UC_X86_REG_DR6, + UC_X86_REG_DR7, + UC_X86_REG_FP0 = UC_X86_REG_DR7 + 9, + UC_X86_REG_FP1, + UC_X86_REG_FP2, + UC_X86_REG_FP3, + UC_X86_REG_FP4, + UC_X86_REG_FP5, + UC_X86_REG_FP6, + UC_X86_REG_FP7, + UC_X86_REG_K0, + UC_X86_REG_K1, + UC_X86_REG_K2, + UC_X86_REG_K3, + UC_X86_REG_K4, + UC_X86_REG_K5, + UC_X86_REG_K6, + UC_X86_REG_K7, + UC_X86_REG_MM0, + UC_X86_REG_MM1, + UC_X86_REG_MM2, + UC_X86_REG_MM3, + UC_X86_REG_MM4, + UC_X86_REG_MM5, + UC_X86_REG_MM6, + UC_X86_REG_MM7, + UC_X86_REG_R8, + UC_X86_REG_R9, + UC_X86_REG_R10, + UC_X86_REG_R11, + UC_X86_REG_R12, + UC_X86_REG_R13, + UC_X86_REG_R14, + UC_X86_REG_R15, + UC_X86_REG_ST0, + UC_X86_REG_ST1, + UC_X86_REG_ST2, + UC_X86_REG_ST3, + UC_X86_REG_ST4, + UC_X86_REG_ST5, + UC_X86_REG_ST6, + UC_X86_REG_ST7, + UC_X86_REG_XMM0, + UC_X86_REG_XMM1, + UC_X86_REG_XMM2, + UC_X86_REG_XMM3, + UC_X86_REG_XMM4, + UC_X86_REG_XMM5, + UC_X86_REG_XMM6, + UC_X86_REG_XMM7, + UC_X86_REG_XMM8, + UC_X86_REG_XMM9, + UC_X86_REG_XMM10, + UC_X86_REG_XMM11, + UC_X86_REG_XMM12, + UC_X86_REG_XMM13, + UC_X86_REG_XMM14, + UC_X86_REG_XMM15, + UC_X86_REG_XMM16, + UC_X86_REG_XMM17, + UC_X86_REG_XMM18, + UC_X86_REG_XMM19, + UC_X86_REG_XMM20, + UC_X86_REG_XMM21, + UC_X86_REG_XMM22, + UC_X86_REG_XMM23, + UC_X86_REG_XMM24, + UC_X86_REG_XMM25, + UC_X86_REG_XMM26, + UC_X86_REG_XMM27, + UC_X86_REG_XMM28, + UC_X86_REG_XMM29, + UC_X86_REG_XMM30, + UC_X86_REG_XMM31, + UC_X86_REG_YMM0, + UC_X86_REG_YMM1, + UC_X86_REG_YMM2, + UC_X86_REG_YMM3, + UC_X86_REG_YMM4, + UC_X86_REG_YMM5, + UC_X86_REG_YMM6, + UC_X86_REG_YMM7, + UC_X86_REG_YMM8, + UC_X86_REG_YMM9, + UC_X86_REG_YMM10, + UC_X86_REG_YMM11, + UC_X86_REG_YMM12, + UC_X86_REG_YMM13, + UC_X86_REG_YMM14, + UC_X86_REG_YMM15, + UC_X86_REG_YMM16, + UC_X86_REG_YMM17, + UC_X86_REG_YMM18, + UC_X86_REG_YMM19, + UC_X86_REG_YMM20, + UC_X86_REG_YMM21, + UC_X86_REG_YMM22, + UC_X86_REG_YMM23, + UC_X86_REG_YMM24, + UC_X86_REG_YMM25, + UC_X86_REG_YMM26, + UC_X86_REG_YMM27, + UC_X86_REG_YMM28, + UC_X86_REG_YMM29, + UC_X86_REG_YMM30, + UC_X86_REG_YMM31, + UC_X86_REG_ZMM0, + UC_X86_REG_ZMM1, + UC_X86_REG_ZMM2, + UC_X86_REG_ZMM3, + UC_X86_REG_ZMM4, + UC_X86_REG_ZMM5, + UC_X86_REG_ZMM6, + UC_X86_REG_ZMM7, + UC_X86_REG_ZMM8, + UC_X86_REG_ZMM9, + UC_X86_REG_ZMM10, + UC_X86_REG_ZMM11, + UC_X86_REG_ZMM12, + UC_X86_REG_ZMM13, + UC_X86_REG_ZMM14, + UC_X86_REG_ZMM15, + UC_X86_REG_ZMM16, + UC_X86_REG_ZMM17, + UC_X86_REG_ZMM18, + UC_X86_REG_ZMM19, + UC_X86_REG_ZMM20, + UC_X86_REG_ZMM21, + UC_X86_REG_ZMM22, + UC_X86_REG_ZMM23, + UC_X86_REG_ZMM24, + UC_X86_REG_ZMM25, + UC_X86_REG_ZMM26, + UC_X86_REG_ZMM27, + UC_X86_REG_ZMM28, + UC_X86_REG_ZMM29, + UC_X86_REG_ZMM30, + UC_X86_REG_ZMM31, + UC_X86_REG_R8B, + UC_X86_REG_R9B, + UC_X86_REG_R10B, + UC_X86_REG_R11B, + UC_X86_REG_R12B, + UC_X86_REG_R13B, + UC_X86_REG_R14B, + UC_X86_REG_R15B, + UC_X86_REG_R8D, + UC_X86_REG_R9D, + UC_X86_REG_R10D, + UC_X86_REG_R11D, + UC_X86_REG_R12D, + UC_X86_REG_R13D, + UC_X86_REG_R14D, + UC_X86_REG_R15D, + UC_X86_REG_R8W, + UC_X86_REG_R9W, + UC_X86_REG_R10W, + UC_X86_REG_R11W, + UC_X86_REG_R12W, + UC_X86_REG_R13W, + UC_X86_REG_R14W, + UC_X86_REG_R15W, + UC_X86_REG_IDTR, + UC_X86_REG_GDTR, + UC_X86_REG_LDTR, + UC_X86_REG_TR, + UC_X86_REG_FPCW, + UC_X86_REG_FPTAG, + UC_X86_REG_MSR, // Model-Specific Register + UC_X86_REG_MXCSR, + UC_X86_REG_FS_BASE, // Base regs for x86_64 + UC_X86_REG_GS_BASE, + UC_X86_REG_FLAGS, + UC_X86_REG_RFLAGS, + UC_X86_REG_FIP, + UC_X86_REG_FCS, + UC_X86_REG_FDP, + UC_X86_REG_FDS, + UC_X86_REG_FOP, + UC_X86_REG_ENDING // <-- mark the end of the list of registers +} uc_x86_reg; + +//> X86 instructions +typedef enum uc_x86_insn { + UC_X86_INS_INVALID = 0, + + UC_X86_INS_AAA, + UC_X86_INS_AAD, + UC_X86_INS_AAM, + UC_X86_INS_AAS, + UC_X86_INS_FABS, + UC_X86_INS_ADC, + UC_X86_INS_ADCX, + UC_X86_INS_ADD, + UC_X86_INS_ADDPD, + UC_X86_INS_ADDPS, + UC_X86_INS_ADDSD, + UC_X86_INS_ADDSS, + UC_X86_INS_ADDSUBPD, + UC_X86_INS_ADDSUBPS, + UC_X86_INS_FADD, + UC_X86_INS_FIADD, + UC_X86_INS_FADDP, + UC_X86_INS_ADOX, + UC_X86_INS_AESDECLAST, + UC_X86_INS_AESDEC, + UC_X86_INS_AESENCLAST, + UC_X86_INS_AESENC, + UC_X86_INS_AESIMC, + UC_X86_INS_AESKEYGENASSIST, + UC_X86_INS_AND, + UC_X86_INS_ANDN, + UC_X86_INS_ANDNPD, + UC_X86_INS_ANDNPS, + UC_X86_INS_ANDPD, + UC_X86_INS_ANDPS, + UC_X86_INS_ARPL, + UC_X86_INS_BEXTR, + UC_X86_INS_BLCFILL, + UC_X86_INS_BLCI, + UC_X86_INS_BLCIC, + UC_X86_INS_BLCMSK, + UC_X86_INS_BLCS, + UC_X86_INS_BLENDPD, + UC_X86_INS_BLENDPS, + UC_X86_INS_BLENDVPD, + UC_X86_INS_BLENDVPS, + UC_X86_INS_BLSFILL, + UC_X86_INS_BLSI, + UC_X86_INS_BLSIC, + UC_X86_INS_BLSMSK, + UC_X86_INS_BLSR, + UC_X86_INS_BOUND, + UC_X86_INS_BSF, + UC_X86_INS_BSR, + UC_X86_INS_BSWAP, + UC_X86_INS_BT, + UC_X86_INS_BTC, + UC_X86_INS_BTR, + UC_X86_INS_BTS, + UC_X86_INS_BZHI, + UC_X86_INS_CALL, + UC_X86_INS_CBW, + UC_X86_INS_CDQ, + UC_X86_INS_CDQE, + UC_X86_INS_FCHS, + UC_X86_INS_CLAC, + UC_X86_INS_CLC, + UC_X86_INS_CLD, + UC_X86_INS_CLFLUSH, + UC_X86_INS_CLFLUSHOPT, + UC_X86_INS_CLGI, + UC_X86_INS_CLI, + UC_X86_INS_CLTS, + UC_X86_INS_CLWB, + UC_X86_INS_CMC, + UC_X86_INS_CMOVA, + UC_X86_INS_CMOVAE, + UC_X86_INS_CMOVB, + UC_X86_INS_CMOVBE, + UC_X86_INS_FCMOVBE, + UC_X86_INS_FCMOVB, + UC_X86_INS_CMOVE, + UC_X86_INS_FCMOVE, + UC_X86_INS_CMOVG, + UC_X86_INS_CMOVGE, + UC_X86_INS_CMOVL, + UC_X86_INS_CMOVLE, + UC_X86_INS_FCMOVNBE, + UC_X86_INS_FCMOVNB, + UC_X86_INS_CMOVNE, + UC_X86_INS_FCMOVNE, + UC_X86_INS_CMOVNO, + UC_X86_INS_CMOVNP, + UC_X86_INS_FCMOVNU, + UC_X86_INS_CMOVNS, + UC_X86_INS_CMOVO, + UC_X86_INS_CMOVP, + UC_X86_INS_FCMOVU, + UC_X86_INS_CMOVS, + UC_X86_INS_CMP, + UC_X86_INS_CMPPD, + UC_X86_INS_CMPPS, + UC_X86_INS_CMPSB, + UC_X86_INS_CMPSD, + UC_X86_INS_CMPSQ, + UC_X86_INS_CMPSS, + UC_X86_INS_CMPSW, + UC_X86_INS_CMPXCHG16B, + UC_X86_INS_CMPXCHG, + UC_X86_INS_CMPXCHG8B, + UC_X86_INS_COMISD, + UC_X86_INS_COMISS, + UC_X86_INS_FCOMP, + UC_X86_INS_FCOMPI, + UC_X86_INS_FCOMI, + UC_X86_INS_FCOM, + UC_X86_INS_FCOS, + UC_X86_INS_CPUID, + UC_X86_INS_CQO, + UC_X86_INS_CRC32, + UC_X86_INS_CVTDQ2PD, + UC_X86_INS_CVTDQ2PS, + UC_X86_INS_CVTPD2DQ, + UC_X86_INS_CVTPD2PS, + UC_X86_INS_CVTPS2DQ, + UC_X86_INS_CVTPS2PD, + UC_X86_INS_CVTSD2SI, + UC_X86_INS_CVTSD2SS, + UC_X86_INS_CVTSI2SD, + UC_X86_INS_CVTSI2SS, + UC_X86_INS_CVTSS2SD, + UC_X86_INS_CVTSS2SI, + UC_X86_INS_CVTTPD2DQ, + UC_X86_INS_CVTTPS2DQ, + UC_X86_INS_CVTTSD2SI, + UC_X86_INS_CVTTSS2SI, + UC_X86_INS_CWD, + UC_X86_INS_CWDE, + UC_X86_INS_DAA, + UC_X86_INS_DAS, + UC_X86_INS_DATA16, + UC_X86_INS_DEC, + UC_X86_INS_DIV, + UC_X86_INS_DIVPD, + UC_X86_INS_DIVPS, + UC_X86_INS_FDIVR, + UC_X86_INS_FIDIVR, + UC_X86_INS_FDIVRP, + UC_X86_INS_DIVSD, + UC_X86_INS_DIVSS, + UC_X86_INS_FDIV, + UC_X86_INS_FIDIV, + UC_X86_INS_FDIVP, + UC_X86_INS_DPPD, + UC_X86_INS_DPPS, + UC_X86_INS_RET, + UC_X86_INS_ENCLS, + UC_X86_INS_ENCLU, + UC_X86_INS_ENTER, + UC_X86_INS_EXTRACTPS, + UC_X86_INS_EXTRQ, + UC_X86_INS_F2XM1, + UC_X86_INS_LCALL, + UC_X86_INS_LJMP, + UC_X86_INS_FBLD, + UC_X86_INS_FBSTP, + UC_X86_INS_FCOMPP, + UC_X86_INS_FDECSTP, + UC_X86_INS_FEMMS, + UC_X86_INS_FFREE, + UC_X86_INS_FICOM, + UC_X86_INS_FICOMP, + UC_X86_INS_FINCSTP, + UC_X86_INS_FLDCW, + UC_X86_INS_FLDENV, + UC_X86_INS_FLDL2E, + UC_X86_INS_FLDL2T, + UC_X86_INS_FLDLG2, + UC_X86_INS_FLDLN2, + UC_X86_INS_FLDPI, + UC_X86_INS_FNCLEX, + UC_X86_INS_FNINIT, + UC_X86_INS_FNOP, + UC_X86_INS_FNSTCW, + UC_X86_INS_FNSTSW, + UC_X86_INS_FPATAN, + UC_X86_INS_FPREM, + UC_X86_INS_FPREM1, + UC_X86_INS_FPTAN, + UC_X86_INS_FFREEP, + UC_X86_INS_FRNDINT, + UC_X86_INS_FRSTOR, + UC_X86_INS_FNSAVE, + UC_X86_INS_FSCALE, + UC_X86_INS_FSETPM, + UC_X86_INS_FSINCOS, + UC_X86_INS_FNSTENV, + UC_X86_INS_FXAM, + UC_X86_INS_FXRSTOR, + UC_X86_INS_FXRSTOR64, + UC_X86_INS_FXSAVE, + UC_X86_INS_FXSAVE64, + UC_X86_INS_FXTRACT, + UC_X86_INS_FYL2X, + UC_X86_INS_FYL2XP1, + UC_X86_INS_MOVAPD, + UC_X86_INS_MOVAPS, + UC_X86_INS_ORPD, + UC_X86_INS_ORPS, + UC_X86_INS_VMOVAPD, + UC_X86_INS_VMOVAPS, + UC_X86_INS_XORPD, + UC_X86_INS_XORPS, + UC_X86_INS_GETSEC, + UC_X86_INS_HADDPD, + UC_X86_INS_HADDPS, + UC_X86_INS_HLT, + UC_X86_INS_HSUBPD, + UC_X86_INS_HSUBPS, + UC_X86_INS_IDIV, + UC_X86_INS_FILD, + UC_X86_INS_IMUL, + UC_X86_INS_IN, + UC_X86_INS_INC, + UC_X86_INS_INSB, + UC_X86_INS_INSERTPS, + UC_X86_INS_INSERTQ, + UC_X86_INS_INSD, + UC_X86_INS_INSW, + UC_X86_INS_INT, + UC_X86_INS_INT1, + UC_X86_INS_INT3, + UC_X86_INS_INTO, + UC_X86_INS_INVD, + UC_X86_INS_INVEPT, + UC_X86_INS_INVLPG, + UC_X86_INS_INVLPGA, + UC_X86_INS_INVPCID, + UC_X86_INS_INVVPID, + UC_X86_INS_IRET, + UC_X86_INS_IRETD, + UC_X86_INS_IRETQ, + UC_X86_INS_FISTTP, + UC_X86_INS_FIST, + UC_X86_INS_FISTP, + UC_X86_INS_UCOMISD, + UC_X86_INS_UCOMISS, + UC_X86_INS_VCOMISD, + UC_X86_INS_VCOMISS, + UC_X86_INS_VCVTSD2SS, + UC_X86_INS_VCVTSI2SD, + UC_X86_INS_VCVTSI2SS, + UC_X86_INS_VCVTSS2SD, + UC_X86_INS_VCVTTSD2SI, + UC_X86_INS_VCVTTSD2USI, + UC_X86_INS_VCVTTSS2SI, + UC_X86_INS_VCVTTSS2USI, + UC_X86_INS_VCVTUSI2SD, + UC_X86_INS_VCVTUSI2SS, + UC_X86_INS_VUCOMISD, + UC_X86_INS_VUCOMISS, + UC_X86_INS_JAE, + UC_X86_INS_JA, + UC_X86_INS_JBE, + UC_X86_INS_JB, + UC_X86_INS_JCXZ, + UC_X86_INS_JECXZ, + UC_X86_INS_JE, + UC_X86_INS_JGE, + UC_X86_INS_JG, + UC_X86_INS_JLE, + UC_X86_INS_JL, + UC_X86_INS_JMP, + UC_X86_INS_JNE, + UC_X86_INS_JNO, + UC_X86_INS_JNP, + UC_X86_INS_JNS, + UC_X86_INS_JO, + UC_X86_INS_JP, + UC_X86_INS_JRCXZ, + UC_X86_INS_JS, + UC_X86_INS_KANDB, + UC_X86_INS_KANDD, + UC_X86_INS_KANDNB, + UC_X86_INS_KANDND, + UC_X86_INS_KANDNQ, + UC_X86_INS_KANDNW, + UC_X86_INS_KANDQ, + UC_X86_INS_KANDW, + UC_X86_INS_KMOVB, + UC_X86_INS_KMOVD, + UC_X86_INS_KMOVQ, + UC_X86_INS_KMOVW, + UC_X86_INS_KNOTB, + UC_X86_INS_KNOTD, + UC_X86_INS_KNOTQ, + UC_X86_INS_KNOTW, + UC_X86_INS_KORB, + UC_X86_INS_KORD, + UC_X86_INS_KORQ, + UC_X86_INS_KORTESTB, + UC_X86_INS_KORTESTD, + UC_X86_INS_KORTESTQ, + UC_X86_INS_KORTESTW, + UC_X86_INS_KORW, + UC_X86_INS_KSHIFTLB, + UC_X86_INS_KSHIFTLD, + UC_X86_INS_KSHIFTLQ, + UC_X86_INS_KSHIFTLW, + UC_X86_INS_KSHIFTRB, + UC_X86_INS_KSHIFTRD, + UC_X86_INS_KSHIFTRQ, + UC_X86_INS_KSHIFTRW, + UC_X86_INS_KUNPCKBW, + UC_X86_INS_KXNORB, + UC_X86_INS_KXNORD, + UC_X86_INS_KXNORQ, + UC_X86_INS_KXNORW, + UC_X86_INS_KXORB, + UC_X86_INS_KXORD, + UC_X86_INS_KXORQ, + UC_X86_INS_KXORW, + UC_X86_INS_LAHF, + UC_X86_INS_LAR, + UC_X86_INS_LDDQU, + UC_X86_INS_LDMXCSR, + UC_X86_INS_LDS, + UC_X86_INS_FLDZ, + UC_X86_INS_FLD1, + UC_X86_INS_FLD, + UC_X86_INS_LEA, + UC_X86_INS_LEAVE, + UC_X86_INS_LES, + UC_X86_INS_LFENCE, + UC_X86_INS_LFS, + UC_X86_INS_LGDT, + UC_X86_INS_LGS, + UC_X86_INS_LIDT, + UC_X86_INS_LLDT, + UC_X86_INS_LMSW, + UC_X86_INS_OR, + UC_X86_INS_SUB, + UC_X86_INS_XOR, + UC_X86_INS_LODSB, + UC_X86_INS_LODSD, + UC_X86_INS_LODSQ, + UC_X86_INS_LODSW, + UC_X86_INS_LOOP, + UC_X86_INS_LOOPE, + UC_X86_INS_LOOPNE, + UC_X86_INS_RETF, + UC_X86_INS_RETFQ, + UC_X86_INS_LSL, + UC_X86_INS_LSS, + UC_X86_INS_LTR, + UC_X86_INS_XADD, + UC_X86_INS_LZCNT, + UC_X86_INS_MASKMOVDQU, + UC_X86_INS_MAXPD, + UC_X86_INS_MAXPS, + UC_X86_INS_MAXSD, + UC_X86_INS_MAXSS, + UC_X86_INS_MFENCE, + UC_X86_INS_MINPD, + UC_X86_INS_MINPS, + UC_X86_INS_MINSD, + UC_X86_INS_MINSS, + UC_X86_INS_CVTPD2PI, + UC_X86_INS_CVTPI2PD, + UC_X86_INS_CVTPI2PS, + UC_X86_INS_CVTPS2PI, + UC_X86_INS_CVTTPD2PI, + UC_X86_INS_CVTTPS2PI, + UC_X86_INS_EMMS, + UC_X86_INS_MASKMOVQ, + UC_X86_INS_MOVD, + UC_X86_INS_MOVDQ2Q, + UC_X86_INS_MOVNTQ, + UC_X86_INS_MOVQ2DQ, + UC_X86_INS_MOVQ, + UC_X86_INS_PABSB, + UC_X86_INS_PABSD, + UC_X86_INS_PABSW, + UC_X86_INS_PACKSSDW, + UC_X86_INS_PACKSSWB, + UC_X86_INS_PACKUSWB, + UC_X86_INS_PADDB, + UC_X86_INS_PADDD, + UC_X86_INS_PADDQ, + UC_X86_INS_PADDSB, + UC_X86_INS_PADDSW, + UC_X86_INS_PADDUSB, + UC_X86_INS_PADDUSW, + UC_X86_INS_PADDW, + UC_X86_INS_PALIGNR, + UC_X86_INS_PANDN, + UC_X86_INS_PAND, + UC_X86_INS_PAVGB, + UC_X86_INS_PAVGW, + UC_X86_INS_PCMPEQB, + UC_X86_INS_PCMPEQD, + UC_X86_INS_PCMPEQW, + UC_X86_INS_PCMPGTB, + UC_X86_INS_PCMPGTD, + UC_X86_INS_PCMPGTW, + UC_X86_INS_PEXTRW, + UC_X86_INS_PHADDSW, + UC_X86_INS_PHADDW, + UC_X86_INS_PHADDD, + UC_X86_INS_PHSUBD, + UC_X86_INS_PHSUBSW, + UC_X86_INS_PHSUBW, + UC_X86_INS_PINSRW, + UC_X86_INS_PMADDUBSW, + UC_X86_INS_PMADDWD, + UC_X86_INS_PMAXSW, + UC_X86_INS_PMAXUB, + UC_X86_INS_PMINSW, + UC_X86_INS_PMINUB, + UC_X86_INS_PMOVMSKB, + UC_X86_INS_PMULHRSW, + UC_X86_INS_PMULHUW, + UC_X86_INS_PMULHW, + UC_X86_INS_PMULLW, + UC_X86_INS_PMULUDQ, + UC_X86_INS_POR, + UC_X86_INS_PSADBW, + UC_X86_INS_PSHUFB, + UC_X86_INS_PSHUFW, + UC_X86_INS_PSIGNB, + UC_X86_INS_PSIGND, + UC_X86_INS_PSIGNW, + UC_X86_INS_PSLLD, + UC_X86_INS_PSLLQ, + UC_X86_INS_PSLLW, + UC_X86_INS_PSRAD, + UC_X86_INS_PSRAW, + UC_X86_INS_PSRLD, + UC_X86_INS_PSRLQ, + UC_X86_INS_PSRLW, + UC_X86_INS_PSUBB, + UC_X86_INS_PSUBD, + UC_X86_INS_PSUBQ, + UC_X86_INS_PSUBSB, + UC_X86_INS_PSUBSW, + UC_X86_INS_PSUBUSB, + UC_X86_INS_PSUBUSW, + UC_X86_INS_PSUBW, + UC_X86_INS_PUNPCKHBW, + UC_X86_INS_PUNPCKHDQ, + UC_X86_INS_PUNPCKHWD, + UC_X86_INS_PUNPCKLBW, + UC_X86_INS_PUNPCKLDQ, + UC_X86_INS_PUNPCKLWD, + UC_X86_INS_PXOR, + UC_X86_INS_MONITOR, + UC_X86_INS_MONTMUL, + UC_X86_INS_MOV, + UC_X86_INS_MOVABS, + UC_X86_INS_MOVBE, + UC_X86_INS_MOVDDUP, + UC_X86_INS_MOVDQA, + UC_X86_INS_MOVDQU, + UC_X86_INS_MOVHLPS, + UC_X86_INS_MOVHPD, + UC_X86_INS_MOVHPS, + UC_X86_INS_MOVLHPS, + UC_X86_INS_MOVLPD, + UC_X86_INS_MOVLPS, + UC_X86_INS_MOVMSKPD, + UC_X86_INS_MOVMSKPS, + UC_X86_INS_MOVNTDQA, + UC_X86_INS_MOVNTDQ, + UC_X86_INS_MOVNTI, + UC_X86_INS_MOVNTPD, + UC_X86_INS_MOVNTPS, + UC_X86_INS_MOVNTSD, + UC_X86_INS_MOVNTSS, + UC_X86_INS_MOVSB, + UC_X86_INS_MOVSD, + UC_X86_INS_MOVSHDUP, + UC_X86_INS_MOVSLDUP, + UC_X86_INS_MOVSQ, + UC_X86_INS_MOVSS, + UC_X86_INS_MOVSW, + UC_X86_INS_MOVSX, + UC_X86_INS_MOVSXD, + UC_X86_INS_MOVUPD, + UC_X86_INS_MOVUPS, + UC_X86_INS_MOVZX, + UC_X86_INS_MPSADBW, + UC_X86_INS_MUL, + UC_X86_INS_MULPD, + UC_X86_INS_MULPS, + UC_X86_INS_MULSD, + UC_X86_INS_MULSS, + UC_X86_INS_MULX, + UC_X86_INS_FMUL, + UC_X86_INS_FIMUL, + UC_X86_INS_FMULP, + UC_X86_INS_MWAIT, + UC_X86_INS_NEG, + UC_X86_INS_NOP, + UC_X86_INS_NOT, + UC_X86_INS_OUT, + UC_X86_INS_OUTSB, + UC_X86_INS_OUTSD, + UC_X86_INS_OUTSW, + UC_X86_INS_PACKUSDW, + UC_X86_INS_PAUSE, + UC_X86_INS_PAVGUSB, + UC_X86_INS_PBLENDVB, + UC_X86_INS_PBLENDW, + UC_X86_INS_PCLMULQDQ, + UC_X86_INS_PCMPEQQ, + UC_X86_INS_PCMPESTRI, + UC_X86_INS_PCMPESTRM, + UC_X86_INS_PCMPGTQ, + UC_X86_INS_PCMPISTRI, + UC_X86_INS_PCMPISTRM, + UC_X86_INS_PCOMMIT, + UC_X86_INS_PDEP, + UC_X86_INS_PEXT, + UC_X86_INS_PEXTRB, + UC_X86_INS_PEXTRD, + UC_X86_INS_PEXTRQ, + UC_X86_INS_PF2ID, + UC_X86_INS_PF2IW, + UC_X86_INS_PFACC, + UC_X86_INS_PFADD, + UC_X86_INS_PFCMPEQ, + UC_X86_INS_PFCMPGE, + UC_X86_INS_PFCMPGT, + UC_X86_INS_PFMAX, + UC_X86_INS_PFMIN, + UC_X86_INS_PFMUL, + UC_X86_INS_PFNACC, + UC_X86_INS_PFPNACC, + UC_X86_INS_PFRCPIT1, + UC_X86_INS_PFRCPIT2, + UC_X86_INS_PFRCP, + UC_X86_INS_PFRSQIT1, + UC_X86_INS_PFRSQRT, + UC_X86_INS_PFSUBR, + UC_X86_INS_PFSUB, + UC_X86_INS_PHMINPOSUW, + UC_X86_INS_PI2FD, + UC_X86_INS_PI2FW, + UC_X86_INS_PINSRB, + UC_X86_INS_PINSRD, + UC_X86_INS_PINSRQ, + UC_X86_INS_PMAXSB, + UC_X86_INS_PMAXSD, + UC_X86_INS_PMAXUD, + UC_X86_INS_PMAXUW, + UC_X86_INS_PMINSB, + UC_X86_INS_PMINSD, + UC_X86_INS_PMINUD, + UC_X86_INS_PMINUW, + UC_X86_INS_PMOVSXBD, + UC_X86_INS_PMOVSXBQ, + UC_X86_INS_PMOVSXBW, + UC_X86_INS_PMOVSXDQ, + UC_X86_INS_PMOVSXWD, + UC_X86_INS_PMOVSXWQ, + UC_X86_INS_PMOVZXBD, + UC_X86_INS_PMOVZXBQ, + UC_X86_INS_PMOVZXBW, + UC_X86_INS_PMOVZXDQ, + UC_X86_INS_PMOVZXWD, + UC_X86_INS_PMOVZXWQ, + UC_X86_INS_PMULDQ, + UC_X86_INS_PMULHRW, + UC_X86_INS_PMULLD, + UC_X86_INS_POP, + UC_X86_INS_POPAW, + UC_X86_INS_POPAL, + UC_X86_INS_POPCNT, + UC_X86_INS_POPF, + UC_X86_INS_POPFD, + UC_X86_INS_POPFQ, + UC_X86_INS_PREFETCH, + UC_X86_INS_PREFETCHNTA, + UC_X86_INS_PREFETCHT0, + UC_X86_INS_PREFETCHT1, + UC_X86_INS_PREFETCHT2, + UC_X86_INS_PREFETCHW, + UC_X86_INS_PSHUFD, + UC_X86_INS_PSHUFHW, + UC_X86_INS_PSHUFLW, + UC_X86_INS_PSLLDQ, + UC_X86_INS_PSRLDQ, + UC_X86_INS_PSWAPD, + UC_X86_INS_PTEST, + UC_X86_INS_PUNPCKHQDQ, + UC_X86_INS_PUNPCKLQDQ, + UC_X86_INS_PUSH, + UC_X86_INS_PUSHAW, + UC_X86_INS_PUSHAL, + UC_X86_INS_PUSHF, + UC_X86_INS_PUSHFD, + UC_X86_INS_PUSHFQ, + UC_X86_INS_RCL, + UC_X86_INS_RCPPS, + UC_X86_INS_RCPSS, + UC_X86_INS_RCR, + UC_X86_INS_RDFSBASE, + UC_X86_INS_RDGSBASE, + UC_X86_INS_RDMSR, + UC_X86_INS_RDPMC, + UC_X86_INS_RDRAND, + UC_X86_INS_RDSEED, + UC_X86_INS_RDTSC, + UC_X86_INS_RDTSCP, + UC_X86_INS_ROL, + UC_X86_INS_ROR, + UC_X86_INS_RORX, + UC_X86_INS_ROUNDPD, + UC_X86_INS_ROUNDPS, + UC_X86_INS_ROUNDSD, + UC_X86_INS_ROUNDSS, + UC_X86_INS_RSM, + UC_X86_INS_RSQRTPS, + UC_X86_INS_RSQRTSS, + UC_X86_INS_SAHF, + UC_X86_INS_SAL, + UC_X86_INS_SALC, + UC_X86_INS_SAR, + UC_X86_INS_SARX, + UC_X86_INS_SBB, + UC_X86_INS_SCASB, + UC_X86_INS_SCASD, + UC_X86_INS_SCASQ, + UC_X86_INS_SCASW, + UC_X86_INS_SETAE, + UC_X86_INS_SETA, + UC_X86_INS_SETBE, + UC_X86_INS_SETB, + UC_X86_INS_SETE, + UC_X86_INS_SETGE, + UC_X86_INS_SETG, + UC_X86_INS_SETLE, + UC_X86_INS_SETL, + UC_X86_INS_SETNE, + UC_X86_INS_SETNO, + UC_X86_INS_SETNP, + UC_X86_INS_SETNS, + UC_X86_INS_SETO, + UC_X86_INS_SETP, + UC_X86_INS_SETS, + UC_X86_INS_SFENCE, + UC_X86_INS_SGDT, + UC_X86_INS_SHA1MSG1, + UC_X86_INS_SHA1MSG2, + UC_X86_INS_SHA1NEXTE, + UC_X86_INS_SHA1RNDS4, + UC_X86_INS_SHA256MSG1, + UC_X86_INS_SHA256MSG2, + UC_X86_INS_SHA256RNDS2, + UC_X86_INS_SHL, + UC_X86_INS_SHLD, + UC_X86_INS_SHLX, + UC_X86_INS_SHR, + UC_X86_INS_SHRD, + UC_X86_INS_SHRX, + UC_X86_INS_SHUFPD, + UC_X86_INS_SHUFPS, + UC_X86_INS_SIDT, + UC_X86_INS_FSIN, + UC_X86_INS_SKINIT, + UC_X86_INS_SLDT, + UC_X86_INS_SMSW, + UC_X86_INS_SQRTPD, + UC_X86_INS_SQRTPS, + UC_X86_INS_SQRTSD, + UC_X86_INS_SQRTSS, + UC_X86_INS_FSQRT, + UC_X86_INS_STAC, + UC_X86_INS_STC, + UC_X86_INS_STD, + UC_X86_INS_STGI, + UC_X86_INS_STI, + UC_X86_INS_STMXCSR, + UC_X86_INS_STOSB, + UC_X86_INS_STOSD, + UC_X86_INS_STOSQ, + UC_X86_INS_STOSW, + UC_X86_INS_STR, + UC_X86_INS_FST, + UC_X86_INS_FSTP, + UC_X86_INS_FSTPNCE, + UC_X86_INS_FXCH, + UC_X86_INS_SUBPD, + UC_X86_INS_SUBPS, + UC_X86_INS_FSUBR, + UC_X86_INS_FISUBR, + UC_X86_INS_FSUBRP, + UC_X86_INS_SUBSD, + UC_X86_INS_SUBSS, + UC_X86_INS_FSUB, + UC_X86_INS_FISUB, + UC_X86_INS_FSUBP, + UC_X86_INS_SWAPGS, + UC_X86_INS_SYSCALL, + UC_X86_INS_SYSENTER, + UC_X86_INS_SYSEXIT, + UC_X86_INS_SYSRET, + UC_X86_INS_T1MSKC, + UC_X86_INS_TEST, + UC_X86_INS_UD2, + UC_X86_INS_FTST, + UC_X86_INS_TZCNT, + UC_X86_INS_TZMSK, + UC_X86_INS_FUCOMPI, + UC_X86_INS_FUCOMI, + UC_X86_INS_FUCOMPP, + UC_X86_INS_FUCOMP, + UC_X86_INS_FUCOM, + UC_X86_INS_UD2B, + UC_X86_INS_UNPCKHPD, + UC_X86_INS_UNPCKHPS, + UC_X86_INS_UNPCKLPD, + UC_X86_INS_UNPCKLPS, + UC_X86_INS_VADDPD, + UC_X86_INS_VADDPS, + UC_X86_INS_VADDSD, + UC_X86_INS_VADDSS, + UC_X86_INS_VADDSUBPD, + UC_X86_INS_VADDSUBPS, + UC_X86_INS_VAESDECLAST, + UC_X86_INS_VAESDEC, + UC_X86_INS_VAESENCLAST, + UC_X86_INS_VAESENC, + UC_X86_INS_VAESIMC, + UC_X86_INS_VAESKEYGENASSIST, + UC_X86_INS_VALIGND, + UC_X86_INS_VALIGNQ, + UC_X86_INS_VANDNPD, + UC_X86_INS_VANDNPS, + UC_X86_INS_VANDPD, + UC_X86_INS_VANDPS, + UC_X86_INS_VBLENDMPD, + UC_X86_INS_VBLENDMPS, + UC_X86_INS_VBLENDPD, + UC_X86_INS_VBLENDPS, + UC_X86_INS_VBLENDVPD, + UC_X86_INS_VBLENDVPS, + UC_X86_INS_VBROADCASTF128, + UC_X86_INS_VBROADCASTI32X4, + UC_X86_INS_VBROADCASTI64X4, + UC_X86_INS_VBROADCASTSD, + UC_X86_INS_VBROADCASTSS, + UC_X86_INS_VCMPPD, + UC_X86_INS_VCMPPS, + UC_X86_INS_VCMPSD, + UC_X86_INS_VCMPSS, + UC_X86_INS_VCOMPRESSPD, + UC_X86_INS_VCOMPRESSPS, + UC_X86_INS_VCVTDQ2PD, + UC_X86_INS_VCVTDQ2PS, + UC_X86_INS_VCVTPD2DQX, + UC_X86_INS_VCVTPD2DQ, + UC_X86_INS_VCVTPD2PSX, + UC_X86_INS_VCVTPD2PS, + UC_X86_INS_VCVTPD2UDQ, + UC_X86_INS_VCVTPH2PS, + UC_X86_INS_VCVTPS2DQ, + UC_X86_INS_VCVTPS2PD, + UC_X86_INS_VCVTPS2PH, + UC_X86_INS_VCVTPS2UDQ, + UC_X86_INS_VCVTSD2SI, + UC_X86_INS_VCVTSD2USI, + UC_X86_INS_VCVTSS2SI, + UC_X86_INS_VCVTSS2USI, + UC_X86_INS_VCVTTPD2DQX, + UC_X86_INS_VCVTTPD2DQ, + UC_X86_INS_VCVTTPD2UDQ, + UC_X86_INS_VCVTTPS2DQ, + UC_X86_INS_VCVTTPS2UDQ, + UC_X86_INS_VCVTUDQ2PD, + UC_X86_INS_VCVTUDQ2PS, + UC_X86_INS_VDIVPD, + UC_X86_INS_VDIVPS, + UC_X86_INS_VDIVSD, + UC_X86_INS_VDIVSS, + UC_X86_INS_VDPPD, + UC_X86_INS_VDPPS, + UC_X86_INS_VERR, + UC_X86_INS_VERW, + UC_X86_INS_VEXP2PD, + UC_X86_INS_VEXP2PS, + UC_X86_INS_VEXPANDPD, + UC_X86_INS_VEXPANDPS, + UC_X86_INS_VEXTRACTF128, + UC_X86_INS_VEXTRACTF32X4, + UC_X86_INS_VEXTRACTF64X4, + UC_X86_INS_VEXTRACTI128, + UC_X86_INS_VEXTRACTI32X4, + UC_X86_INS_VEXTRACTI64X4, + UC_X86_INS_VEXTRACTPS, + UC_X86_INS_VFMADD132PD, + UC_X86_INS_VFMADD132PS, + UC_X86_INS_VFMADDPD, + UC_X86_INS_VFMADD213PD, + UC_X86_INS_VFMADD231PD, + UC_X86_INS_VFMADDPS, + UC_X86_INS_VFMADD213PS, + UC_X86_INS_VFMADD231PS, + UC_X86_INS_VFMADDSD, + UC_X86_INS_VFMADD213SD, + UC_X86_INS_VFMADD132SD, + UC_X86_INS_VFMADD231SD, + UC_X86_INS_VFMADDSS, + UC_X86_INS_VFMADD213SS, + UC_X86_INS_VFMADD132SS, + UC_X86_INS_VFMADD231SS, + UC_X86_INS_VFMADDSUB132PD, + UC_X86_INS_VFMADDSUB132PS, + UC_X86_INS_VFMADDSUBPD, + UC_X86_INS_VFMADDSUB213PD, + UC_X86_INS_VFMADDSUB231PD, + UC_X86_INS_VFMADDSUBPS, + UC_X86_INS_VFMADDSUB213PS, + UC_X86_INS_VFMADDSUB231PS, + UC_X86_INS_VFMSUB132PD, + UC_X86_INS_VFMSUB132PS, + UC_X86_INS_VFMSUBADD132PD, + UC_X86_INS_VFMSUBADD132PS, + UC_X86_INS_VFMSUBADDPD, + UC_X86_INS_VFMSUBADD213PD, + UC_X86_INS_VFMSUBADD231PD, + UC_X86_INS_VFMSUBADDPS, + UC_X86_INS_VFMSUBADD213PS, + UC_X86_INS_VFMSUBADD231PS, + UC_X86_INS_VFMSUBPD, + UC_X86_INS_VFMSUB213PD, + UC_X86_INS_VFMSUB231PD, + UC_X86_INS_VFMSUBPS, + UC_X86_INS_VFMSUB213PS, + UC_X86_INS_VFMSUB231PS, + UC_X86_INS_VFMSUBSD, + UC_X86_INS_VFMSUB213SD, + UC_X86_INS_VFMSUB132SD, + UC_X86_INS_VFMSUB231SD, + UC_X86_INS_VFMSUBSS, + UC_X86_INS_VFMSUB213SS, + UC_X86_INS_VFMSUB132SS, + UC_X86_INS_VFMSUB231SS, + UC_X86_INS_VFNMADD132PD, + UC_X86_INS_VFNMADD132PS, + UC_X86_INS_VFNMADDPD, + UC_X86_INS_VFNMADD213PD, + UC_X86_INS_VFNMADD231PD, + UC_X86_INS_VFNMADDPS, + UC_X86_INS_VFNMADD213PS, + UC_X86_INS_VFNMADD231PS, + UC_X86_INS_VFNMADDSD, + UC_X86_INS_VFNMADD213SD, + UC_X86_INS_VFNMADD132SD, + UC_X86_INS_VFNMADD231SD, + UC_X86_INS_VFNMADDSS, + UC_X86_INS_VFNMADD213SS, + UC_X86_INS_VFNMADD132SS, + UC_X86_INS_VFNMADD231SS, + UC_X86_INS_VFNMSUB132PD, + UC_X86_INS_VFNMSUB132PS, + UC_X86_INS_VFNMSUBPD, + UC_X86_INS_VFNMSUB213PD, + UC_X86_INS_VFNMSUB231PD, + UC_X86_INS_VFNMSUBPS, + UC_X86_INS_VFNMSUB213PS, + UC_X86_INS_VFNMSUB231PS, + UC_X86_INS_VFNMSUBSD, + UC_X86_INS_VFNMSUB213SD, + UC_X86_INS_VFNMSUB132SD, + UC_X86_INS_VFNMSUB231SD, + UC_X86_INS_VFNMSUBSS, + UC_X86_INS_VFNMSUB213SS, + UC_X86_INS_VFNMSUB132SS, + UC_X86_INS_VFNMSUB231SS, + UC_X86_INS_VFRCZPD, + UC_X86_INS_VFRCZPS, + UC_X86_INS_VFRCZSD, + UC_X86_INS_VFRCZSS, + UC_X86_INS_VORPD, + UC_X86_INS_VORPS, + UC_X86_INS_VXORPD, + UC_X86_INS_VXORPS, + UC_X86_INS_VGATHERDPD, + UC_X86_INS_VGATHERDPS, + UC_X86_INS_VGATHERPF0DPD, + UC_X86_INS_VGATHERPF0DPS, + UC_X86_INS_VGATHERPF0QPD, + UC_X86_INS_VGATHERPF0QPS, + UC_X86_INS_VGATHERPF1DPD, + UC_X86_INS_VGATHERPF1DPS, + UC_X86_INS_VGATHERPF1QPD, + UC_X86_INS_VGATHERPF1QPS, + UC_X86_INS_VGATHERQPD, + UC_X86_INS_VGATHERQPS, + UC_X86_INS_VHADDPD, + UC_X86_INS_VHADDPS, + UC_X86_INS_VHSUBPD, + UC_X86_INS_VHSUBPS, + UC_X86_INS_VINSERTF128, + UC_X86_INS_VINSERTF32X4, + UC_X86_INS_VINSERTF32X8, + UC_X86_INS_VINSERTF64X2, + UC_X86_INS_VINSERTF64X4, + UC_X86_INS_VINSERTI128, + UC_X86_INS_VINSERTI32X4, + UC_X86_INS_VINSERTI32X8, + UC_X86_INS_VINSERTI64X2, + UC_X86_INS_VINSERTI64X4, + UC_X86_INS_VINSERTPS, + UC_X86_INS_VLDDQU, + UC_X86_INS_VLDMXCSR, + UC_X86_INS_VMASKMOVDQU, + UC_X86_INS_VMASKMOVPD, + UC_X86_INS_VMASKMOVPS, + UC_X86_INS_VMAXPD, + UC_X86_INS_VMAXPS, + UC_X86_INS_VMAXSD, + UC_X86_INS_VMAXSS, + UC_X86_INS_VMCALL, + UC_X86_INS_VMCLEAR, + UC_X86_INS_VMFUNC, + UC_X86_INS_VMINPD, + UC_X86_INS_VMINPS, + UC_X86_INS_VMINSD, + UC_X86_INS_VMINSS, + UC_X86_INS_VMLAUNCH, + UC_X86_INS_VMLOAD, + UC_X86_INS_VMMCALL, + UC_X86_INS_VMOVQ, + UC_X86_INS_VMOVDDUP, + UC_X86_INS_VMOVD, + UC_X86_INS_VMOVDQA32, + UC_X86_INS_VMOVDQA64, + UC_X86_INS_VMOVDQA, + UC_X86_INS_VMOVDQU16, + UC_X86_INS_VMOVDQU32, + UC_X86_INS_VMOVDQU64, + UC_X86_INS_VMOVDQU8, + UC_X86_INS_VMOVDQU, + UC_X86_INS_VMOVHLPS, + UC_X86_INS_VMOVHPD, + UC_X86_INS_VMOVHPS, + UC_X86_INS_VMOVLHPS, + UC_X86_INS_VMOVLPD, + UC_X86_INS_VMOVLPS, + UC_X86_INS_VMOVMSKPD, + UC_X86_INS_VMOVMSKPS, + UC_X86_INS_VMOVNTDQA, + UC_X86_INS_VMOVNTDQ, + UC_X86_INS_VMOVNTPD, + UC_X86_INS_VMOVNTPS, + UC_X86_INS_VMOVSD, + UC_X86_INS_VMOVSHDUP, + UC_X86_INS_VMOVSLDUP, + UC_X86_INS_VMOVSS, + UC_X86_INS_VMOVUPD, + UC_X86_INS_VMOVUPS, + UC_X86_INS_VMPSADBW, + UC_X86_INS_VMPTRLD, + UC_X86_INS_VMPTRST, + UC_X86_INS_VMREAD, + UC_X86_INS_VMRESUME, + UC_X86_INS_VMRUN, + UC_X86_INS_VMSAVE, + UC_X86_INS_VMULPD, + UC_X86_INS_VMULPS, + UC_X86_INS_VMULSD, + UC_X86_INS_VMULSS, + UC_X86_INS_VMWRITE, + UC_X86_INS_VMXOFF, + UC_X86_INS_VMXON, + UC_X86_INS_VPABSB, + UC_X86_INS_VPABSD, + UC_X86_INS_VPABSQ, + UC_X86_INS_VPABSW, + UC_X86_INS_VPACKSSDW, + UC_X86_INS_VPACKSSWB, + UC_X86_INS_VPACKUSDW, + UC_X86_INS_VPACKUSWB, + UC_X86_INS_VPADDB, + UC_X86_INS_VPADDD, + UC_X86_INS_VPADDQ, + UC_X86_INS_VPADDSB, + UC_X86_INS_VPADDSW, + UC_X86_INS_VPADDUSB, + UC_X86_INS_VPADDUSW, + UC_X86_INS_VPADDW, + UC_X86_INS_VPALIGNR, + UC_X86_INS_VPANDD, + UC_X86_INS_VPANDND, + UC_X86_INS_VPANDNQ, + UC_X86_INS_VPANDN, + UC_X86_INS_VPANDQ, + UC_X86_INS_VPAND, + UC_X86_INS_VPAVGB, + UC_X86_INS_VPAVGW, + UC_X86_INS_VPBLENDD, + UC_X86_INS_VPBLENDMB, + UC_X86_INS_VPBLENDMD, + UC_X86_INS_VPBLENDMQ, + UC_X86_INS_VPBLENDMW, + UC_X86_INS_VPBLENDVB, + UC_X86_INS_VPBLENDW, + UC_X86_INS_VPBROADCASTB, + UC_X86_INS_VPBROADCASTD, + UC_X86_INS_VPBROADCASTMB2Q, + UC_X86_INS_VPBROADCASTMW2D, + UC_X86_INS_VPBROADCASTQ, + UC_X86_INS_VPBROADCASTW, + UC_X86_INS_VPCLMULQDQ, + UC_X86_INS_VPCMOV, + UC_X86_INS_VPCMPB, + UC_X86_INS_VPCMPD, + UC_X86_INS_VPCMPEQB, + UC_X86_INS_VPCMPEQD, + UC_X86_INS_VPCMPEQQ, + UC_X86_INS_VPCMPEQW, + UC_X86_INS_VPCMPESTRI, + UC_X86_INS_VPCMPESTRM, + UC_X86_INS_VPCMPGTB, + UC_X86_INS_VPCMPGTD, + UC_X86_INS_VPCMPGTQ, + UC_X86_INS_VPCMPGTW, + UC_X86_INS_VPCMPISTRI, + UC_X86_INS_VPCMPISTRM, + UC_X86_INS_VPCMPQ, + UC_X86_INS_VPCMPUB, + UC_X86_INS_VPCMPUD, + UC_X86_INS_VPCMPUQ, + UC_X86_INS_VPCMPUW, + UC_X86_INS_VPCMPW, + UC_X86_INS_VPCOMB, + UC_X86_INS_VPCOMD, + UC_X86_INS_VPCOMPRESSD, + UC_X86_INS_VPCOMPRESSQ, + UC_X86_INS_VPCOMQ, + UC_X86_INS_VPCOMUB, + UC_X86_INS_VPCOMUD, + UC_X86_INS_VPCOMUQ, + UC_X86_INS_VPCOMUW, + UC_X86_INS_VPCOMW, + UC_X86_INS_VPCONFLICTD, + UC_X86_INS_VPCONFLICTQ, + UC_X86_INS_VPERM2F128, + UC_X86_INS_VPERM2I128, + UC_X86_INS_VPERMD, + UC_X86_INS_VPERMI2D, + UC_X86_INS_VPERMI2PD, + UC_X86_INS_VPERMI2PS, + UC_X86_INS_VPERMI2Q, + UC_X86_INS_VPERMIL2PD, + UC_X86_INS_VPERMIL2PS, + UC_X86_INS_VPERMILPD, + UC_X86_INS_VPERMILPS, + UC_X86_INS_VPERMPD, + UC_X86_INS_VPERMPS, + UC_X86_INS_VPERMQ, + UC_X86_INS_VPERMT2D, + UC_X86_INS_VPERMT2PD, + UC_X86_INS_VPERMT2PS, + UC_X86_INS_VPERMT2Q, + UC_X86_INS_VPEXPANDD, + UC_X86_INS_VPEXPANDQ, + UC_X86_INS_VPEXTRB, + UC_X86_INS_VPEXTRD, + UC_X86_INS_VPEXTRQ, + UC_X86_INS_VPEXTRW, + UC_X86_INS_VPGATHERDD, + UC_X86_INS_VPGATHERDQ, + UC_X86_INS_VPGATHERQD, + UC_X86_INS_VPGATHERQQ, + UC_X86_INS_VPHADDBD, + UC_X86_INS_VPHADDBQ, + UC_X86_INS_VPHADDBW, + UC_X86_INS_VPHADDDQ, + UC_X86_INS_VPHADDD, + UC_X86_INS_VPHADDSW, + UC_X86_INS_VPHADDUBD, + UC_X86_INS_VPHADDUBQ, + UC_X86_INS_VPHADDUBW, + UC_X86_INS_VPHADDUDQ, + UC_X86_INS_VPHADDUWD, + UC_X86_INS_VPHADDUWQ, + UC_X86_INS_VPHADDWD, + UC_X86_INS_VPHADDWQ, + UC_X86_INS_VPHADDW, + UC_X86_INS_VPHMINPOSUW, + UC_X86_INS_VPHSUBBW, + UC_X86_INS_VPHSUBDQ, + UC_X86_INS_VPHSUBD, + UC_X86_INS_VPHSUBSW, + UC_X86_INS_VPHSUBWD, + UC_X86_INS_VPHSUBW, + UC_X86_INS_VPINSRB, + UC_X86_INS_VPINSRD, + UC_X86_INS_VPINSRQ, + UC_X86_INS_VPINSRW, + UC_X86_INS_VPLZCNTD, + UC_X86_INS_VPLZCNTQ, + UC_X86_INS_VPMACSDD, + UC_X86_INS_VPMACSDQH, + UC_X86_INS_VPMACSDQL, + UC_X86_INS_VPMACSSDD, + UC_X86_INS_VPMACSSDQH, + UC_X86_INS_VPMACSSDQL, + UC_X86_INS_VPMACSSWD, + UC_X86_INS_VPMACSSWW, + UC_X86_INS_VPMACSWD, + UC_X86_INS_VPMACSWW, + UC_X86_INS_VPMADCSSWD, + UC_X86_INS_VPMADCSWD, + UC_X86_INS_VPMADDUBSW, + UC_X86_INS_VPMADDWD, + UC_X86_INS_VPMASKMOVD, + UC_X86_INS_VPMASKMOVQ, + UC_X86_INS_VPMAXSB, + UC_X86_INS_VPMAXSD, + UC_X86_INS_VPMAXSQ, + UC_X86_INS_VPMAXSW, + UC_X86_INS_VPMAXUB, + UC_X86_INS_VPMAXUD, + UC_X86_INS_VPMAXUQ, + UC_X86_INS_VPMAXUW, + UC_X86_INS_VPMINSB, + UC_X86_INS_VPMINSD, + UC_X86_INS_VPMINSQ, + UC_X86_INS_VPMINSW, + UC_X86_INS_VPMINUB, + UC_X86_INS_VPMINUD, + UC_X86_INS_VPMINUQ, + UC_X86_INS_VPMINUW, + UC_X86_INS_VPMOVDB, + UC_X86_INS_VPMOVDW, + UC_X86_INS_VPMOVM2B, + UC_X86_INS_VPMOVM2D, + UC_X86_INS_VPMOVM2Q, + UC_X86_INS_VPMOVM2W, + UC_X86_INS_VPMOVMSKB, + UC_X86_INS_VPMOVQB, + UC_X86_INS_VPMOVQD, + UC_X86_INS_VPMOVQW, + UC_X86_INS_VPMOVSDB, + UC_X86_INS_VPMOVSDW, + UC_X86_INS_VPMOVSQB, + UC_X86_INS_VPMOVSQD, + UC_X86_INS_VPMOVSQW, + UC_X86_INS_VPMOVSXBD, + UC_X86_INS_VPMOVSXBQ, + UC_X86_INS_VPMOVSXBW, + UC_X86_INS_VPMOVSXDQ, + UC_X86_INS_VPMOVSXWD, + UC_X86_INS_VPMOVSXWQ, + UC_X86_INS_VPMOVUSDB, + UC_X86_INS_VPMOVUSDW, + UC_X86_INS_VPMOVUSQB, + UC_X86_INS_VPMOVUSQD, + UC_X86_INS_VPMOVUSQW, + UC_X86_INS_VPMOVZXBD, + UC_X86_INS_VPMOVZXBQ, + UC_X86_INS_VPMOVZXBW, + UC_X86_INS_VPMOVZXDQ, + UC_X86_INS_VPMOVZXWD, + UC_X86_INS_VPMOVZXWQ, + UC_X86_INS_VPMULDQ, + UC_X86_INS_VPMULHRSW, + UC_X86_INS_VPMULHUW, + UC_X86_INS_VPMULHW, + UC_X86_INS_VPMULLD, + UC_X86_INS_VPMULLQ, + UC_X86_INS_VPMULLW, + UC_X86_INS_VPMULUDQ, + UC_X86_INS_VPORD, + UC_X86_INS_VPORQ, + UC_X86_INS_VPOR, + UC_X86_INS_VPPERM, + UC_X86_INS_VPROTB, + UC_X86_INS_VPROTD, + UC_X86_INS_VPROTQ, + UC_X86_INS_VPROTW, + UC_X86_INS_VPSADBW, + UC_X86_INS_VPSCATTERDD, + UC_X86_INS_VPSCATTERDQ, + UC_X86_INS_VPSCATTERQD, + UC_X86_INS_VPSCATTERQQ, + UC_X86_INS_VPSHAB, + UC_X86_INS_VPSHAD, + UC_X86_INS_VPSHAQ, + UC_X86_INS_VPSHAW, + UC_X86_INS_VPSHLB, + UC_X86_INS_VPSHLD, + UC_X86_INS_VPSHLQ, + UC_X86_INS_VPSHLW, + UC_X86_INS_VPSHUFB, + UC_X86_INS_VPSHUFD, + UC_X86_INS_VPSHUFHW, + UC_X86_INS_VPSHUFLW, + UC_X86_INS_VPSIGNB, + UC_X86_INS_VPSIGND, + UC_X86_INS_VPSIGNW, + UC_X86_INS_VPSLLDQ, + UC_X86_INS_VPSLLD, + UC_X86_INS_VPSLLQ, + UC_X86_INS_VPSLLVD, + UC_X86_INS_VPSLLVQ, + UC_X86_INS_VPSLLW, + UC_X86_INS_VPSRAD, + UC_X86_INS_VPSRAQ, + UC_X86_INS_VPSRAVD, + UC_X86_INS_VPSRAVQ, + UC_X86_INS_VPSRAW, + UC_X86_INS_VPSRLDQ, + UC_X86_INS_VPSRLD, + UC_X86_INS_VPSRLQ, + UC_X86_INS_VPSRLVD, + UC_X86_INS_VPSRLVQ, + UC_X86_INS_VPSRLW, + UC_X86_INS_VPSUBB, + UC_X86_INS_VPSUBD, + UC_X86_INS_VPSUBQ, + UC_X86_INS_VPSUBSB, + UC_X86_INS_VPSUBSW, + UC_X86_INS_VPSUBUSB, + UC_X86_INS_VPSUBUSW, + UC_X86_INS_VPSUBW, + UC_X86_INS_VPTESTMD, + UC_X86_INS_VPTESTMQ, + UC_X86_INS_VPTESTNMD, + UC_X86_INS_VPTESTNMQ, + UC_X86_INS_VPTEST, + UC_X86_INS_VPUNPCKHBW, + UC_X86_INS_VPUNPCKHDQ, + UC_X86_INS_VPUNPCKHQDQ, + UC_X86_INS_VPUNPCKHWD, + UC_X86_INS_VPUNPCKLBW, + UC_X86_INS_VPUNPCKLDQ, + UC_X86_INS_VPUNPCKLQDQ, + UC_X86_INS_VPUNPCKLWD, + UC_X86_INS_VPXORD, + UC_X86_INS_VPXORQ, + UC_X86_INS_VPXOR, + UC_X86_INS_VRCP14PD, + UC_X86_INS_VRCP14PS, + UC_X86_INS_VRCP14SD, + UC_X86_INS_VRCP14SS, + UC_X86_INS_VRCP28PD, + UC_X86_INS_VRCP28PS, + UC_X86_INS_VRCP28SD, + UC_X86_INS_VRCP28SS, + UC_X86_INS_VRCPPS, + UC_X86_INS_VRCPSS, + UC_X86_INS_VRNDSCALEPD, + UC_X86_INS_VRNDSCALEPS, + UC_X86_INS_VRNDSCALESD, + UC_X86_INS_VRNDSCALESS, + UC_X86_INS_VROUNDPD, + UC_X86_INS_VROUNDPS, + UC_X86_INS_VROUNDSD, + UC_X86_INS_VROUNDSS, + UC_X86_INS_VRSQRT14PD, + UC_X86_INS_VRSQRT14PS, + UC_X86_INS_VRSQRT14SD, + UC_X86_INS_VRSQRT14SS, + UC_X86_INS_VRSQRT28PD, + UC_X86_INS_VRSQRT28PS, + UC_X86_INS_VRSQRT28SD, + UC_X86_INS_VRSQRT28SS, + UC_X86_INS_VRSQRTPS, + UC_X86_INS_VRSQRTSS, + UC_X86_INS_VSCATTERDPD, + UC_X86_INS_VSCATTERDPS, + UC_X86_INS_VSCATTERPF0DPD, + UC_X86_INS_VSCATTERPF0DPS, + UC_X86_INS_VSCATTERPF0QPD, + UC_X86_INS_VSCATTERPF0QPS, + UC_X86_INS_VSCATTERPF1DPD, + UC_X86_INS_VSCATTERPF1DPS, + UC_X86_INS_VSCATTERPF1QPD, + UC_X86_INS_VSCATTERPF1QPS, + UC_X86_INS_VSCATTERQPD, + UC_X86_INS_VSCATTERQPS, + UC_X86_INS_VSHUFPD, + UC_X86_INS_VSHUFPS, + UC_X86_INS_VSQRTPD, + UC_X86_INS_VSQRTPS, + UC_X86_INS_VSQRTSD, + UC_X86_INS_VSQRTSS, + UC_X86_INS_VSTMXCSR, + UC_X86_INS_VSUBPD, + UC_X86_INS_VSUBPS, + UC_X86_INS_VSUBSD, + UC_X86_INS_VSUBSS, + UC_X86_INS_VTESTPD, + UC_X86_INS_VTESTPS, + UC_X86_INS_VUNPCKHPD, + UC_X86_INS_VUNPCKHPS, + UC_X86_INS_VUNPCKLPD, + UC_X86_INS_VUNPCKLPS, + UC_X86_INS_VZEROALL, + UC_X86_INS_VZEROUPPER, + UC_X86_INS_WAIT, + UC_X86_INS_WBINVD, + UC_X86_INS_WRFSBASE, + UC_X86_INS_WRGSBASE, + UC_X86_INS_WRMSR, + UC_X86_INS_XABORT, + UC_X86_INS_XACQUIRE, + UC_X86_INS_XBEGIN, + UC_X86_INS_XCHG, + UC_X86_INS_XCRYPTCBC, + UC_X86_INS_XCRYPTCFB, + UC_X86_INS_XCRYPTCTR, + UC_X86_INS_XCRYPTECB, + UC_X86_INS_XCRYPTOFB, + UC_X86_INS_XEND, + UC_X86_INS_XGETBV, + UC_X86_INS_XLATB, + UC_X86_INS_XRELEASE, + UC_X86_INS_XRSTOR, + UC_X86_INS_XRSTOR64, + UC_X86_INS_XRSTORS, + UC_X86_INS_XRSTORS64, + UC_X86_INS_XSAVE, + UC_X86_INS_XSAVE64, + UC_X86_INS_XSAVEC, + UC_X86_INS_XSAVEC64, + UC_X86_INS_XSAVEOPT, + UC_X86_INS_XSAVEOPT64, + UC_X86_INS_XSAVES, + UC_X86_INS_XSAVES64, + UC_X86_INS_XSETBV, + UC_X86_INS_XSHA1, + UC_X86_INS_XSHA256, + UC_X86_INS_XSTORE, + UC_X86_INS_XTEST, + UC_X86_INS_FDISI8087_NOP, + UC_X86_INS_FENI8087_NOP, + + UC_X86_INS_ENDING, // mark the end of the list of insn +} uc_x86_insn; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/m68k_const.py b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/m68k_const.py new file mode 100644 index 0000000000..6d0cdc577a --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/m68k_const.py @@ -0,0 +1,37 @@ +# For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT [m68k_const.py] + +# M68K CPU + +UC_CPU_M68K_M5206 = 0 +UC_CPU_M68K_M68000 = 1 +UC_CPU_M68K_M68020 = 2 +UC_CPU_M68K_M68030 = 3 +UC_CPU_M68K_M68040 = 4 +UC_CPU_M68K_M68060 = 5 +UC_CPU_M68K_M5208 = 6 +UC_CPU_M68K_CFV4E = 7 +UC_CPU_M68K_ANY = 8 +UC_CPU_M68K_ENDING = 9 + +# M68K registers + +UC_M68K_REG_INVALID = 0 +UC_M68K_REG_A0 = 1 +UC_M68K_REG_A1 = 2 +UC_M68K_REG_A2 = 3 +UC_M68K_REG_A3 = 4 +UC_M68K_REG_A4 = 5 +UC_M68K_REG_A5 = 6 +UC_M68K_REG_A6 = 7 +UC_M68K_REG_A7 = 8 +UC_M68K_REG_D0 = 9 +UC_M68K_REG_D1 = 10 +UC_M68K_REG_D2 = 11 +UC_M68K_REG_D3 = 12 +UC_M68K_REG_D4 = 13 +UC_M68K_REG_D5 = 14 +UC_M68K_REG_D6 = 15 +UC_M68K_REG_D7 = 16 +UC_M68K_REG_SR = 17 +UC_M68K_REG_PC = 18 +UC_M68K_REG_ENDING = 19 diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/mips_const.py b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/mips_const.py new file mode 100644 index 0000000000..e19148b927 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/mips_const.py @@ -0,0 +1,235 @@ +# For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT [mips_const.py] + +# MIPS32 CPUS + +UC_CPU_MIPS32_4KC = 0 +UC_CPU_MIPS32_4KM = 1 +UC_CPU_MIPS32_4KECR1 = 2 +UC_CPU_MIPS32_4KEMR1 = 3 +UC_CPU_MIPS32_4KEC = 4 +UC_CPU_MIPS32_4KEM = 5 +UC_CPU_MIPS32_24KC = 6 +UC_CPU_MIPS32_24KEC = 7 +UC_CPU_MIPS32_24KF = 8 +UC_CPU_MIPS32_34KF = 9 +UC_CPU_MIPS32_74KF = 10 +UC_CPU_MIPS32_M14K = 11 +UC_CPU_MIPS32_M14KC = 12 +UC_CPU_MIPS32_P5600 = 13 +UC_CPU_MIPS32_MIPS32R6_GENERIC = 14 +UC_CPU_MIPS32_I7200 = 15 +UC_CPU_MIPS32_ENDING = 16 + +# MIPS64 CPUS + +UC_CPU_MIPS64_R4000 = 0 +UC_CPU_MIPS64_VR5432 = 1 +UC_CPU_MIPS64_5KC = 2 +UC_CPU_MIPS64_5KF = 3 +UC_CPU_MIPS64_20KC = 4 +UC_CPU_MIPS64_MIPS64R2_GENERIC = 5 +UC_CPU_MIPS64_5KEC = 6 +UC_CPU_MIPS64_5KEF = 7 +UC_CPU_MIPS64_I6400 = 8 +UC_CPU_MIPS64_I6500 = 9 +UC_CPU_MIPS64_LOONGSON_2E = 10 +UC_CPU_MIPS64_LOONGSON_2F = 11 +UC_CPU_MIPS64_MIPS64DSPR2 = 12 +UC_CPU_MIPS64_ENDING = 13 + +# MIPS registers + +UC_MIPS_REG_INVALID = 0 + +# General purpose registers +UC_MIPS_REG_PC = 1 +UC_MIPS_REG_0 = 2 +UC_MIPS_REG_1 = 3 +UC_MIPS_REG_2 = 4 +UC_MIPS_REG_3 = 5 +UC_MIPS_REG_4 = 6 +UC_MIPS_REG_5 = 7 +UC_MIPS_REG_6 = 8 +UC_MIPS_REG_7 = 9 +UC_MIPS_REG_8 = 10 +UC_MIPS_REG_9 = 11 +UC_MIPS_REG_10 = 12 +UC_MIPS_REG_11 = 13 +UC_MIPS_REG_12 = 14 +UC_MIPS_REG_13 = 15 +UC_MIPS_REG_14 = 16 +UC_MIPS_REG_15 = 17 +UC_MIPS_REG_16 = 18 +UC_MIPS_REG_17 = 19 +UC_MIPS_REG_18 = 20 +UC_MIPS_REG_19 = 21 +UC_MIPS_REG_20 = 22 +UC_MIPS_REG_21 = 23 +UC_MIPS_REG_22 = 24 +UC_MIPS_REG_23 = 25 +UC_MIPS_REG_24 = 26 +UC_MIPS_REG_25 = 27 +UC_MIPS_REG_26 = 28 +UC_MIPS_REG_27 = 29 +UC_MIPS_REG_28 = 30 +UC_MIPS_REG_29 = 31 +UC_MIPS_REG_30 = 32 +UC_MIPS_REG_31 = 33 + +# DSP registers +UC_MIPS_REG_DSPCCOND = 34 +UC_MIPS_REG_DSPCARRY = 35 +UC_MIPS_REG_DSPEFI = 36 +UC_MIPS_REG_DSPOUTFLAG = 37 +UC_MIPS_REG_DSPOUTFLAG16_19 = 38 +UC_MIPS_REG_DSPOUTFLAG20 = 39 +UC_MIPS_REG_DSPOUTFLAG21 = 40 +UC_MIPS_REG_DSPOUTFLAG22 = 41 +UC_MIPS_REG_DSPOUTFLAG23 = 42 +UC_MIPS_REG_DSPPOS = 43 +UC_MIPS_REG_DSPSCOUNT = 44 + +# ACC registers +UC_MIPS_REG_AC0 = 45 +UC_MIPS_REG_AC1 = 46 +UC_MIPS_REG_AC2 = 47 +UC_MIPS_REG_AC3 = 48 + +# COP registers +UC_MIPS_REG_CC0 = 49 +UC_MIPS_REG_CC1 = 50 +UC_MIPS_REG_CC2 = 51 +UC_MIPS_REG_CC3 = 52 +UC_MIPS_REG_CC4 = 53 +UC_MIPS_REG_CC5 = 54 +UC_MIPS_REG_CC6 = 55 +UC_MIPS_REG_CC7 = 56 + +# FPU registers +UC_MIPS_REG_F0 = 57 +UC_MIPS_REG_F1 = 58 +UC_MIPS_REG_F2 = 59 +UC_MIPS_REG_F3 = 60 +UC_MIPS_REG_F4 = 61 +UC_MIPS_REG_F5 = 62 +UC_MIPS_REG_F6 = 63 +UC_MIPS_REG_F7 = 64 +UC_MIPS_REG_F8 = 65 +UC_MIPS_REG_F9 = 66 +UC_MIPS_REG_F10 = 67 +UC_MIPS_REG_F11 = 68 +UC_MIPS_REG_F12 = 69 +UC_MIPS_REG_F13 = 70 +UC_MIPS_REG_F14 = 71 +UC_MIPS_REG_F15 = 72 +UC_MIPS_REG_F16 = 73 +UC_MIPS_REG_F17 = 74 +UC_MIPS_REG_F18 = 75 +UC_MIPS_REG_F19 = 76 +UC_MIPS_REG_F20 = 77 +UC_MIPS_REG_F21 = 78 +UC_MIPS_REG_F22 = 79 +UC_MIPS_REG_F23 = 80 +UC_MIPS_REG_F24 = 81 +UC_MIPS_REG_F25 = 82 +UC_MIPS_REG_F26 = 83 +UC_MIPS_REG_F27 = 84 +UC_MIPS_REG_F28 = 85 +UC_MIPS_REG_F29 = 86 +UC_MIPS_REG_F30 = 87 +UC_MIPS_REG_F31 = 88 +UC_MIPS_REG_FCC0 = 89 +UC_MIPS_REG_FCC1 = 90 +UC_MIPS_REG_FCC2 = 91 +UC_MIPS_REG_FCC3 = 92 +UC_MIPS_REG_FCC4 = 93 +UC_MIPS_REG_FCC5 = 94 +UC_MIPS_REG_FCC6 = 95 +UC_MIPS_REG_FCC7 = 96 + +# AFPR128 +UC_MIPS_REG_W0 = 97 +UC_MIPS_REG_W1 = 98 +UC_MIPS_REG_W2 = 99 +UC_MIPS_REG_W3 = 100 +UC_MIPS_REG_W4 = 101 +UC_MIPS_REG_W5 = 102 +UC_MIPS_REG_W6 = 103 +UC_MIPS_REG_W7 = 104 +UC_MIPS_REG_W8 = 105 +UC_MIPS_REG_W9 = 106 +UC_MIPS_REG_W10 = 107 +UC_MIPS_REG_W11 = 108 +UC_MIPS_REG_W12 = 109 +UC_MIPS_REG_W13 = 110 +UC_MIPS_REG_W14 = 111 +UC_MIPS_REG_W15 = 112 +UC_MIPS_REG_W16 = 113 +UC_MIPS_REG_W17 = 114 +UC_MIPS_REG_W18 = 115 +UC_MIPS_REG_W19 = 116 +UC_MIPS_REG_W20 = 117 +UC_MIPS_REG_W21 = 118 +UC_MIPS_REG_W22 = 119 +UC_MIPS_REG_W23 = 120 +UC_MIPS_REG_W24 = 121 +UC_MIPS_REG_W25 = 122 +UC_MIPS_REG_W26 = 123 +UC_MIPS_REG_W27 = 124 +UC_MIPS_REG_W28 = 125 +UC_MIPS_REG_W29 = 126 +UC_MIPS_REG_W30 = 127 +UC_MIPS_REG_W31 = 128 +UC_MIPS_REG_HI = 129 +UC_MIPS_REG_LO = 130 +UC_MIPS_REG_P0 = 131 +UC_MIPS_REG_P1 = 132 +UC_MIPS_REG_P2 = 133 +UC_MIPS_REG_MPL0 = 134 +UC_MIPS_REG_MPL1 = 135 +UC_MIPS_REG_MPL2 = 136 +UC_MIPS_REG_CP0_CONFIG3 = 137 +UC_MIPS_REG_CP0_USERLOCAL = 138 +UC_MIPS_REG_CP0_STATUS = 139 +UC_MIPS_REG_ENDING = 140 +UC_MIPS_REG_ZERO = 2 +UC_MIPS_REG_AT = 3 +UC_MIPS_REG_V0 = 4 +UC_MIPS_REG_V1 = 5 +UC_MIPS_REG_A0 = 6 +UC_MIPS_REG_A1 = 7 +UC_MIPS_REG_A2 = 8 +UC_MIPS_REG_A3 = 9 +UC_MIPS_REG_T0 = 10 +UC_MIPS_REG_T1 = 11 +UC_MIPS_REG_T2 = 12 +UC_MIPS_REG_T3 = 13 +UC_MIPS_REG_T4 = 14 +UC_MIPS_REG_T5 = 15 +UC_MIPS_REG_T6 = 16 +UC_MIPS_REG_T7 = 17 +UC_MIPS_REG_S0 = 18 +UC_MIPS_REG_S1 = 19 +UC_MIPS_REG_S2 = 20 +UC_MIPS_REG_S3 = 21 +UC_MIPS_REG_S4 = 22 +UC_MIPS_REG_S5 = 23 +UC_MIPS_REG_S6 = 24 +UC_MIPS_REG_S7 = 25 +UC_MIPS_REG_T8 = 26 +UC_MIPS_REG_T9 = 27 +UC_MIPS_REG_K0 = 28 +UC_MIPS_REG_K1 = 29 +UC_MIPS_REG_GP = 30 +UC_MIPS_REG_SP = 31 +UC_MIPS_REG_FP = 32 +UC_MIPS_REG_S8 = 32 +UC_MIPS_REG_RA = 33 +UC_MIPS_REG_HI0 = 45 +UC_MIPS_REG_HI1 = 46 +UC_MIPS_REG_HI2 = 47 +UC_MIPS_REG_HI3 = 48 +UC_MIPS_REG_LO0 = 45 +UC_MIPS_REG_LO1 = 46 +UC_MIPS_REG_LO2 = 47 +UC_MIPS_REG_LO3 = 48 diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/ppc_const.py b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/ppc_const.py new file mode 100644 index 0000000000..9066fde9e7 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/ppc_const.py @@ -0,0 +1,404 @@ +# For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT [ppc_const.py] + +# PPC CPU + +UC_CPU_PPC32_401 = 0 +UC_CPU_PPC32_401A1 = 1 +UC_CPU_PPC32_401B2 = 2 +UC_CPU_PPC32_401C2 = 3 +UC_CPU_PPC32_401D2 = 4 +UC_CPU_PPC32_401E2 = 5 +UC_CPU_PPC32_401F2 = 6 +UC_CPU_PPC32_401G2 = 7 +UC_CPU_PPC32_IOP480 = 8 +UC_CPU_PPC32_COBRA = 9 +UC_CPU_PPC32_403GA = 10 +UC_CPU_PPC32_403GB = 11 +UC_CPU_PPC32_403GC = 12 +UC_CPU_PPC32_403GCX = 13 +UC_CPU_PPC32_405D2 = 14 +UC_CPU_PPC32_405D4 = 15 +UC_CPU_PPC32_405CRA = 16 +UC_CPU_PPC32_405CRB = 17 +UC_CPU_PPC32_405CRC = 18 +UC_CPU_PPC32_405EP = 19 +UC_CPU_PPC32_405EZ = 20 +UC_CPU_PPC32_405GPA = 21 +UC_CPU_PPC32_405GPB = 22 +UC_CPU_PPC32_405GPC = 23 +UC_CPU_PPC32_405GPD = 24 +UC_CPU_PPC32_405GPR = 25 +UC_CPU_PPC32_405LP = 26 +UC_CPU_PPC32_NPE405H = 27 +UC_CPU_PPC32_NPE405H2 = 28 +UC_CPU_PPC32_NPE405L = 29 +UC_CPU_PPC32_NPE4GS3 = 30 +UC_CPU_PPC32_STB03 = 31 +UC_CPU_PPC32_STB04 = 32 +UC_CPU_PPC32_STB25 = 33 +UC_CPU_PPC32_X2VP4 = 34 +UC_CPU_PPC32_X2VP20 = 35 +UC_CPU_PPC32_440_XILINX = 36 +UC_CPU_PPC32_440_XILINX_W_DFPU = 37 +UC_CPU_PPC32_440EPA = 38 +UC_CPU_PPC32_440EPB = 39 +UC_CPU_PPC32_440EPX = 40 +UC_CPU_PPC32_460EXB = 41 +UC_CPU_PPC32_G2 = 42 +UC_CPU_PPC32_G2H4 = 43 +UC_CPU_PPC32_G2GP = 44 +UC_CPU_PPC32_G2LS = 45 +UC_CPU_PPC32_G2HIP3 = 46 +UC_CPU_PPC32_G2HIP4 = 47 +UC_CPU_PPC32_MPC603 = 48 +UC_CPU_PPC32_G2LE = 49 +UC_CPU_PPC32_G2LEGP = 50 +UC_CPU_PPC32_G2LELS = 51 +UC_CPU_PPC32_G2LEGP1 = 52 +UC_CPU_PPC32_G2LEGP3 = 53 +UC_CPU_PPC32_MPC5200_V10 = 54 +UC_CPU_PPC32_MPC5200_V11 = 55 +UC_CPU_PPC32_MPC5200_V12 = 56 +UC_CPU_PPC32_MPC5200B_V20 = 57 +UC_CPU_PPC32_MPC5200B_V21 = 58 +UC_CPU_PPC32_E200Z5 = 59 +UC_CPU_PPC32_E200Z6 = 60 +UC_CPU_PPC32_E300C1 = 61 +UC_CPU_PPC32_E300C2 = 62 +UC_CPU_PPC32_E300C3 = 63 +UC_CPU_PPC32_E300C4 = 64 +UC_CPU_PPC32_MPC8343 = 65 +UC_CPU_PPC32_MPC8343A = 66 +UC_CPU_PPC32_MPC8343E = 67 +UC_CPU_PPC32_MPC8343EA = 68 +UC_CPU_PPC32_MPC8347T = 69 +UC_CPU_PPC32_MPC8347P = 70 +UC_CPU_PPC32_MPC8347AT = 71 +UC_CPU_PPC32_MPC8347AP = 72 +UC_CPU_PPC32_MPC8347ET = 73 +UC_CPU_PPC32_MPC8347EP = 74 +UC_CPU_PPC32_MPC8347EAT = 75 +UC_CPU_PPC32_MPC8347EAP = 76 +UC_CPU_PPC32_MPC8349 = 77 +UC_CPU_PPC32_MPC8349A = 78 +UC_CPU_PPC32_MPC8349E = 79 +UC_CPU_PPC32_MPC8349EA = 80 +UC_CPU_PPC32_MPC8377 = 81 +UC_CPU_PPC32_MPC8377E = 82 +UC_CPU_PPC32_MPC8378 = 83 +UC_CPU_PPC32_MPC8378E = 84 +UC_CPU_PPC32_MPC8379 = 85 +UC_CPU_PPC32_MPC8379E = 86 +UC_CPU_PPC32_E500_V10 = 87 +UC_CPU_PPC32_E500_V20 = 88 +UC_CPU_PPC32_E500V2_V10 = 89 +UC_CPU_PPC32_E500V2_V20 = 90 +UC_CPU_PPC32_E500V2_V21 = 91 +UC_CPU_PPC32_E500V2_V22 = 92 +UC_CPU_PPC32_E500V2_V30 = 93 +UC_CPU_PPC32_E500MC = 94 +UC_CPU_PPC32_MPC8533_V10 = 95 +UC_CPU_PPC32_MPC8533_V11 = 96 +UC_CPU_PPC32_MPC8533E_V10 = 97 +UC_CPU_PPC32_MPC8533E_V11 = 98 +UC_CPU_PPC32_MPC8540_V10 = 99 +UC_CPU_PPC32_MPC8540_V20 = 100 +UC_CPU_PPC32_MPC8540_V21 = 101 +UC_CPU_PPC32_MPC8541_V10 = 102 +UC_CPU_PPC32_MPC8541_V11 = 103 +UC_CPU_PPC32_MPC8541E_V10 = 104 +UC_CPU_PPC32_MPC8541E_V11 = 105 +UC_CPU_PPC32_MPC8543_V10 = 106 +UC_CPU_PPC32_MPC8543_V11 = 107 +UC_CPU_PPC32_MPC8543_V20 = 108 +UC_CPU_PPC32_MPC8543_V21 = 109 +UC_CPU_PPC32_MPC8543E_V10 = 110 +UC_CPU_PPC32_MPC8543E_V11 = 111 +UC_CPU_PPC32_MPC8543E_V20 = 112 +UC_CPU_PPC32_MPC8543E_V21 = 113 +UC_CPU_PPC32_MPC8544_V10 = 114 +UC_CPU_PPC32_MPC8544_V11 = 115 +UC_CPU_PPC32_MPC8544E_V10 = 116 +UC_CPU_PPC32_MPC8544E_V11 = 117 +UC_CPU_PPC32_MPC8545_V20 = 118 +UC_CPU_PPC32_MPC8545_V21 = 119 +UC_CPU_PPC32_MPC8545E_V20 = 120 +UC_CPU_PPC32_MPC8545E_V21 = 121 +UC_CPU_PPC32_MPC8547E_V20 = 122 +UC_CPU_PPC32_MPC8547E_V21 = 123 +UC_CPU_PPC32_MPC8548_V10 = 124 +UC_CPU_PPC32_MPC8548_V11 = 125 +UC_CPU_PPC32_MPC8548_V20 = 126 +UC_CPU_PPC32_MPC8548_V21 = 127 +UC_CPU_PPC32_MPC8548E_V10 = 128 +UC_CPU_PPC32_MPC8548E_V11 = 129 +UC_CPU_PPC32_MPC8548E_V20 = 130 +UC_CPU_PPC32_MPC8548E_V21 = 131 +UC_CPU_PPC32_MPC8555_V10 = 132 +UC_CPU_PPC32_MPC8555_V11 = 133 +UC_CPU_PPC32_MPC8555E_V10 = 134 +UC_CPU_PPC32_MPC8555E_V11 = 135 +UC_CPU_PPC32_MPC8560_V10 = 136 +UC_CPU_PPC32_MPC8560_V20 = 137 +UC_CPU_PPC32_MPC8560_V21 = 138 +UC_CPU_PPC32_MPC8567 = 139 +UC_CPU_PPC32_MPC8567E = 140 +UC_CPU_PPC32_MPC8568 = 141 +UC_CPU_PPC32_MPC8568E = 142 +UC_CPU_PPC32_MPC8572 = 143 +UC_CPU_PPC32_MPC8572E = 144 +UC_CPU_PPC32_E600 = 145 +UC_CPU_PPC32_MPC8610 = 146 +UC_CPU_PPC32_MPC8641 = 147 +UC_CPU_PPC32_MPC8641D = 148 +UC_CPU_PPC32_601_V0 = 149 +UC_CPU_PPC32_601_V1 = 150 +UC_CPU_PPC32_601_V2 = 151 +UC_CPU_PPC32_602 = 152 +UC_CPU_PPC32_603 = 153 +UC_CPU_PPC32_603E_V1_1 = 154 +UC_CPU_PPC32_603E_V1_2 = 155 +UC_CPU_PPC32_603E_V1_3 = 156 +UC_CPU_PPC32_603E_V1_4 = 157 +UC_CPU_PPC32_603E_V2_2 = 158 +UC_CPU_PPC32_603E_V3 = 159 +UC_CPU_PPC32_603E_V4 = 160 +UC_CPU_PPC32_603E_V4_1 = 161 +UC_CPU_PPC32_603E7 = 162 +UC_CPU_PPC32_603E7T = 163 +UC_CPU_PPC32_603E7V = 164 +UC_CPU_PPC32_603E7V1 = 165 +UC_CPU_PPC32_603E7V2 = 166 +UC_CPU_PPC32_603P = 167 +UC_CPU_PPC32_604 = 168 +UC_CPU_PPC32_604E_V1_0 = 169 +UC_CPU_PPC32_604E_V2_2 = 170 +UC_CPU_PPC32_604E_V2_4 = 171 +UC_CPU_PPC32_604R = 172 +UC_CPU_PPC32_740_V1_0 = 173 +UC_CPU_PPC32_750_V1_0 = 174 +UC_CPU_PPC32_740_V2_0 = 175 +UC_CPU_PPC32_750_V2_0 = 176 +UC_CPU_PPC32_740_V2_1 = 177 +UC_CPU_PPC32_750_V2_1 = 178 +UC_CPU_PPC32_740_V2_2 = 179 +UC_CPU_PPC32_750_V2_2 = 180 +UC_CPU_PPC32_740_V3_0 = 181 +UC_CPU_PPC32_750_V3_0 = 182 +UC_CPU_PPC32_740_V3_1 = 183 +UC_CPU_PPC32_750_V3_1 = 184 +UC_CPU_PPC32_740E = 185 +UC_CPU_PPC32_750E = 186 +UC_CPU_PPC32_740P = 187 +UC_CPU_PPC32_750P = 188 +UC_CPU_PPC32_750CL_V1_0 = 189 +UC_CPU_PPC32_750CL_V2_0 = 190 +UC_CPU_PPC32_750CX_V1_0 = 191 +UC_CPU_PPC32_750CX_V2_0 = 192 +UC_CPU_PPC32_750CX_V2_1 = 193 +UC_CPU_PPC32_750CX_V2_2 = 194 +UC_CPU_PPC32_750CXE_V2_1 = 195 +UC_CPU_PPC32_750CXE_V2_2 = 196 +UC_CPU_PPC32_750CXE_V2_3 = 197 +UC_CPU_PPC32_750CXE_V2_4 = 198 +UC_CPU_PPC32_750CXE_V2_4B = 199 +UC_CPU_PPC32_750CXE_V3_0 = 200 +UC_CPU_PPC32_750CXE_V3_1 = 201 +UC_CPU_PPC32_750CXE_V3_1B = 202 +UC_CPU_PPC32_750CXR = 203 +UC_CPU_PPC32_750FL = 204 +UC_CPU_PPC32_750FX_V1_0 = 205 +UC_CPU_PPC32_750FX_V2_0 = 206 +UC_CPU_PPC32_750FX_V2_1 = 207 +UC_CPU_PPC32_750FX_V2_2 = 208 +UC_CPU_PPC32_750FX_V2_3 = 209 +UC_CPU_PPC32_750GL = 210 +UC_CPU_PPC32_750GX_V1_0 = 211 +UC_CPU_PPC32_750GX_V1_1 = 212 +UC_CPU_PPC32_750GX_V1_2 = 213 +UC_CPU_PPC32_750L_V2_0 = 214 +UC_CPU_PPC32_750L_V2_1 = 215 +UC_CPU_PPC32_750L_V2_2 = 216 +UC_CPU_PPC32_750L_V3_0 = 217 +UC_CPU_PPC32_750L_V3_2 = 218 +UC_CPU_PPC32_745_V1_0 = 219 +UC_CPU_PPC32_755_V1_0 = 220 +UC_CPU_PPC32_745_V1_1 = 221 +UC_CPU_PPC32_755_V1_1 = 222 +UC_CPU_PPC32_745_V2_0 = 223 +UC_CPU_PPC32_755_V2_0 = 224 +UC_CPU_PPC32_745_V2_1 = 225 +UC_CPU_PPC32_755_V2_1 = 226 +UC_CPU_PPC32_745_V2_2 = 227 +UC_CPU_PPC32_755_V2_2 = 228 +UC_CPU_PPC32_745_V2_3 = 229 +UC_CPU_PPC32_755_V2_3 = 230 +UC_CPU_PPC32_745_V2_4 = 231 +UC_CPU_PPC32_755_V2_4 = 232 +UC_CPU_PPC32_745_V2_5 = 233 +UC_CPU_PPC32_755_V2_5 = 234 +UC_CPU_PPC32_745_V2_6 = 235 +UC_CPU_PPC32_755_V2_6 = 236 +UC_CPU_PPC32_745_V2_7 = 237 +UC_CPU_PPC32_755_V2_7 = 238 +UC_CPU_PPC32_745_V2_8 = 239 +UC_CPU_PPC32_755_V2_8 = 240 +UC_CPU_PPC32_7400_V1_0 = 241 +UC_CPU_PPC32_7400_V1_1 = 242 +UC_CPU_PPC32_7400_V2_0 = 243 +UC_CPU_PPC32_7400_V2_1 = 244 +UC_CPU_PPC32_7400_V2_2 = 245 +UC_CPU_PPC32_7400_V2_6 = 246 +UC_CPU_PPC32_7400_V2_7 = 247 +UC_CPU_PPC32_7400_V2_8 = 248 +UC_CPU_PPC32_7400_V2_9 = 249 +UC_CPU_PPC32_7410_V1_0 = 250 +UC_CPU_PPC32_7410_V1_1 = 251 +UC_CPU_PPC32_7410_V1_2 = 252 +UC_CPU_PPC32_7410_V1_3 = 253 +UC_CPU_PPC32_7410_V1_4 = 254 +UC_CPU_PPC32_7448_V1_0 = 255 +UC_CPU_PPC32_7448_V1_1 = 256 +UC_CPU_PPC32_7448_V2_0 = 257 +UC_CPU_PPC32_7448_V2_1 = 258 +UC_CPU_PPC32_7450_V1_0 = 259 +UC_CPU_PPC32_7450_V1_1 = 260 +UC_CPU_PPC32_7450_V1_2 = 261 +UC_CPU_PPC32_7450_V2_0 = 262 +UC_CPU_PPC32_7450_V2_1 = 263 +UC_CPU_PPC32_7441_V2_1 = 264 +UC_CPU_PPC32_7441_V2_3 = 265 +UC_CPU_PPC32_7451_V2_3 = 266 +UC_CPU_PPC32_7441_V2_10 = 267 +UC_CPU_PPC32_7451_V2_10 = 268 +UC_CPU_PPC32_7445_V1_0 = 269 +UC_CPU_PPC32_7455_V1_0 = 270 +UC_CPU_PPC32_7445_V2_1 = 271 +UC_CPU_PPC32_7455_V2_1 = 272 +UC_CPU_PPC32_7445_V3_2 = 273 +UC_CPU_PPC32_7455_V3_2 = 274 +UC_CPU_PPC32_7445_V3_3 = 275 +UC_CPU_PPC32_7455_V3_3 = 276 +UC_CPU_PPC32_7445_V3_4 = 277 +UC_CPU_PPC32_7455_V3_4 = 278 +UC_CPU_PPC32_7447_V1_0 = 279 +UC_CPU_PPC32_7457_V1_0 = 280 +UC_CPU_PPC32_7447_V1_1 = 281 +UC_CPU_PPC32_7457_V1_1 = 282 +UC_CPU_PPC32_7457_V1_2 = 283 +UC_CPU_PPC32_7447A_V1_0 = 284 +UC_CPU_PPC32_7457A_V1_0 = 285 +UC_CPU_PPC32_7447A_V1_1 = 286 +UC_CPU_PPC32_7457A_V1_1 = 287 +UC_CPU_PPC32_7447A_V1_2 = 288 +UC_CPU_PPC32_7457A_V1_2 = 289 +UC_CPU_PPC32_ENDING = 290 + +# PPC64 CPU + +UC_CPU_PPC64_E5500 = 0 +UC_CPU_PPC64_E6500 = 1 +UC_CPU_PPC64_970_V2_2 = 2 +UC_CPU_PPC64_970FX_V1_0 = 3 +UC_CPU_PPC64_970FX_V2_0 = 4 +UC_CPU_PPC64_970FX_V2_1 = 5 +UC_CPU_PPC64_970FX_V3_0 = 6 +UC_CPU_PPC64_970FX_V3_1 = 7 +UC_CPU_PPC64_970MP_V1_0 = 8 +UC_CPU_PPC64_970MP_V1_1 = 9 +UC_CPU_PPC64_POWER5_V2_1 = 10 +UC_CPU_PPC64_POWER7_V2_3 = 11 +UC_CPU_PPC64_POWER7_V2_1 = 12 +UC_CPU_PPC64_POWER8E_V2_1 = 13 +UC_CPU_PPC64_POWER8_V2_0 = 14 +UC_CPU_PPC64_POWER8NVL_V1_0 = 15 +UC_CPU_PPC64_POWER9_V1_0 = 16 +UC_CPU_PPC64_POWER9_V2_0 = 17 +UC_CPU_PPC64_POWER10_V1_0 = 18 +UC_CPU_PPC64_ENDING = 19 + +# PPC registers + +UC_PPC_REG_INVALID = 0 + +# General purpose registers +UC_PPC_REG_PC = 1 +UC_PPC_REG_0 = 2 +UC_PPC_REG_1 = 3 +UC_PPC_REG_2 = 4 +UC_PPC_REG_3 = 5 +UC_PPC_REG_4 = 6 +UC_PPC_REG_5 = 7 +UC_PPC_REG_6 = 8 +UC_PPC_REG_7 = 9 +UC_PPC_REG_8 = 10 +UC_PPC_REG_9 = 11 +UC_PPC_REG_10 = 12 +UC_PPC_REG_11 = 13 +UC_PPC_REG_12 = 14 +UC_PPC_REG_13 = 15 +UC_PPC_REG_14 = 16 +UC_PPC_REG_15 = 17 +UC_PPC_REG_16 = 18 +UC_PPC_REG_17 = 19 +UC_PPC_REG_18 = 20 +UC_PPC_REG_19 = 21 +UC_PPC_REG_20 = 22 +UC_PPC_REG_21 = 23 +UC_PPC_REG_22 = 24 +UC_PPC_REG_23 = 25 +UC_PPC_REG_24 = 26 +UC_PPC_REG_25 = 27 +UC_PPC_REG_26 = 28 +UC_PPC_REG_27 = 29 +UC_PPC_REG_28 = 30 +UC_PPC_REG_29 = 31 +UC_PPC_REG_30 = 32 +UC_PPC_REG_31 = 33 +UC_PPC_REG_CR0 = 34 +UC_PPC_REG_CR1 = 35 +UC_PPC_REG_CR2 = 36 +UC_PPC_REG_CR3 = 37 +UC_PPC_REG_CR4 = 38 +UC_PPC_REG_CR5 = 39 +UC_PPC_REG_CR6 = 40 +UC_PPC_REG_CR7 = 41 +UC_PPC_REG_FPR0 = 42 +UC_PPC_REG_FPR1 = 43 +UC_PPC_REG_FPR2 = 44 +UC_PPC_REG_FPR3 = 45 +UC_PPC_REG_FPR4 = 46 +UC_PPC_REG_FPR5 = 47 +UC_PPC_REG_FPR6 = 48 +UC_PPC_REG_FPR7 = 49 +UC_PPC_REG_FPR8 = 50 +UC_PPC_REG_FPR9 = 51 +UC_PPC_REG_FPR10 = 52 +UC_PPC_REG_FPR11 = 53 +UC_PPC_REG_FPR12 = 54 +UC_PPC_REG_FPR13 = 55 +UC_PPC_REG_FPR14 = 56 +UC_PPC_REG_FPR15 = 57 +UC_PPC_REG_FPR16 = 58 +UC_PPC_REG_FPR17 = 59 +UC_PPC_REG_FPR18 = 60 +UC_PPC_REG_FPR19 = 61 +UC_PPC_REG_FPR20 = 62 +UC_PPC_REG_FPR21 = 63 +UC_PPC_REG_FPR22 = 64 +UC_PPC_REG_FPR23 = 65 +UC_PPC_REG_FPR24 = 66 +UC_PPC_REG_FPR25 = 67 +UC_PPC_REG_FPR26 = 68 +UC_PPC_REG_FPR27 = 69 +UC_PPC_REG_FPR28 = 70 +UC_PPC_REG_FPR29 = 71 +UC_PPC_REG_FPR30 = 72 +UC_PPC_REG_FPR31 = 73 +UC_PPC_REG_LR = 74 +UC_PPC_REG_XER = 75 +UC_PPC_REG_CTR = 76 +UC_PPC_REG_MSR = 77 +UC_PPC_REG_FPSCR = 78 +UC_PPC_REG_CR = 79 +UC_PPC_REG_ENDING = 80 diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/rh850_const.py b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/rh850_const.py new file mode 100644 index 0000000000..663cb3eda7 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/rh850_const.py @@ -0,0 +1,91 @@ +# For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT [rh850_const.py] +UC_RH850_SYSREG_SELID0 = 32 +UC_RH850_SYSREG_SELID1 = 64 +UC_RH850_SYSREG_SELID2 = 96 +UC_RH850_SYSREG_SELID3 = 128 +UC_RH850_SYSREG_SELID4 = 160 +UC_RH850_SYSREG_SELID5 = 192 +UC_RH850_SYSREG_SELID6 = 224 +UC_RH850_SYSREG_SELID7 = 256 + +# RH850 global purpose registers + +UC_RH850_REG_R0 = 0 +UC_RH850_REG_R1 = 1 +UC_RH850_REG_R2 = 2 +UC_RH850_REG_R3 = 3 +UC_RH850_REG_R4 = 4 +UC_RH850_REG_R5 = 5 +UC_RH850_REG_R6 = 6 +UC_RH850_REG_R7 = 7 +UC_RH850_REG_R8 = 8 +UC_RH850_REG_R9 = 9 +UC_RH850_REG_R10 = 10 +UC_RH850_REG_R11 = 11 +UC_RH850_REG_R12 = 12 +UC_RH850_REG_R13 = 13 +UC_RH850_REG_R14 = 14 +UC_RH850_REG_R15 = 15 +UC_RH850_REG_R16 = 16 +UC_RH850_REG_R17 = 17 +UC_RH850_REG_R18 = 18 +UC_RH850_REG_R19 = 19 +UC_RH850_REG_R20 = 20 +UC_RH850_REG_R21 = 21 +UC_RH850_REG_R22 = 22 +UC_RH850_REG_R23 = 23 +UC_RH850_REG_R24 = 24 +UC_RH850_REG_R25 = 25 +UC_RH850_REG_R26 = 26 +UC_RH850_REG_R27 = 27 +UC_RH850_REG_R28 = 28 +UC_RH850_REG_R29 = 29 +UC_RH850_REG_R30 = 30 +UC_RH850_REG_R31 = 31 + +# RH850 system registers, selection ID 0 +UC_RH850_REG_EIPC = 32 +UC_RH850_REG_EIPSW = 33 +UC_RH850_REG_FEPC = 34 +UC_RH850_REG_FEPSW = 35 +UC_RH850_REG_ECR = 36 +UC_RH850_REG_PSW = 37 +UC_RH850_REG_FPSR = 38 +UC_RH850_REG_FPEPC = 39 +UC_RH850_REG_FPST = 40 +UC_RH850_REG_FPCC = 41 +UC_RH850_REG_FPCFG = 42 +UC_RH850_REG_FPEC = 43 +UC_RH850_REG_EIIC = 45 +UC_RH850_REG_FEIC = 46 +UC_RH850_REG_CTPC = 48 +UC_RH850_REG_CTPSW = 49 +UC_RH850_REG_CTBP = 52 +UC_RH850_REG_EIWR = 60 +UC_RH850_REG_FEWR = 61 +UC_RH850_REG_BSEL = 63 + +# RH850 system regusters, selection ID 1 +UC_RH850_REG_MCFG0 = 64 +UC_RH850_REG_RBASE = 65 +UC_RH850_REG_EBASE = 66 +UC_RH850_REG_INTBP = 67 +UC_RH850_REG_MCTL = 68 +UC_RH850_REG_PID = 69 +UC_RH850_REG_SCCFG = 75 +UC_RH850_REG_SCBP = 76 + +# RH850 system registers, selection ID 2 +UC_RH850_REG_HTCFG0 = 96 +UC_RH850_REG_MEA = 102 +UC_RH850_REG_ASID = 103 +UC_RH850_REG_MEI = 104 +UC_RH850_REG_PC = 288 +UC_RH850_REG_ENDING = 289 + +# RH8509 Registers aliases. + +UC_RH850_REG_ZERO = 0 +UC_RH850_REG_SP = 2 +UC_RH850_REG_EP = 30 +UC_RH850_REG_LP = 31 diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/riscv_const.py b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/riscv_const.py new file mode 100644 index 0000000000..1765bfdb73 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/riscv_const.py @@ -0,0 +1,285 @@ +# For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT [riscv_const.py] + +# RISCV32 CPU + +UC_CPU_RISCV32_ANY = 0 +UC_CPU_RISCV32_BASE32 = 1 +UC_CPU_RISCV32_SIFIVE_E31 = 2 +UC_CPU_RISCV32_SIFIVE_U34 = 3 +UC_CPU_RISCV32_ENDING = 4 + +# RISCV64 CPU + +UC_CPU_RISCV64_ANY = 0 +UC_CPU_RISCV64_BASE64 = 1 +UC_CPU_RISCV64_SIFIVE_E51 = 2 +UC_CPU_RISCV64_SIFIVE_U54 = 3 +UC_CPU_RISCV64_ENDING = 4 + +# RISCV registers + +UC_RISCV_REG_INVALID = 0 + +# General purpose registers +UC_RISCV_REG_X0 = 1 +UC_RISCV_REG_X1 = 2 +UC_RISCV_REG_X2 = 3 +UC_RISCV_REG_X3 = 4 +UC_RISCV_REG_X4 = 5 +UC_RISCV_REG_X5 = 6 +UC_RISCV_REG_X6 = 7 +UC_RISCV_REG_X7 = 8 +UC_RISCV_REG_X8 = 9 +UC_RISCV_REG_X9 = 10 +UC_RISCV_REG_X10 = 11 +UC_RISCV_REG_X11 = 12 +UC_RISCV_REG_X12 = 13 +UC_RISCV_REG_X13 = 14 +UC_RISCV_REG_X14 = 15 +UC_RISCV_REG_X15 = 16 +UC_RISCV_REG_X16 = 17 +UC_RISCV_REG_X17 = 18 +UC_RISCV_REG_X18 = 19 +UC_RISCV_REG_X19 = 20 +UC_RISCV_REG_X20 = 21 +UC_RISCV_REG_X21 = 22 +UC_RISCV_REG_X22 = 23 +UC_RISCV_REG_X23 = 24 +UC_RISCV_REG_X24 = 25 +UC_RISCV_REG_X25 = 26 +UC_RISCV_REG_X26 = 27 +UC_RISCV_REG_X27 = 28 +UC_RISCV_REG_X28 = 29 +UC_RISCV_REG_X29 = 30 +UC_RISCV_REG_X30 = 31 +UC_RISCV_REG_X31 = 32 + +# RISCV CSR +UC_RISCV_REG_USTATUS = 33 +UC_RISCV_REG_UIE = 34 +UC_RISCV_REG_UTVEC = 35 +UC_RISCV_REG_USCRATCH = 36 +UC_RISCV_REG_UEPC = 37 +UC_RISCV_REG_UCAUSE = 38 +UC_RISCV_REG_UTVAL = 39 +UC_RISCV_REG_UIP = 40 +UC_RISCV_REG_FFLAGS = 41 +UC_RISCV_REG_FRM = 42 +UC_RISCV_REG_FCSR = 43 +UC_RISCV_REG_CYCLE = 44 +UC_RISCV_REG_TIME = 45 +UC_RISCV_REG_INSTRET = 46 +UC_RISCV_REG_HPMCOUNTER3 = 47 +UC_RISCV_REG_HPMCOUNTER4 = 48 +UC_RISCV_REG_HPMCOUNTER5 = 49 +UC_RISCV_REG_HPMCOUNTER6 = 50 +UC_RISCV_REG_HPMCOUNTER7 = 51 +UC_RISCV_REG_HPMCOUNTER8 = 52 +UC_RISCV_REG_HPMCOUNTER9 = 53 +UC_RISCV_REG_HPMCOUNTER10 = 54 +UC_RISCV_REG_HPMCOUNTER11 = 55 +UC_RISCV_REG_HPMCOUNTER12 = 56 +UC_RISCV_REG_HPMCOUNTER13 = 57 +UC_RISCV_REG_HPMCOUNTER14 = 58 +UC_RISCV_REG_HPMCOUNTER15 = 59 +UC_RISCV_REG_HPMCOUNTER16 = 60 +UC_RISCV_REG_HPMCOUNTER17 = 61 +UC_RISCV_REG_HPMCOUNTER18 = 62 +UC_RISCV_REG_HPMCOUNTER19 = 63 +UC_RISCV_REG_HPMCOUNTER20 = 64 +UC_RISCV_REG_HPMCOUNTER21 = 65 +UC_RISCV_REG_HPMCOUNTER22 = 66 +UC_RISCV_REG_HPMCOUNTER23 = 67 +UC_RISCV_REG_HPMCOUNTER24 = 68 +UC_RISCV_REG_HPMCOUNTER25 = 69 +UC_RISCV_REG_HPMCOUNTER26 = 70 +UC_RISCV_REG_HPMCOUNTER27 = 71 +UC_RISCV_REG_HPMCOUNTER28 = 72 +UC_RISCV_REG_HPMCOUNTER29 = 73 +UC_RISCV_REG_HPMCOUNTER30 = 74 +UC_RISCV_REG_HPMCOUNTER31 = 75 +UC_RISCV_REG_CYCLEH = 76 +UC_RISCV_REG_TIMEH = 77 +UC_RISCV_REG_INSTRETH = 78 +UC_RISCV_REG_HPMCOUNTER3H = 79 +UC_RISCV_REG_HPMCOUNTER4H = 80 +UC_RISCV_REG_HPMCOUNTER5H = 81 +UC_RISCV_REG_HPMCOUNTER6H = 82 +UC_RISCV_REG_HPMCOUNTER7H = 83 +UC_RISCV_REG_HPMCOUNTER8H = 84 +UC_RISCV_REG_HPMCOUNTER9H = 85 +UC_RISCV_REG_HPMCOUNTER10H = 86 +UC_RISCV_REG_HPMCOUNTER11H = 87 +UC_RISCV_REG_HPMCOUNTER12H = 88 +UC_RISCV_REG_HPMCOUNTER13H = 89 +UC_RISCV_REG_HPMCOUNTER14H = 90 +UC_RISCV_REG_HPMCOUNTER15H = 91 +UC_RISCV_REG_HPMCOUNTER16H = 92 +UC_RISCV_REG_HPMCOUNTER17H = 93 +UC_RISCV_REG_HPMCOUNTER18H = 94 +UC_RISCV_REG_HPMCOUNTER19H = 95 +UC_RISCV_REG_HPMCOUNTER20H = 96 +UC_RISCV_REG_HPMCOUNTER21H = 97 +UC_RISCV_REG_HPMCOUNTER22H = 98 +UC_RISCV_REG_HPMCOUNTER23H = 99 +UC_RISCV_REG_HPMCOUNTER24H = 100 +UC_RISCV_REG_HPMCOUNTER25H = 101 +UC_RISCV_REG_HPMCOUNTER26H = 102 +UC_RISCV_REG_HPMCOUNTER27H = 103 +UC_RISCV_REG_HPMCOUNTER28H = 104 +UC_RISCV_REG_HPMCOUNTER29H = 105 +UC_RISCV_REG_HPMCOUNTER30H = 106 +UC_RISCV_REG_HPMCOUNTER31H = 107 +UC_RISCV_REG_MCYCLE = 108 +UC_RISCV_REG_MINSTRET = 109 +UC_RISCV_REG_MCYCLEH = 110 +UC_RISCV_REG_MINSTRETH = 111 +UC_RISCV_REG_MVENDORID = 112 +UC_RISCV_REG_MARCHID = 113 +UC_RISCV_REG_MIMPID = 114 +UC_RISCV_REG_MHARTID = 115 +UC_RISCV_REG_MSTATUS = 116 +UC_RISCV_REG_MISA = 117 +UC_RISCV_REG_MEDELEG = 118 +UC_RISCV_REG_MIDELEG = 119 +UC_RISCV_REG_MIE = 120 +UC_RISCV_REG_MTVEC = 121 +UC_RISCV_REG_MCOUNTEREN = 122 +UC_RISCV_REG_MSTATUSH = 123 +UC_RISCV_REG_MUCOUNTEREN = 124 +UC_RISCV_REG_MSCOUNTEREN = 125 +UC_RISCV_REG_MHCOUNTEREN = 126 +UC_RISCV_REG_MSCRATCH = 127 +UC_RISCV_REG_MEPC = 128 +UC_RISCV_REG_MCAUSE = 129 +UC_RISCV_REG_MTVAL = 130 +UC_RISCV_REG_MIP = 131 +UC_RISCV_REG_MBADADDR = 132 +UC_RISCV_REG_SSTATUS = 133 +UC_RISCV_REG_SEDELEG = 134 +UC_RISCV_REG_SIDELEG = 135 +UC_RISCV_REG_SIE = 136 +UC_RISCV_REG_STVEC = 137 +UC_RISCV_REG_SCOUNTEREN = 138 +UC_RISCV_REG_SSCRATCH = 139 +UC_RISCV_REG_SEPC = 140 +UC_RISCV_REG_SCAUSE = 141 +UC_RISCV_REG_STVAL = 142 +UC_RISCV_REG_SIP = 143 +UC_RISCV_REG_SBADADDR = 144 +UC_RISCV_REG_SPTBR = 145 +UC_RISCV_REG_SATP = 146 +UC_RISCV_REG_HSTATUS = 147 +UC_RISCV_REG_HEDELEG = 148 +UC_RISCV_REG_HIDELEG = 149 +UC_RISCV_REG_HIE = 150 +UC_RISCV_REG_HCOUNTEREN = 151 +UC_RISCV_REG_HTVAL = 152 +UC_RISCV_REG_HIP = 153 +UC_RISCV_REG_HTINST = 154 +UC_RISCV_REG_HGATP = 155 +UC_RISCV_REG_HTIMEDELTA = 156 +UC_RISCV_REG_HTIMEDELTAH = 157 + +# Floating-point registers +UC_RISCV_REG_F0 = 158 +UC_RISCV_REG_F1 = 159 +UC_RISCV_REG_F2 = 160 +UC_RISCV_REG_F3 = 161 +UC_RISCV_REG_F4 = 162 +UC_RISCV_REG_F5 = 163 +UC_RISCV_REG_F6 = 164 +UC_RISCV_REG_F7 = 165 +UC_RISCV_REG_F8 = 166 +UC_RISCV_REG_F9 = 167 +UC_RISCV_REG_F10 = 168 +UC_RISCV_REG_F11 = 169 +UC_RISCV_REG_F12 = 170 +UC_RISCV_REG_F13 = 171 +UC_RISCV_REG_F14 = 172 +UC_RISCV_REG_F15 = 173 +UC_RISCV_REG_F16 = 174 +UC_RISCV_REG_F17 = 175 +UC_RISCV_REG_F18 = 176 +UC_RISCV_REG_F19 = 177 +UC_RISCV_REG_F20 = 178 +UC_RISCV_REG_F21 = 179 +UC_RISCV_REG_F22 = 180 +UC_RISCV_REG_F23 = 181 +UC_RISCV_REG_F24 = 182 +UC_RISCV_REG_F25 = 183 +UC_RISCV_REG_F26 = 184 +UC_RISCV_REG_F27 = 185 +UC_RISCV_REG_F28 = 186 +UC_RISCV_REG_F29 = 187 +UC_RISCV_REG_F30 = 188 +UC_RISCV_REG_F31 = 189 +UC_RISCV_REG_PC = 190 +UC_RISCV_REG_ENDING = 191 + +# Alias registers +UC_RISCV_REG_ZERO = 1 +UC_RISCV_REG_RA = 2 +UC_RISCV_REG_SP = 3 +UC_RISCV_REG_GP = 4 +UC_RISCV_REG_TP = 5 +UC_RISCV_REG_T0 = 6 +UC_RISCV_REG_T1 = 7 +UC_RISCV_REG_T2 = 8 +UC_RISCV_REG_S0 = 9 +UC_RISCV_REG_FP = 9 +UC_RISCV_REG_S1 = 10 +UC_RISCV_REG_A0 = 11 +UC_RISCV_REG_A1 = 12 +UC_RISCV_REG_A2 = 13 +UC_RISCV_REG_A3 = 14 +UC_RISCV_REG_A4 = 15 +UC_RISCV_REG_A5 = 16 +UC_RISCV_REG_A6 = 17 +UC_RISCV_REG_A7 = 18 +UC_RISCV_REG_S2 = 19 +UC_RISCV_REG_S3 = 20 +UC_RISCV_REG_S4 = 21 +UC_RISCV_REG_S5 = 22 +UC_RISCV_REG_S6 = 23 +UC_RISCV_REG_S7 = 24 +UC_RISCV_REG_S8 = 25 +UC_RISCV_REG_S9 = 26 +UC_RISCV_REG_S10 = 27 +UC_RISCV_REG_S11 = 28 +UC_RISCV_REG_T3 = 29 +UC_RISCV_REG_T4 = 30 +UC_RISCV_REG_T5 = 31 +UC_RISCV_REG_T6 = 32 +UC_RISCV_REG_FT0 = 158 +UC_RISCV_REG_FT1 = 159 +UC_RISCV_REG_FT2 = 160 +UC_RISCV_REG_FT3 = 161 +UC_RISCV_REG_FT4 = 162 +UC_RISCV_REG_FT5 = 163 +UC_RISCV_REG_FT6 = 164 +UC_RISCV_REG_FT7 = 165 +UC_RISCV_REG_FS0 = 166 +UC_RISCV_REG_FS1 = 167 +UC_RISCV_REG_FA0 = 168 +UC_RISCV_REG_FA1 = 169 +UC_RISCV_REG_FA2 = 170 +UC_RISCV_REG_FA3 = 171 +UC_RISCV_REG_FA4 = 172 +UC_RISCV_REG_FA5 = 173 +UC_RISCV_REG_FA6 = 174 +UC_RISCV_REG_FA7 = 175 +UC_RISCV_REG_FS2 = 176 +UC_RISCV_REG_FS3 = 177 +UC_RISCV_REG_FS4 = 178 +UC_RISCV_REG_FS5 = 179 +UC_RISCV_REG_FS6 = 180 +UC_RISCV_REG_FS7 = 181 +UC_RISCV_REG_FS8 = 182 +UC_RISCV_REG_FS9 = 183 +UC_RISCV_REG_FS10 = 184 +UC_RISCV_REG_FS11 = 185 +UC_RISCV_REG_FT8 = 186 +UC_RISCV_REG_FT9 = 187 +UC_RISCV_REG_FT10 = 188 +UC_RISCV_REG_FT11 = 189 diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/s390x_const.py b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/s390x_const.py new file mode 100644 index 0000000000..1674736d9d --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/s390x_const.py @@ -0,0 +1,122 @@ +# For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT [s390x_const.py] + +# S390X CPU + +UC_CPU_S390X_Z900 = 0 +UC_CPU_S390X_Z900_2 = 1 +UC_CPU_S390X_Z900_3 = 2 +UC_CPU_S390X_Z800 = 3 +UC_CPU_S390X_Z990 = 4 +UC_CPU_S390X_Z990_2 = 5 +UC_CPU_S390X_Z990_3 = 6 +UC_CPU_S390X_Z890 = 7 +UC_CPU_S390X_Z990_4 = 8 +UC_CPU_S390X_Z890_2 = 9 +UC_CPU_S390X_Z990_5 = 10 +UC_CPU_S390X_Z890_3 = 11 +UC_CPU_S390X_Z9EC = 12 +UC_CPU_S390X_Z9EC_2 = 13 +UC_CPU_S390X_Z9BC = 14 +UC_CPU_S390X_Z9EC_3 = 15 +UC_CPU_S390X_Z9BC_2 = 16 +UC_CPU_S390X_Z10EC = 17 +UC_CPU_S390X_Z10EC_2 = 18 +UC_CPU_S390X_Z10BC = 19 +UC_CPU_S390X_Z10EC_3 = 20 +UC_CPU_S390X_Z10BC_2 = 21 +UC_CPU_S390X_Z196 = 22 +UC_CPU_S390X_Z196_2 = 23 +UC_CPU_S390X_Z114 = 24 +UC_CPU_S390X_ZEC12 = 25 +UC_CPU_S390X_ZEC12_2 = 26 +UC_CPU_S390X_ZBC12 = 27 +UC_CPU_S390X_Z13 = 28 +UC_CPU_S390X_Z13_2 = 29 +UC_CPU_S390X_Z13S = 30 +UC_CPU_S390X_Z14 = 31 +UC_CPU_S390X_Z14_2 = 32 +UC_CPU_S390X_Z14ZR1 = 33 +UC_CPU_S390X_GEN15A = 34 +UC_CPU_S390X_GEN15B = 35 +UC_CPU_S390X_QEMU = 36 +UC_CPU_S390X_MAX = 37 +UC_CPU_S390X_ENDING = 38 + +# S390X registers + +UC_S390X_REG_INVALID = 0 + +# General purpose registers +UC_S390X_REG_R0 = 1 +UC_S390X_REG_R1 = 2 +UC_S390X_REG_R2 = 3 +UC_S390X_REG_R3 = 4 +UC_S390X_REG_R4 = 5 +UC_S390X_REG_R5 = 6 +UC_S390X_REG_R6 = 7 +UC_S390X_REG_R7 = 8 +UC_S390X_REG_R8 = 9 +UC_S390X_REG_R9 = 10 +UC_S390X_REG_R10 = 11 +UC_S390X_REG_R11 = 12 +UC_S390X_REG_R12 = 13 +UC_S390X_REG_R13 = 14 +UC_S390X_REG_R14 = 15 +UC_S390X_REG_R15 = 16 + +# Floating point registers +UC_S390X_REG_F0 = 17 +UC_S390X_REG_F1 = 18 +UC_S390X_REG_F2 = 19 +UC_S390X_REG_F3 = 20 +UC_S390X_REG_F4 = 21 +UC_S390X_REG_F5 = 22 +UC_S390X_REG_F6 = 23 +UC_S390X_REG_F7 = 24 +UC_S390X_REG_F8 = 25 +UC_S390X_REG_F9 = 26 +UC_S390X_REG_F10 = 27 +UC_S390X_REG_F11 = 28 +UC_S390X_REG_F12 = 29 +UC_S390X_REG_F13 = 30 +UC_S390X_REG_F14 = 31 +UC_S390X_REG_F15 = 32 +UC_S390X_REG_F16 = 33 +UC_S390X_REG_F17 = 34 +UC_S390X_REG_F18 = 35 +UC_S390X_REG_F19 = 36 +UC_S390X_REG_F20 = 37 +UC_S390X_REG_F21 = 38 +UC_S390X_REG_F22 = 39 +UC_S390X_REG_F23 = 40 +UC_S390X_REG_F24 = 41 +UC_S390X_REG_F25 = 42 +UC_S390X_REG_F26 = 43 +UC_S390X_REG_F27 = 44 +UC_S390X_REG_F28 = 45 +UC_S390X_REG_F29 = 46 +UC_S390X_REG_F30 = 47 +UC_S390X_REG_F31 = 48 + +# Access registers +UC_S390X_REG_A0 = 49 +UC_S390X_REG_A1 = 50 +UC_S390X_REG_A2 = 51 +UC_S390X_REG_A3 = 52 +UC_S390X_REG_A4 = 53 +UC_S390X_REG_A5 = 54 +UC_S390X_REG_A6 = 55 +UC_S390X_REG_A7 = 56 +UC_S390X_REG_A8 = 57 +UC_S390X_REG_A9 = 58 +UC_S390X_REG_A10 = 59 +UC_S390X_REG_A11 = 60 +UC_S390X_REG_A12 = 61 +UC_S390X_REG_A13 = 62 +UC_S390X_REG_A14 = 63 +UC_S390X_REG_A15 = 64 +UC_S390X_REG_PC = 65 +UC_S390X_REG_PSWM = 66 +UC_S390X_REG_ENDING = 67 + +# Alias registers diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/sparc_const.py b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/sparc_const.py new file mode 100644 index 0000000000..77b8c004c7 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/sparc_const.py @@ -0,0 +1,134 @@ +# For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT [sparc_const.py] + +# SPARC32 CPU + +UC_CPU_SPARC32_FUJITSU_MB86904 = 0 +UC_CPU_SPARC32_FUJITSU_MB86907 = 1 +UC_CPU_SPARC32_TI_MICROSPARC_I = 2 +UC_CPU_SPARC32_TI_MICROSPARC_II = 3 +UC_CPU_SPARC32_TI_MICROSPARC_IIEP = 4 +UC_CPU_SPARC32_TI_SUPERSPARC_40 = 5 +UC_CPU_SPARC32_TI_SUPERSPARC_50 = 6 +UC_CPU_SPARC32_TI_SUPERSPARC_51 = 7 +UC_CPU_SPARC32_TI_SUPERSPARC_60 = 8 +UC_CPU_SPARC32_TI_SUPERSPARC_61 = 9 +UC_CPU_SPARC32_TI_SUPERSPARC_II = 10 +UC_CPU_SPARC32_LEON2 = 11 +UC_CPU_SPARC32_LEON3 = 12 +UC_CPU_SPARC32_ENDING = 13 + +# SPARC64 CPU + +UC_CPU_SPARC64_FUJITSU = 0 +UC_CPU_SPARC64_FUJITSU_III = 1 +UC_CPU_SPARC64_FUJITSU_IV = 2 +UC_CPU_SPARC64_FUJITSU_V = 3 +UC_CPU_SPARC64_TI_ULTRASPARC_I = 4 +UC_CPU_SPARC64_TI_ULTRASPARC_II = 5 +UC_CPU_SPARC64_TI_ULTRASPARC_III = 6 +UC_CPU_SPARC64_TI_ULTRASPARC_IIE = 7 +UC_CPU_SPARC64_SUN_ULTRASPARC_III = 8 +UC_CPU_SPARC64_SUN_ULTRASPARC_III_CU = 9 +UC_CPU_SPARC64_SUN_ULTRASPARC_IIII = 10 +UC_CPU_SPARC64_SUN_ULTRASPARC_IV = 11 +UC_CPU_SPARC64_SUN_ULTRASPARC_IV_PLUS = 12 +UC_CPU_SPARC64_SUN_ULTRASPARC_IIII_PLUS = 13 +UC_CPU_SPARC64_SUN_ULTRASPARC_T1 = 14 +UC_CPU_SPARC64_SUN_ULTRASPARC_T2 = 15 +UC_CPU_SPARC64_NEC_ULTRASPARC_I = 16 +UC_CPU_SPARC64_ENDING = 17 + +# SPARC registers + +UC_SPARC_REG_INVALID = 0 +UC_SPARC_REG_F0 = 1 +UC_SPARC_REG_F1 = 2 +UC_SPARC_REG_F2 = 3 +UC_SPARC_REG_F3 = 4 +UC_SPARC_REG_F4 = 5 +UC_SPARC_REG_F5 = 6 +UC_SPARC_REG_F6 = 7 +UC_SPARC_REG_F7 = 8 +UC_SPARC_REG_F8 = 9 +UC_SPARC_REG_F9 = 10 +UC_SPARC_REG_F10 = 11 +UC_SPARC_REG_F11 = 12 +UC_SPARC_REG_F12 = 13 +UC_SPARC_REG_F13 = 14 +UC_SPARC_REG_F14 = 15 +UC_SPARC_REG_F15 = 16 +UC_SPARC_REG_F16 = 17 +UC_SPARC_REG_F17 = 18 +UC_SPARC_REG_F18 = 19 +UC_SPARC_REG_F19 = 20 +UC_SPARC_REG_F20 = 21 +UC_SPARC_REG_F21 = 22 +UC_SPARC_REG_F22 = 23 +UC_SPARC_REG_F23 = 24 +UC_SPARC_REG_F24 = 25 +UC_SPARC_REG_F25 = 26 +UC_SPARC_REG_F26 = 27 +UC_SPARC_REG_F27 = 28 +UC_SPARC_REG_F28 = 29 +UC_SPARC_REG_F29 = 30 +UC_SPARC_REG_F30 = 31 +UC_SPARC_REG_F31 = 32 +UC_SPARC_REG_F32 = 33 +UC_SPARC_REG_F34 = 34 +UC_SPARC_REG_F36 = 35 +UC_SPARC_REG_F38 = 36 +UC_SPARC_REG_F40 = 37 +UC_SPARC_REG_F42 = 38 +UC_SPARC_REG_F44 = 39 +UC_SPARC_REG_F46 = 40 +UC_SPARC_REG_F48 = 41 +UC_SPARC_REG_F50 = 42 +UC_SPARC_REG_F52 = 43 +UC_SPARC_REG_F54 = 44 +UC_SPARC_REG_F56 = 45 +UC_SPARC_REG_F58 = 46 +UC_SPARC_REG_F60 = 47 +UC_SPARC_REG_F62 = 48 +UC_SPARC_REG_FCC0 = 49 +UC_SPARC_REG_FCC1 = 50 +UC_SPARC_REG_FCC2 = 51 +UC_SPARC_REG_FCC3 = 52 +UC_SPARC_REG_G0 = 53 +UC_SPARC_REG_G1 = 54 +UC_SPARC_REG_G2 = 55 +UC_SPARC_REG_G3 = 56 +UC_SPARC_REG_G4 = 57 +UC_SPARC_REG_G5 = 58 +UC_SPARC_REG_G6 = 59 +UC_SPARC_REG_G7 = 60 +UC_SPARC_REG_I0 = 61 +UC_SPARC_REG_I1 = 62 +UC_SPARC_REG_I2 = 63 +UC_SPARC_REG_I3 = 64 +UC_SPARC_REG_I4 = 65 +UC_SPARC_REG_I5 = 66 +UC_SPARC_REG_FP = 67 +UC_SPARC_REG_I7 = 68 +UC_SPARC_REG_ICC = 69 +UC_SPARC_REG_L0 = 70 +UC_SPARC_REG_L1 = 71 +UC_SPARC_REG_L2 = 72 +UC_SPARC_REG_L3 = 73 +UC_SPARC_REG_L4 = 74 +UC_SPARC_REG_L5 = 75 +UC_SPARC_REG_L6 = 76 +UC_SPARC_REG_L7 = 77 +UC_SPARC_REG_O0 = 78 +UC_SPARC_REG_O1 = 79 +UC_SPARC_REG_O2 = 80 +UC_SPARC_REG_O3 = 81 +UC_SPARC_REG_O4 = 82 +UC_SPARC_REG_O5 = 83 +UC_SPARC_REG_SP = 84 +UC_SPARC_REG_O7 = 85 +UC_SPARC_REG_Y = 86 +UC_SPARC_REG_XCC = 87 +UC_SPARC_REG_PC = 88 +UC_SPARC_REG_ENDING = 89 +UC_SPARC_REG_O6 = 84 +UC_SPARC_REG_I6 = 67 diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/tricore_const.py b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/tricore_const.py new file mode 100644 index 0000000000..2453e0035b --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/tricore_const.py @@ -0,0 +1,124 @@ +# For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT [tricore_const.py] + +# TRICORE CPU + +UC_CPU_TRICORE_TC1796 = 0 +UC_CPU_TRICORE_TC1797 = 1 +UC_CPU_TRICORE_TC27X = 2 +UC_CPU_TRICORE_ENDING = 3 + +# TRICORE registers + +UC_TRICORE_REG_INVALID = 0 +UC_TRICORE_REG_A0 = 1 +UC_TRICORE_REG_A1 = 2 +UC_TRICORE_REG_A2 = 3 +UC_TRICORE_REG_A3 = 4 +UC_TRICORE_REG_A4 = 5 +UC_TRICORE_REG_A5 = 6 +UC_TRICORE_REG_A6 = 7 +UC_TRICORE_REG_A7 = 8 +UC_TRICORE_REG_A8 = 9 +UC_TRICORE_REG_A9 = 10 +UC_TRICORE_REG_A10 = 11 +UC_TRICORE_REG_A11 = 12 +UC_TRICORE_REG_A12 = 13 +UC_TRICORE_REG_A13 = 14 +UC_TRICORE_REG_A14 = 15 +UC_TRICORE_REG_A15 = 16 +UC_TRICORE_REG_D0 = 17 +UC_TRICORE_REG_D1 = 18 +UC_TRICORE_REG_D2 = 19 +UC_TRICORE_REG_D3 = 20 +UC_TRICORE_REG_D4 = 21 +UC_TRICORE_REG_D5 = 22 +UC_TRICORE_REG_D6 = 23 +UC_TRICORE_REG_D7 = 24 +UC_TRICORE_REG_D8 = 25 +UC_TRICORE_REG_D9 = 26 +UC_TRICORE_REG_D10 = 27 +UC_TRICORE_REG_D11 = 28 +UC_TRICORE_REG_D12 = 29 +UC_TRICORE_REG_D13 = 30 +UC_TRICORE_REG_D14 = 31 +UC_TRICORE_REG_D15 = 32 +UC_TRICORE_REG_PCXI = 33 +UC_TRICORE_REG_PSW = 34 +UC_TRICORE_REG_PSW_USB_C = 35 +UC_TRICORE_REG_PSW_USB_V = 36 +UC_TRICORE_REG_PSW_USB_SV = 37 +UC_TRICORE_REG_PSW_USB_AV = 38 +UC_TRICORE_REG_PSW_USB_SAV = 39 +UC_TRICORE_REG_PC = 40 +UC_TRICORE_REG_SYSCON = 41 +UC_TRICORE_REG_CPU_ID = 42 +UC_TRICORE_REG_BIV = 43 +UC_TRICORE_REG_BTV = 44 +UC_TRICORE_REG_ISP = 45 +UC_TRICORE_REG_ICR = 46 +UC_TRICORE_REG_FCX = 47 +UC_TRICORE_REG_LCX = 48 +UC_TRICORE_REG_COMPAT = 49 +UC_TRICORE_REG_DPR0_U = 50 +UC_TRICORE_REG_DPR1_U = 51 +UC_TRICORE_REG_DPR2_U = 52 +UC_TRICORE_REG_DPR3_U = 53 +UC_TRICORE_REG_DPR0_L = 54 +UC_TRICORE_REG_DPR1_L = 55 +UC_TRICORE_REG_DPR2_L = 56 +UC_TRICORE_REG_DPR3_L = 57 +UC_TRICORE_REG_CPR0_U = 58 +UC_TRICORE_REG_CPR1_U = 59 +UC_TRICORE_REG_CPR2_U = 60 +UC_TRICORE_REG_CPR3_U = 61 +UC_TRICORE_REG_CPR0_L = 62 +UC_TRICORE_REG_CPR1_L = 63 +UC_TRICORE_REG_CPR2_L = 64 +UC_TRICORE_REG_CPR3_L = 65 +UC_TRICORE_REG_DPM0 = 66 +UC_TRICORE_REG_DPM1 = 67 +UC_TRICORE_REG_DPM2 = 68 +UC_TRICORE_REG_DPM3 = 69 +UC_TRICORE_REG_CPM0 = 70 +UC_TRICORE_REG_CPM1 = 71 +UC_TRICORE_REG_CPM2 = 72 +UC_TRICORE_REG_CPM3 = 73 +UC_TRICORE_REG_MMU_CON = 74 +UC_TRICORE_REG_MMU_ASI = 75 +UC_TRICORE_REG_MMU_TVA = 76 +UC_TRICORE_REG_MMU_TPA = 77 +UC_TRICORE_REG_MMU_TPX = 78 +UC_TRICORE_REG_MMU_TFA = 79 +UC_TRICORE_REG_BMACON = 80 +UC_TRICORE_REG_SMACON = 81 +UC_TRICORE_REG_DIEAR = 82 +UC_TRICORE_REG_DIETR = 83 +UC_TRICORE_REG_CCDIER = 84 +UC_TRICORE_REG_MIECON = 85 +UC_TRICORE_REG_PIEAR = 86 +UC_TRICORE_REG_PIETR = 87 +UC_TRICORE_REG_CCPIER = 88 +UC_TRICORE_REG_DBGSR = 89 +UC_TRICORE_REG_EXEVT = 90 +UC_TRICORE_REG_CREVT = 91 +UC_TRICORE_REG_SWEVT = 92 +UC_TRICORE_REG_TR0EVT = 93 +UC_TRICORE_REG_TR1EVT = 94 +UC_TRICORE_REG_DMS = 95 +UC_TRICORE_REG_DCX = 96 +UC_TRICORE_REG_DBGTCR = 97 +UC_TRICORE_REG_CCTRL = 98 +UC_TRICORE_REG_CCNT = 99 +UC_TRICORE_REG_ICNT = 100 +UC_TRICORE_REG_M1CNT = 101 +UC_TRICORE_REG_M2CNT = 102 +UC_TRICORE_REG_M3CNT = 103 +UC_TRICORE_REG_ENDING = 104 +UC_TRICORE_REG_GA0 = 1 +UC_TRICORE_REG_GA1 = 2 +UC_TRICORE_REG_GA8 = 9 +UC_TRICORE_REG_GA9 = 10 +UC_TRICORE_REG_SP = 11 +UC_TRICORE_REG_LR = 12 +UC_TRICORE_REG_IA = 16 +UC_TRICORE_REG_ID = 32 diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/unicorn.py b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/unicorn.py new file mode 100644 index 0000000000..2e6a938f43 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/unicorn.py @@ -0,0 +1,1051 @@ +# Unicorn Python bindings, by Nguyen Anh Quynnh +from __future__ import annotations +import ctypes +import ctypes.util +import distutils.sysconfig +from functools import wraps +from typing import Any, Callable, List, Tuple, Union +import pkg_resources +import inspect +import os.path +import sys +import weakref +import functools +from collections import namedtuple + +from . import x86_const, arm_const, arm64_const, unicorn_const as uc + +if not hasattr(sys.modules[__name__], "__file__"): + __file__ = inspect.getfile(inspect.currentframe()) + +_python2 = sys.version_info[0] < 3 +if _python2: + range = xrange + +_lib = { 'darwin': 'libunicorn.2.dylib', + 'win32': 'unicorn.dll', + 'cygwin': 'cygunicorn.dll', + 'linux': 'libunicorn.so.2', + 'linux2': 'libunicorn.so.2' } + + +# Windows DLL in dependency order +_all_windows_dlls = ( + "libwinpthread-1.dll", + "libgcc_s_seh-1.dll", + "libgcc_s_dw2-1.dll", +) + +_loaded_windows_dlls = set() + +def _load_win_support(path): + for dll in _all_windows_dlls: + if dll in _loaded_windows_dlls: + continue + + lib_file = os.path.join(path, dll) + if ('/' not in path and '\\' not in path) or os.path.exists(lib_file): + try: + #print('Trying to load Windows library', lib_file) + ctypes.cdll.LoadLibrary(lib_file) + #print('SUCCESS') + _loaded_windows_dlls.add(dll) + except OSError as e: + #print('FAIL to load %s' %lib_file, e) + continue + +# Initial attempt: load all dlls globally +if sys.platform in ('win32', 'cygwin'): + _load_win_support('') + +def _load_lib(path, lib_name): + try: + if sys.platform in ('win32', 'cygwin'): + _load_win_support(path) + + lib_file = os.path.join(path, lib_name) + dll = ctypes.cdll.LoadLibrary(lib_file) + #print('SUCCESS') + return dll + except OSError as e: + #print('FAIL to load %s' %lib_file, e) + return None + +_uc = None + +# Loading attempts, in order +# - user-provided environment variable +# - pkg_resources can get us the path to the local libraries +# - we can get the path to the local libraries by parsing our filename +# - global load +# - python's lib directory +# - last-gasp attempt at some hardcoded paths on darwin and linux + +_path_list = [os.getenv('LIBUNICORN_PATH', None), + pkg_resources.resource_filename(__name__, 'lib'), + os.path.join(os.path.split(__file__)[0], 'lib'), + '', + distutils.sysconfig.get_python_lib(), + "/usr/local/lib/" if sys.platform == 'darwin' else '/usr/lib64', + os.getenv('PATH', '')] + +#print(_path_list) +#print("-" * 80) + +for _path in _path_list: + if _path is None: continue + _uc = _load_lib(_path, _lib.get(sys.platform, "libunicorn.so")) + if _uc is not None: + break + +# Try to search old unicorn1 library without SONAME +if _uc is None: + for _path in _path_list: + if _path is None: + continue + + _uc = _load_lib(_path, "libunicorn.so") + if _uc is not None: + # In this case, show a warning for users + print("Found an old style dynamic library libunicorn.so, consider checking your installation", file=sys.stderr) + break + +if _uc is None: + raise ImportError("ERROR: fail to load the dynamic library.") + +__version__ = "%u.%u.%u" % (uc.UC_VERSION_MAJOR, uc.UC_VERSION_MINOR, uc.UC_VERSION_PATCH) + +# setup all the function prototype +def _setup_prototype(lib, fname, restype, *argtypes): + try: + getattr(lib, fname).restype = restype + getattr(lib, fname).argtypes = argtypes + except AttributeError: + raise ImportError("ERROR: Fail to setup some function prototypes. Make sure you have cleaned your unicorn1 installation.") + +ucerr = ctypes.c_int +uc_mode = ctypes.c_int +uc_arch = ctypes.c_int +uc_engine = ctypes.c_void_p +uc_context = ctypes.c_void_p +uc_hook_h = ctypes.c_size_t + +class _uc_mem_region(ctypes.Structure): + _fields_ = [ + ("begin", ctypes.c_uint64), + ("end", ctypes.c_uint64), + ("perms", ctypes.c_uint32), + ] + +class uc_tb(ctypes.Structure): + """"TranslationBlock""" + _fields_ = [ + ("pc", ctypes.c_uint64), + ("icount", ctypes.c_uint16), + ("size", ctypes.c_uint16) + ] + +_setup_prototype(_uc, "uc_version", ctypes.c_uint, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int)) +_setup_prototype(_uc, "uc_arch_supported", ctypes.c_bool, ctypes.c_int) +_setup_prototype(_uc, "uc_open", ucerr, ctypes.c_uint, ctypes.c_uint, ctypes.POINTER(uc_engine)) +_setup_prototype(_uc, "uc_close", ucerr, uc_engine) +_setup_prototype(_uc, "uc_strerror", ctypes.c_char_p, ucerr) +_setup_prototype(_uc, "uc_errno", ucerr, uc_engine) +_setup_prototype(_uc, "uc_reg_read", ucerr, uc_engine, ctypes.c_int, ctypes.c_void_p) +_setup_prototype(_uc, "uc_reg_write", ucerr, uc_engine, ctypes.c_int, ctypes.c_void_p) +_setup_prototype(_uc, "uc_mem_read", ucerr, uc_engine, ctypes.c_uint64, ctypes.POINTER(ctypes.c_char), ctypes.c_size_t) +_setup_prototype(_uc, "uc_mem_write", ucerr, uc_engine, ctypes.c_uint64, ctypes.POINTER(ctypes.c_char), ctypes.c_size_t) +_setup_prototype(_uc, "uc_emu_start", ucerr, uc_engine, ctypes.c_uint64, ctypes.c_uint64, ctypes.c_uint64, ctypes.c_size_t) +_setup_prototype(_uc, "uc_emu_stop", ucerr, uc_engine) +_setup_prototype(_uc, "uc_hook_del", ucerr, uc_engine, uc_hook_h) +_setup_prototype(_uc, "uc_mmio_map", ucerr, uc_engine, ctypes.c_uint64, ctypes.c_size_t, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p) +_setup_prototype(_uc, "uc_mem_map", ucerr, uc_engine, ctypes.c_uint64, ctypes.c_size_t, ctypes.c_uint32) +_setup_prototype(_uc, "uc_mem_map_ptr", ucerr, uc_engine, ctypes.c_uint64, ctypes.c_size_t, ctypes.c_uint32, ctypes.c_void_p) +_setup_prototype(_uc, "uc_mem_unmap", ucerr, uc_engine, ctypes.c_uint64, ctypes.c_size_t) +_setup_prototype(_uc, "uc_mem_protect", ucerr, uc_engine, ctypes.c_uint64, ctypes.c_size_t, ctypes.c_uint32) +_setup_prototype(_uc, "uc_query", ucerr, uc_engine, ctypes.c_uint32, ctypes.POINTER(ctypes.c_size_t)) +_setup_prototype(_uc, "uc_context_alloc", ucerr, uc_engine, ctypes.POINTER(uc_context)) +_setup_prototype(_uc, "uc_free", ucerr, ctypes.c_void_p) +_setup_prototype(_uc, "uc_context_save", ucerr, uc_engine, uc_context) +_setup_prototype(_uc, "uc_context_restore", ucerr, uc_engine, uc_context) +_setup_prototype(_uc, "uc_context_size", ctypes.c_size_t, uc_engine) +_setup_prototype(_uc, "uc_context_reg_read", ucerr, uc_context, ctypes.c_int, ctypes.c_void_p) +_setup_prototype(_uc, "uc_context_reg_write", ucerr, uc_context, ctypes.c_int, ctypes.c_void_p) +_setup_prototype(_uc, "uc_context_free", ucerr, uc_context) +_setup_prototype(_uc, "uc_mem_regions", ucerr, uc_engine, ctypes.POINTER(ctypes.POINTER(_uc_mem_region)), ctypes.POINTER(ctypes.c_uint32)) +# https://bugs.python.org/issue42880 +_setup_prototype(_uc, "uc_hook_add", ucerr, uc_engine, ctypes.POINTER(uc_hook_h), ctypes.c_int, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_uint64, ctypes.c_uint64) +_setup_prototype(_uc, "uc_ctl", ucerr, uc_engine, ctypes.c_int) + +UC_HOOK_CODE_CB = ctypes.CFUNCTYPE(None, uc_engine, ctypes.c_uint64, ctypes.c_size_t, ctypes.c_void_p) +UC_HOOK_INSN_INVALID_CB = ctypes.CFUNCTYPE(ctypes.c_bool, uc_engine, ctypes.c_void_p) +UC_HOOK_MEM_INVALID_CB = ctypes.CFUNCTYPE( + ctypes.c_bool, uc_engine, ctypes.c_int, + ctypes.c_uint64, ctypes.c_int, ctypes.c_int64, ctypes.c_void_p +) +UC_HOOK_MEM_ACCESS_CB = ctypes.CFUNCTYPE( + None, uc_engine, ctypes.c_int, + ctypes.c_uint64, ctypes.c_int, ctypes.c_int64, ctypes.c_void_p +) +UC_HOOK_INTR_CB = ctypes.CFUNCTYPE( + None, uc_engine, ctypes.c_uint32, ctypes.c_void_p +) +UC_HOOK_INSN_IN_CB = ctypes.CFUNCTYPE( + ctypes.c_uint32, uc_engine, ctypes.c_uint32, ctypes.c_int, ctypes.c_void_p +) +UC_HOOK_INSN_OUT_CB = ctypes.CFUNCTYPE( + None, uc_engine, ctypes.c_uint32, + ctypes.c_int, ctypes.c_uint32, ctypes.c_void_p +) +UC_HOOK_INSN_SYSCALL_CB = ctypes.CFUNCTYPE(None, uc_engine, ctypes.c_void_p) +UC_HOOK_INSN_SYS_CB = ctypes.CFUNCTYPE(ctypes.c_uint32, uc_engine, ctypes.c_uint32, ctypes.c_void_p, ctypes.c_void_p) +UC_HOOK_INSN_CPUID_CB = ctypes.CFUNCTYPE(ctypes.c_uint32, uc_engine, ctypes.c_void_p) +UC_MMIO_READ_CB = ctypes.CFUNCTYPE( + ctypes.c_uint64, uc_engine, ctypes.c_uint64, ctypes.c_int, ctypes.c_void_p +) +UC_MMIO_WRITE_CB = ctypes.CFUNCTYPE( + None, uc_engine, ctypes.c_uint64, ctypes.c_int, ctypes.c_uint64, ctypes.c_void_p +) +UC_HOOK_EDGE_GEN_CB = ctypes.CFUNCTYPE( + None, uc_engine, ctypes.POINTER(uc_tb), ctypes.POINTER(uc_tb), ctypes.c_void_p +) +UC_HOOK_TCG_OPCODE_CB = ctypes.CFUNCTYPE( + None, uc_engine, ctypes.c_uint64, ctypes.c_uint64, ctypes.c_uint64, ctypes.c_void_p +) + +# access to error code via @errno of UcError +class UcError(Exception): + def __init__(self, errno): + self.errno = errno + + def __str__(self): + return _uc.uc_strerror(self.errno).decode('ascii') + + +# return the core's version +def uc_version(): + major = ctypes.c_int() + minor = ctypes.c_int() + combined = _uc.uc_version(ctypes.byref(major), ctypes.byref(minor)) + return (major.value, minor.value, combined) + + +# return the binding's version +def version_bind(): + return ( + uc.UC_API_MAJOR, uc.UC_API_MINOR, + (uc.UC_API_MAJOR << 8) + uc.UC_API_MINOR, + ) + + +# check to see if this engine supports a particular arch +def uc_arch_supported(query): + return _uc.uc_arch_supported(query) + +ARMCPReg = Tuple[int, int, int, int, int, int, int] +ARM64CPReg = Tuple[int, int, int, int, int] +ARMCPRegValue = Tuple[int, int, int, int, int, int, int, int] +ARM64CPRegValue = Tuple[int, int, int, int, int, int] +X86MMRReg = Tuple[int, int, int, int] +X86FPReg = Tuple[int, int] + +# uc_reg_read/write and uc_context_reg_read/write. +def reg_read(reg_read_func, arch, reg_id, opt=None): + if arch == uc.UC_ARCH_X86: + if reg_id in [x86_const.UC_X86_REG_IDTR, x86_const.UC_X86_REG_GDTR, x86_const.UC_X86_REG_LDTR, x86_const.UC_X86_REG_TR]: + reg = uc_x86_mmr() + status = reg_read_func(reg_id, ctypes.byref(reg)) + if status != uc.UC_ERR_OK: + raise UcError(status) + return reg.selector, reg.base, reg.limit, reg.flags + if reg_id in range(x86_const.UC_X86_REG_FP0, x86_const.UC_X86_REG_FP0+8): + reg = uc_x86_float80() + status = reg_read_func(reg_id, ctypes.byref(reg)) + if status != uc.UC_ERR_OK: + raise UcError(status) + return reg.mantissa, reg.exponent + if reg_id in range(x86_const.UC_X86_REG_XMM0, x86_const.UC_X86_REG_XMM0+8): + reg = uc_x86_xmm() + status = reg_read_func(reg_id, ctypes.byref(reg)) + if status != uc.UC_ERR_OK: + raise UcError(status) + return reg.low_qword | (reg.high_qword << 64) + if reg_id in range(x86_const.UC_X86_REG_YMM0, x86_const.UC_X86_REG_YMM0+16): + reg = uc_x86_ymm() + status = reg_read_func(reg_id, ctypes.byref(reg)) + if status != uc.UC_ERR_OK: + raise UcError(status) + return reg.first_qword | (reg.second_qword << 64) | (reg.third_qword << 128) | (reg.fourth_qword << 192) + if reg_id is x86_const.UC_X86_REG_MSR: + if opt is None: + raise UcError(uc.UC_ERR_ARG) + reg = uc_x86_msr() + reg.rid = opt + status = reg_read_func(reg_id, ctypes.byref(reg)) + if status != uc.UC_ERR_OK: + raise UcError(status) + return reg.value + + if arch == uc.UC_ARCH_ARM: + if reg_id == arm_const.UC_ARM_REG_CP_REG: + reg = uc_arm_cp_reg() + if not isinstance(opt, tuple) or len(opt) != 7: + raise UcError(uc.UC_ERR_ARG) + reg.cp, reg.is64, reg.sec, reg.crn, reg.crm, reg.opc1, reg.opc2 = opt + status = reg_read_func(reg_id, ctypes.byref(reg)) + if status != uc.UC_ERR_OK: + raise UcError(status) + return reg.val + + if arch == uc.UC_ARCH_ARM64: + if reg_id == arm64_const.UC_ARM64_REG_CP_REG: + reg = uc_arm64_cp_reg() + if not isinstance(opt, tuple) or len(opt) != 5: + raise UcError(uc.UC_ERR_ARG) + reg.crn, reg.crm, reg.op0, reg.op1, reg.op2 = opt + status = reg_read_func(reg_id, ctypes.byref(reg)) + if status != uc.UC_ERR_OK: + raise UcError(status) + return reg.val + + elif reg_id in range(arm64_const.UC_ARM64_REG_Q0, arm64_const.UC_ARM64_REG_Q31+1) or reg_id in range(arm64_const.UC_ARM64_REG_V0, arm64_const.UC_ARM64_REG_V31+1): + reg = uc_arm64_neon128() + status = reg_read_func(reg_id, ctypes.byref(reg)) + if status != uc.UC_ERR_OK: + raise UcError(status) + return reg.low_qword | (reg.high_qword << 64) + + # read to 64bit number to be safe + reg = ctypes.c_uint64(0) + status = reg_read_func(reg_id, ctypes.byref(reg)) + if status != uc.UC_ERR_OK: + raise UcError(status) + return reg.value + +def reg_write(reg_write_func, arch, reg_id, value): + reg = None + + if arch == uc.UC_ARCH_X86: + if reg_id in [x86_const.UC_X86_REG_IDTR, x86_const.UC_X86_REG_GDTR, x86_const.UC_X86_REG_LDTR, x86_const.UC_X86_REG_TR]: + assert isinstance(value, tuple) and len(value) == 4 + reg = uc_x86_mmr() + reg.selector = value[0] + reg.base = value[1] + reg.limit = value[2] + reg.flags = value[3] + if reg_id in range(x86_const.UC_X86_REG_FP0, x86_const.UC_X86_REG_FP0+8): + reg = uc_x86_float80() + reg.mantissa = value[0] + reg.exponent = value[1] + if reg_id in range(x86_const.UC_X86_REG_XMM0, x86_const.UC_X86_REG_XMM0+8): + reg = uc_x86_xmm() + reg.low_qword = value & 0xffffffffffffffff + reg.high_qword = value >> 64 + if reg_id in range(x86_const.UC_X86_REG_YMM0, x86_const.UC_X86_REG_YMM0+16): + reg = uc_x86_ymm() + reg.first_qword = value & 0xffffffffffffffff + reg.second_qword = (value >> 64) & 0xffffffffffffffff + reg.third_qword = (value >> 128) & 0xffffffffffffffff + reg.fourth_qword = value >> 192 + if reg_id is x86_const.UC_X86_REG_MSR: + reg = uc_x86_msr() + reg.rid = value[0] + reg.value = value[1] + + if arch == uc.UC_ARCH_ARM64: + if reg_id in range(arm64_const.UC_ARM64_REG_Q0, arm64_const.UC_ARM64_REG_Q31+1) or reg_id in range(arm64_const.UC_ARM64_REG_V0, arm64_const.UC_ARM64_REG_V31+1): + reg = uc_arm64_neon128() + reg.low_qword = value & 0xffffffffffffffff + reg.high_qword = value >> 64 + elif reg_id == arm64_const.UC_ARM64_REG_CP_REG: + reg = uc_arm64_cp_reg() + if not isinstance(value, tuple) or len(value) != 6: + raise UcError(uc.UC_ERR_ARG) + reg.crn, reg.crm, reg.op0, reg.op1, reg.op2, reg.val = value + + if arch == uc.UC_ARCH_ARM: + if reg_id == arm_const.UC_ARM_REG_CP_REG: + reg = uc_arm_cp_reg() + if not isinstance(value, tuple) or len(value) != 8: + raise UcError(uc.UC_ERR_ARG) + reg.cp, reg.is64, reg.sec, reg.crn, reg.crm, reg.opc1, reg.opc2, reg.val = value + + if reg is None: + # convert to 64bit number to be safe + reg = ctypes.c_uint64(value) + + status = reg_write_func(reg_id, ctypes.byref(reg)) + if status != uc.UC_ERR_OK: + raise UcError(status) + + return + +def _catch_hook_exception(func): + @wraps(func) + def wrapper(self, *args, **kwargs): + """Catches exceptions raised in hook functions. + + If an exception is raised, it is saved to the Uc object and a call to stop + emulation is issued. + """ + try: + return func(self, *args, **kwargs) + except Exception as e: + # If multiple hooks raise exceptions, just use the first one + if self._hook_exception is None: + self._hook_exception = e + + self.emu_stop() + + return wrapper + + +class uc_arm_cp_reg(ctypes.Structure): + """ARM coprocessors registers for instructions MRC, MCR, MRRC, MCRR""" + _fields_ = [ + ("cp", ctypes.c_uint32), + ("is64", ctypes.c_uint32), + ("sec", ctypes.c_uint32), + ("crn", ctypes.c_uint32), + ("crm", ctypes.c_uint32), + ("opc1", ctypes.c_uint32), + ("opc2", ctypes.c_uint32), + ("val", ctypes.c_uint64) + ] + +class uc_arm64_cp_reg(ctypes.Structure): + """ARM64 coprocessors registers for instructions MRS, MSR""" + _fields_ = [ + ("crn", ctypes.c_uint32), + ("crm", ctypes.c_uint32), + ("op0", ctypes.c_uint32), + ("op1", ctypes.c_uint32), + ("op2", ctypes.c_uint32), + ("val", ctypes.c_uint64) + ] + +class uc_x86_mmr(ctypes.Structure): + """Memory-Management Register for instructions IDTR, GDTR, LDTR, TR.""" + _fields_ = [ + ("selector", ctypes.c_uint16), # not used by GDTR and IDTR + ("base", ctypes.c_uint64), # handle 32 or 64 bit CPUs + ("limit", ctypes.c_uint32), + ("flags", ctypes.c_uint32), # not used by GDTR and IDTR + ] + +class uc_x86_msr(ctypes.Structure): + _fields_ = [ + ("rid", ctypes.c_uint32), + ("value", ctypes.c_uint64), + ] + +class uc_x86_float80(ctypes.Structure): + """Float80""" + _fields_ = [ + ("mantissa", ctypes.c_uint64), + ("exponent", ctypes.c_uint16), + ] + + +class uc_x86_xmm(ctypes.Structure): + """128-bit xmm register""" + _fields_ = [ + ("low_qword", ctypes.c_uint64), + ("high_qword", ctypes.c_uint64), + ] + +class uc_x86_ymm(ctypes.Structure): + """256-bit ymm register""" + _fields_ = [ + ("first_qword", ctypes.c_uint64), + ("second_qword", ctypes.c_uint64), + ("third_qword", ctypes.c_uint64), + ("fourth_qword", ctypes.c_uint64), + ] + +class uc_arm64_neon128(ctypes.Structure): + """128-bit neon register""" + _fields_ = [ + ("low_qword", ctypes.c_uint64), + ("high_qword", ctypes.c_uint64), + ] + +# Subclassing ref to allow property assignment. +class UcRef(weakref.ref): + pass + +# This class tracks Uc instance destruction and releases handles. +class UcCleanupManager(object): + def __init__(self): + self._refs = {} + + def register(self, uc): + ref = UcRef(uc, self._finalizer) + ref._uch = uc._uch + ref._class = uc.__class__ + self._refs[id(ref)] = ref + + def _finalizer(self, ref): + # note: this method must be completely self-contained and cannot have any references + # to anything else in this module. + # + # This is because it may be called late in the Python interpreter's shutdown phase, at + # which point the module's variables may already have been deinitialized and set to None. + # + # Not respecting that can lead to errors such as: + # Exception AttributeError: + # "'NoneType' object has no attribute 'release_handle'" + # in > ignored + # + # For that reason, we do not try to access the `Uc` class directly here but instead use + # the saved `._class` reference. + del self._refs[id(ref)] + ref._class.release_handle(ref._uch) + +class Uc(object): + _cleanup = UcCleanupManager() + + def __init__(self, arch: int, mode: int): + # verify version compatibility with the core before doing anything + (major, minor, _combined) = uc_version() + # print("core version =", uc_version()) + # print("binding version =", uc.UC_API_MAJOR, uc.UC_API_MINOR) + if major != uc.UC_API_MAJOR or minor != uc.UC_API_MINOR: + self._uch = None + # our binding version is different from the core's API version + raise UcError(uc.UC_ERR_VERSION) + + self._arch, self._mode = arch, mode + self._uch = ctypes.c_void_p() + status = _uc.uc_open(arch, mode, ctypes.byref(self._uch)) + if status != uc.UC_ERR_OK: + self._uch = None + raise UcError(status) + # internal mapping table to save callback & userdata + self._callbacks = {} + self._ctype_cbs = [] + self._callback_count = 0 + self._cleanup.register(self) + self._hook_exception = None # The exception raised in a hook + + @staticmethod + def release_handle(uch: ctypes.CDLL): + if uch: + try: + status = _uc.uc_close(uch) + if status != uc.UC_ERR_OK: + raise UcError(status) + except: # _uc might be pulled from under our feet + pass + + # emulate from @begin, and stop when reaching address @until + def emu_start(self, begin: int, until: int, timeout: int=0, count: int=0) -> None: + self._hook_exception = None + status = _uc.uc_emu_start(self._uch, begin, until, timeout, count) + if status != uc.UC_ERR_OK: + raise UcError(status) + + if self._hook_exception is not None: + raise self._hook_exception + + # stop emulation + def emu_stop(self) -> None: + status = _uc.uc_emu_stop(self._uch) + if status != uc.UC_ERR_OK: + raise UcError(status) + + # return the value of a register, for @opt parameter, specify int for x86 msr, tuple for arm cp/neon regs. + def reg_read(self, reg_id: int, opt: Union[None, int, ARMCPReg, ARM64CPReg]=None) -> Union[int, X86MMRReg, X86FPReg]: + return reg_read(functools.partial(_uc.uc_reg_read, self._uch), self._arch, reg_id, opt) + + # write to a register, tuple for arm cp regs. + def reg_write(self, reg_id: int, value: Union[int, ARMCPRegValue, ARM64CPRegValue, X86MMRReg, X86FPReg]): + return reg_write(functools.partial(_uc.uc_reg_write, self._uch), self._arch, reg_id, value) + + # read from MSR - X86 only + def msr_read(self, msr_id: int): + return self.reg_read(x86_const.UC_X86_REG_MSR, msr_id) + + # write to MSR - X86 only + def msr_write(self, msr_id, value: int): + return self.reg_write(x86_const.UC_X86_REG_MSR, (msr_id, value)) + + # read data from memory + def mem_read(self, address: int, size: int): + data = ctypes.create_string_buffer(size) + status = _uc.uc_mem_read(self._uch, address, data, size) + if status != uc.UC_ERR_OK: + raise UcError(status) + return bytearray(data) + + # write to memory + def mem_write(self, address: int, data: bytes): + status = _uc.uc_mem_write(self._uch, address, data, len(data)) + if status != uc.UC_ERR_OK: + raise UcError(status) + + def _mmio_map_read_cb(self, handle, offset, size, user_data): + (cb, data) = self._callbacks[user_data] + return cb(self, offset, size, data) + + def _mmio_map_write_cb(self, handle, offset, size, value, user_data): + (cb, data) = self._callbacks[user_data] + cb(self, offset, size, value, data) + + def mmio_map(self, address: int, size: int, + read_cb: UC_MMIO_READ_TYPE, user_data_read: Any, + write_cb: UC_MMIO_WRITE_TYPE, user_data_write: Any): + internal_read_cb = ctypes.cast(UC_MMIO_READ_CB(self._mmio_map_read_cb), UC_MMIO_READ_CB) + internal_write_cb = ctypes.cast(UC_MMIO_WRITE_CB(self._mmio_map_write_cb), UC_MMIO_WRITE_CB) + + self._callback_count += 1 + self._callbacks[self._callback_count] = (read_cb, user_data_read) + read_count = self._callback_count + self._callback_count += 1 + self._callbacks[self._callback_count] = (write_cb, user_data_write) + write_count = self._callback_count + + status = _uc.uc_mmio_map(self._uch, address, size, internal_read_cb, read_count, internal_write_cb, write_count) + if status != uc.UC_ERR_OK: + raise UcError(status) + + # https://docs.python.org/3/library/ctypes.html#callback-functions + self._ctype_cbs.append(internal_read_cb) + self._ctype_cbs.append(internal_write_cb) + + # map a range of memory + def mem_map(self, address: int, size: int, perms: int=uc.UC_PROT_ALL): + status = _uc.uc_mem_map(self._uch, address, size, perms) + if status != uc.UC_ERR_OK: + raise UcError(status) + + # map a range of memory from a raw host memory address + def mem_map_ptr(self, address: int, size: int, perms: int, ptr: int): + status = _uc.uc_mem_map_ptr(self._uch, address, size, perms, ptr) + if status != uc.UC_ERR_OK: + raise UcError(status) + + # unmap a range of memory + def mem_unmap(self, address: int, size: int): + status = _uc.uc_mem_unmap(self._uch, address, size) + if status != uc.UC_ERR_OK: + raise UcError(status) + + # protect a range of memory + def mem_protect(self, address: int, size: int, perms: int=uc.UC_PROT_ALL): + status = _uc.uc_mem_protect(self._uch, address, size, perms) + if status != uc.UC_ERR_OK: + raise UcError(status) + + # return CPU mode at runtime + def query(self, query_mode: int): + result = ctypes.c_size_t(0) + status = _uc.uc_query(self._uch, query_mode, ctypes.byref(result)) + if status != uc.UC_ERR_OK: + raise UcError(status) + return result.value + + @_catch_hook_exception + def _hook_tcg_op_cb(self, handle, address, arg1, arg2, user_data): + (cb, data) = self._callbacks[user_data] + cb(self, address, arg1, arg2, user_data) + + @_catch_hook_exception + def _hook_edge_gen_cb(self, handle, cur, prev, user_data): + (cb, data) = self._callbacks[user_data] + cb(self, cur.contents, prev.contents, user_data) + + @_catch_hook_exception + def _hookcode_cb(self, handle, address, size, user_data): + # call user's callback with self object + (cb, data) = self._callbacks[user_data] + cb(self, address, size, data) + + @_catch_hook_exception + def _hook_mem_invalid_cb(self, handle, access, address, size, value, user_data): + # call user's callback with self object + (cb, data) = self._callbacks[user_data] + return cb(self, access, address, size, value, data) + + @_catch_hook_exception + def _hook_mem_access_cb(self, handle, access, address, size, value, user_data): + # call user's callback with self object + (cb, data) = self._callbacks[user_data] + cb(self, access, address, size, value, data) + + @_catch_hook_exception + def _hook_intr_cb(self, handle, intno, user_data): + # call user's callback with self object + (cb, data) = self._callbacks[user_data] + cb(self, intno, data) + + @_catch_hook_exception + def _hook_insn_invalid_cb(self, handle, user_data): + # call user's callback with self object + (cb, data) = self._callbacks[user_data] + return cb(self, data) + + @_catch_hook_exception + def _hook_insn_in_cb(self, handle, port, size, user_data): + # call user's callback with self object + (cb, data) = self._callbacks[user_data] + return cb(self, port, size, data) + + @_catch_hook_exception + def _hook_insn_sys_cb(self, handle, reg, pcp_reg, user_data): + cp_reg = ctypes.cast(pcp_reg, ctypes.POINTER(uc_arm64_cp_reg)).contents + + uc_arm64_cp_reg_tuple = namedtuple("uc_arm64_cp_reg_tuple", ["crn", "crm", "op0", "op1", "op2", "val"]) + + (cb, data) = self._callbacks[user_data] + + return cb(self, reg, uc_arm64_cp_reg_tuple(cp_reg.crn, cp_reg.crm, cp_reg.op0, cp_reg.op1, cp_reg.op2, cp_reg.val), data) + + @_catch_hook_exception + def _hook_insn_out_cb(self, handle, port, size, value, user_data): + # call user's callback with self object + (cb, data) = self._callbacks[user_data] + cb(self, port, size, value, data) + + @_catch_hook_exception + def _hook_insn_syscall_cb(self, handle, user_data): + # call user's callback with self object + (cb, data) = self._callbacks[user_data] + cb(self, data) + + @_catch_hook_exception + def _hook_insn_cpuid_cb(self, handle: int, user_data: int) -> int: + # call user's callback with self object + (cb, data) = self._callbacks[user_data] + return cb(self, data) + + def ctl(self, control: int, *args): + status = _uc.uc_ctl(self._uch, control, *args) + if status != uc.UC_ERR_OK: + raise UcError(status) + return status + + def __ctl(self, ctl, nr, rw): + return ctl | (nr << 26) | (rw << 30) + + def __ctl_r(self, ctl, nr): + return self.__ctl(ctl, nr, uc.UC_CTL_IO_READ) + + def __ctl_w(self, ctl, nr): + return self.__ctl(ctl, nr, uc.UC_CTL_IO_WRITE) + + def __ctl_rw(self, ctl, nr): + return self.__ctl(ctl, nr, uc.UC_CTL_IO_READ_WRITE) + + def __ctl_r_1_arg(self, ctl, ctp): + arg = ctp() + self.ctl(self.__ctl_r(ctl, 1), ctypes.byref(arg)) + return arg.value + + def __ctl_w_1_arg(self, ctl, val, ctp): + arg = ctp(val) + self.ctl(self.__ctl_w(ctl, 1), arg) + + def __ctl_w_2_arg(self, ctl, val1, val2, ctp1, ctp2): + arg1 = ctp1(val1) + arg2 = ctp2(val2) + self.ctl(self.__ctl_w(ctl, 2), arg1, arg2) + + def __ctl_rw_1_1_arg(self, ctl, val, ctp1, ctp2): + arg1 = ctp1(val) + arg2 = ctp2() + self.ctl(self.__ctl_rw(ctl, 2), arg1, ctypes.byref(arg2)) + return arg2 + + def ctl_get_mode(self): + return self.__ctl_r_1_arg(uc.UC_CTL_UC_MODE, ctypes.c_int) + + def ctl_get_page_size(self): + return self.__ctl_r_1_arg(uc.UC_CTL_UC_PAGE_SIZE, ctypes.c_uint32) + + def ctl_set_page_size(self, val: int): + self.__ctl_w_1_arg(uc.UC_CTL_UC_PAGE_SIZE, val, ctypes.c_uint32) + + def ctl_get_arch(self): + return self.__ctl_r_1_arg(uc.UC_CTL_UC_ARCH, ctypes.c_int) + + def ctl_get_timeout(self): + return self.__ctl_r_1_arg(uc.UC_CTL_UC_TIMEOUT, ctypes.c_uint64) + + def ctl_exits_enabled(self, val: bool): + self.__ctl_w_1_arg(uc.UC_CTL_UC_USE_EXITS, val, ctypes.c_int) + + def ctl_get_exits_cnt(self): + return self.__ctl_r_1_arg(uc.UC_CTL_UC_EXITS_CNT, ctypes.c_size_t) + + def ctl_get_exits(self): + l = self.ctl_get_exits_cnt() + arr = (ctypes.c_uint64 * l)() + self.ctl(self.__ctl_r(uc.UC_CTL_UC_EXITS, 2), ctypes.cast(arr, ctypes.c_void_p), ctypes.c_size_t(l)) + return [i for i in arr] + + def ctl_set_exits(self, exits: List[int]): + arr = (ctypes.c_uint64 * len(exits))() + for idx, exit in enumerate(exits): + arr[idx] = exit + self.ctl(self.__ctl_w(uc.UC_CTL_UC_EXITS, 2), ctypes.cast(arr, ctypes.c_void_p), ctypes.c_size_t(len(exits))) + + def ctl_get_cpu_model(self): + return self.__ctl_r_1_arg(uc.UC_CTL_CPU_MODEL, ctypes.c_int) + + def ctl_set_cpu_model(self, val: int): + self.__ctl_w_1_arg(uc.UC_CTL_CPU_MODEL, val, ctypes.c_int) + + def ctl_remove_cache(self, addr: int, end: int): + self.__ctl_w_2_arg(uc.UC_CTL_TB_REMOVE_CACHE, addr, end, ctypes.c_uint64, ctypes.c_uint64) + + def ctl_request_cache(self, addr: int): + return self.__ctl_rw_1_1_arg(uc.UC_CTL_TB_REQUEST_CACHE, addr, ctypes.c_uint64, uc_tb) + + def ctl_flush_tb(self): + self.ctl(self.__ctl_w(uc.UC_CTL_TB_FLUSH, 0)) + + # add a hook + def hook_add(self, htype: int, callback: UC_HOOK_CALLBACK_TYPE , user_data: Any=None, begin: int=1, end: int=0, arg1: int=0, arg2: int=0): + _h2 = uc_hook_h() + + # save callback & user_data + self._callback_count += 1 + self._callbacks[self._callback_count] = (callback, user_data) + cb = None + + if htype == uc.UC_HOOK_INSN: + insn = ctypes.c_int(arg1) + if arg1 == x86_const.UC_X86_INS_IN: # IN instruction + cb = ctypes.cast(UC_HOOK_INSN_IN_CB(self._hook_insn_in_cb), UC_HOOK_INSN_IN_CB) + if arg1 == x86_const.UC_X86_INS_OUT: # OUT instruction + cb = ctypes.cast(UC_HOOK_INSN_OUT_CB(self._hook_insn_out_cb), UC_HOOK_INSN_OUT_CB) + if arg1 in (x86_const.UC_X86_INS_SYSCALL, x86_const.UC_X86_INS_SYSENTER): # SYSCALL/SYSENTER instruction + cb = ctypes.cast(UC_HOOK_INSN_SYSCALL_CB(self._hook_insn_syscall_cb), UC_HOOK_INSN_SYSCALL_CB) + if arg1 == x86_const.UC_X86_INS_CPUID: # CPUID instruction + cb = ctypes.cast(UC_HOOK_INSN_CPUID_CB(self._hook_insn_cpuid_cb), UC_HOOK_INSN_CPUID_CB) + if arg1 in (arm64_const.UC_ARM64_INS_MRS, arm64_const.UC_ARM64_INS_MSR, arm64_const.UC_ARM64_INS_SYS, arm64_const.UC_ARM64_INS_SYSL): + cb = ctypes.cast(UC_HOOK_INSN_SYS_CB(self._hook_insn_sys_cb), UC_HOOK_INSN_SYS_CB) + status = _uc.uc_hook_add( + self._uch, ctypes.byref(_h2), htype, cb, + ctypes.cast(self._callback_count, ctypes.c_void_p), + ctypes.c_uint64(begin), ctypes.c_uint64(end), insn + ) + elif htype == uc.UC_HOOK_TCG_OPCODE: + opcode = ctypes.c_int(arg1) + flags = ctypes.c_int(arg2) + + status = _uc.uc_hook_add( + self._uch, ctypes.byref(_h2), htype, ctypes.cast(UC_HOOK_TCG_OPCODE_CB(self._hook_tcg_op_cb), UC_HOOK_TCG_OPCODE_CB), + ctypes.cast(self._callback_count, ctypes.c_void_p), + ctypes.c_uint64(begin), ctypes.c_uint64(end), opcode, flags + ) + elif htype == uc.UC_HOOK_INTR: + cb = ctypes.cast(UC_HOOK_INTR_CB(self._hook_intr_cb), UC_HOOK_INTR_CB) + status = _uc.uc_hook_add( + self._uch, ctypes.byref(_h2), htype, cb, + ctypes.cast(self._callback_count, ctypes.c_void_p), + ctypes.c_uint64(begin), ctypes.c_uint64(end) + ) + elif htype == uc.UC_HOOK_INSN_INVALID: + cb = ctypes.cast(UC_HOOK_INSN_INVALID_CB(self._hook_insn_invalid_cb), UC_HOOK_INSN_INVALID_CB) + status = _uc.uc_hook_add( + self._uch, ctypes.byref(_h2), htype, cb, + ctypes.cast(self._callback_count, ctypes.c_void_p), + ctypes.c_uint64(begin), ctypes.c_uint64(end) + ) + elif htype == uc.UC_HOOK_EDGE_GENERATED: + cb = ctypes.cast(UC_HOOK_EDGE_GEN_CB(self._hook_edge_gen_cb), UC_HOOK_EDGE_GEN_CB) + status = _uc.uc_hook_add( + self._uch, ctypes.byref(_h2), htype, cb, + ctypes.cast(self._callback_count, ctypes.c_void_p), + ctypes.c_uint64(begin), ctypes.c_uint64(end) + ) + else: + if htype in (uc.UC_HOOK_BLOCK, uc.UC_HOOK_CODE): + # set callback with wrapper, so it can be called + # with this object as param + cb = ctypes.cast(UC_HOOK_CODE_CB(self._hookcode_cb), UC_HOOK_CODE_CB) + status = _uc.uc_hook_add( + self._uch, ctypes.byref(_h2), htype, cb, + ctypes.cast(self._callback_count, ctypes.c_void_p), + ctypes.c_uint64(begin), ctypes.c_uint64(end) + ) + elif htype & (uc.UC_HOOK_MEM_READ_UNMAPPED | + uc.UC_HOOK_MEM_WRITE_UNMAPPED | + uc.UC_HOOK_MEM_FETCH_UNMAPPED | + uc.UC_HOOK_MEM_READ_PROT | + uc.UC_HOOK_MEM_WRITE_PROT | + uc.UC_HOOK_MEM_FETCH_PROT): + cb = ctypes.cast(UC_HOOK_MEM_INVALID_CB(self._hook_mem_invalid_cb), UC_HOOK_MEM_INVALID_CB) + status = _uc.uc_hook_add( + self._uch, ctypes.byref(_h2), htype, cb, + ctypes.cast(self._callback_count, ctypes.c_void_p), + ctypes.c_uint64(begin), ctypes.c_uint64(end) + ) + else: + cb = ctypes.cast(UC_HOOK_MEM_ACCESS_CB(self._hook_mem_access_cb), UC_HOOK_MEM_ACCESS_CB) + status = _uc.uc_hook_add( + self._uch, ctypes.byref(_h2), htype, cb, + ctypes.cast(self._callback_count, ctypes.c_void_p), + ctypes.c_uint64(begin), ctypes.c_uint64(end) + ) + + # save the ctype function so gc will leave it alone. + self._ctype_cbs.append(cb) + + if status != uc.UC_ERR_OK: + raise UcError(status) + + return _h2.value + + # delete a hook + def hook_del(self, h: int): + _h = uc_hook_h(h) + status = _uc.uc_hook_del(self._uch, _h) + if status != uc.UC_ERR_OK: + raise UcError(status) + h = 0 + + def context_save(self): + context = UcContext(self._uch, self._arch, self._mode) + status = _uc.uc_context_save(self._uch, context.context) + if status != uc.UC_ERR_OK: + raise UcError(status) + + return context + + def context_update(self, context: UcContext): + status = _uc.uc_context_save(self._uch, context.context) + if status != uc.UC_ERR_OK: + raise UcError(status) + + def context_restore(self, context: UcContext): + status = _uc.uc_context_restore(self._uch, context.context) + if status != uc.UC_ERR_OK: + raise UcError(status) + + # this returns a generator of regions in the form (begin, end, perms) + def mem_regions(self): + regions = ctypes.POINTER(_uc_mem_region)() + count = ctypes.c_uint32() + status = _uc.uc_mem_regions(self._uch, ctypes.byref(regions), ctypes.byref(count)) + if status != uc.UC_ERR_OK: + raise UcError(status) + + try: + for i in range(count.value): + yield (regions[i].begin, regions[i].end, regions[i].perms) + finally: + _uc.uc_free(regions) + + +class UcContext: + def __init__(self, h, arch, mode): + self._context = uc_context() + self._size = _uc.uc_context_size(h) + self._to_free = True + status = _uc.uc_context_alloc(h, ctypes.byref(self._context)) + if status != uc.UC_ERR_OK: + raise UcError(status) + self._arch = arch + self._mode = mode + + @property + def context(self): + return self._context + + @property + def size(self): + return self._size + + @property + def arch(self): + return self._arch + + @property + def mode(self): + return self._mode + + # return the value of a register + def reg_read(self, reg_id, opt=None): + return reg_read(functools.partial(_uc.uc_context_reg_read, self._context), self.arch, reg_id, opt) + + # write to a register + def reg_write(self, reg_id, value): + return reg_write(functools.partial(_uc.uc_context_reg_write, self._context), self.arch, reg_id, value) + + # Make UcContext picklable + def __getstate__(self): + return (bytes(self), self.size, self.arch, self.mode) + + def __setstate__(self, state): + self._size = state[1] + self._context = ctypes.cast(ctypes.create_string_buffer(state[0], self._size), uc_context) + # __init__ won'e be invoked, so we are safe to set it here. + self._to_free = False + self._arch = state[2] + self._mode = state[3] + + def __bytes__(self): + return ctypes.string_at(self.context, self.size) + + def __del__(self): + # We need this property since we shouldn't free it if the object is constructed from pickled bytes. + if self._to_free: + _uc.uc_context_free(self._context) + +UC_HOOK_CODE_TYPE = Callable[[Uc, int, int, Any], None] +UC_HOOK_INSN_INVALID_TYPE = Callable[[Uc, Any], bool] +UC_HOOK_MEM_INVALID_TYPE = Callable[[Uc, int, int, int, int, Any], bool] +UC_HOOK_MEM_ACCESS_TYPE = Callable[[Uc, int, int, int, int, Any], None] +UC_HOOK_INTR_TYPE = Callable[[Uc, int, Any], None] +UC_HOOK_INSN_IN_TYPE = Callable[[Uc, int, int, Any], int] +UC_HOOK_INSN_OUT_TYPE = Callable[[Uc, int, int, int, Any], None] +UC_HOOK_INSN_SYSCALL_TYPE = Callable[[Uc, Any], None] +UC_HOOK_INSN_SYS_TYPE = Callable[[Uc, int, Tuple[int, int, int, int, int, int], Any], int] +UC_HOOK_INSN_CPUID_TYPE = Callable[[Uc, Any], int] +UC_MMIO_READ_TYPE = Callable[[Uc, int, int, Any], int] +UC_MMIO_WRITE_TYPE = Callable[[Uc, int, int, int, Any], None] +UC_HOOK_EDGE_GEN_TYPE = Callable[[Uc, uc_tb, uc_tb, Any], None] +UC_HOOK_TCG_OPCODE_TYPE = Callable[[Uc, int, int, int, Any], None] + +UC_HOOK_CALLBACK_TYPE = Union[ + UC_HOOK_CODE_TYPE, + UC_HOOK_INSN_INVALID_TYPE, + UC_HOOK_MEM_INVALID_TYPE, + UC_HOOK_MEM_ACCESS_TYPE, + UC_HOOK_INSN_IN_TYPE, + UC_HOOK_INSN_OUT_TYPE, + UC_HOOK_INSN_SYSCALL_TYPE, + UC_HOOK_INSN_SYS_TYPE, + UC_HOOK_INSN_CPUID_TYPE, + UC_HOOK_EDGE_GEN_TYPE, + UC_HOOK_TCG_OPCODE_TYPE +] + +# print out debugging info +def debug(): + archs = { + "arm": uc.UC_ARCH_ARM, + "arm64": uc.UC_ARCH_ARM64, + "mips": uc.UC_ARCH_MIPS, + "sparc": uc.UC_ARCH_SPARC, + "m68k": uc.UC_ARCH_M68K, + "x86": uc.UC_ARCH_X86, + "riscv": uc.UC_ARCH_RISCV, + "ppc": uc.UC_ARCH_PPC, + } + + all_archs = "" + keys = archs.keys() + for k in sorted(keys): + if uc_arch_supported(archs[k]): + all_archs += "-%s" % k + + major, minor, _combined = uc_version() + + return "python-%s-c%u.%u-b%u.%u" % ( + all_archs, major, minor, uc.UC_API_MAJOR, uc.UC_API_MINOR + ) diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/unicorn_const.py b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/unicorn_const.py new file mode 100644 index 0000000000..3c86bd6bda --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/unicorn_const.py @@ -0,0 +1,142 @@ +# For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT [unicorn_const.py] +UC_API_MAJOR = 2 + +UC_API_MINOR = 0 +UC_API_PATCH = 1 +UC_API_EXTRA = 255 +UC_VERSION_MAJOR = 2 + +UC_VERSION_MINOR = 0 +UC_VERSION_PATCH = 1 +UC_VERSION_EXTRA = 255 +UC_SECOND_SCALE = 1000000 +UC_MILISECOND_SCALE = 1000 +UC_ARCH_ARM = 1 +UC_ARCH_ARM64 = 2 +UC_ARCH_MIPS = 3 +UC_ARCH_X86 = 4 +UC_ARCH_PPC = 5 +UC_ARCH_SPARC = 6 +UC_ARCH_M68K = 7 +UC_ARCH_RISCV = 8 +UC_ARCH_S390X = 9 +UC_ARCH_TRICORE = 10 +UC_ARCH_RH850 = 11 +UC_ARCH_MAX = 12 + +UC_MODE_LITTLE_ENDIAN = 0 +UC_MODE_BIG_ENDIAN = 1073741824 + +UC_MODE_ARM = 0 +UC_MODE_THUMB = 16 +UC_MODE_MCLASS = 32 +UC_MODE_V8 = 64 +UC_MODE_ARMBE8 = 1024 +UC_MODE_ARM926 = 128 +UC_MODE_ARM946 = 256 +UC_MODE_ARM1176 = 512 +UC_MODE_MICRO = 16 +UC_MODE_MIPS3 = 32 +UC_MODE_MIPS32R6 = 64 +UC_MODE_MIPS32 = 4 +UC_MODE_MIPS64 = 8 +UC_MODE_16 = 2 +UC_MODE_32 = 4 +UC_MODE_64 = 8 +UC_MODE_PPC32 = 4 +UC_MODE_PPC64 = 8 +UC_MODE_QPX = 16 +UC_MODE_SPARC32 = 4 +UC_MODE_SPARC64 = 8 +UC_MODE_V9 = 16 +UC_MODE_RH850 = 4 +UC_MODE_RISCV32 = 4 +UC_MODE_RISCV64 = 8 + +UC_ERR_OK = 0 +UC_ERR_NOMEM = 1 +UC_ERR_ARCH = 2 +UC_ERR_HANDLE = 3 +UC_ERR_MODE = 4 +UC_ERR_VERSION = 5 +UC_ERR_READ_UNMAPPED = 6 +UC_ERR_WRITE_UNMAPPED = 7 +UC_ERR_FETCH_UNMAPPED = 8 +UC_ERR_HOOK = 9 +UC_ERR_INSN_INVALID = 10 +UC_ERR_MAP = 11 +UC_ERR_WRITE_PROT = 12 +UC_ERR_READ_PROT = 13 +UC_ERR_FETCH_PROT = 14 +UC_ERR_ARG = 15 +UC_ERR_READ_UNALIGNED = 16 +UC_ERR_WRITE_UNALIGNED = 17 +UC_ERR_FETCH_UNALIGNED = 18 +UC_ERR_HOOK_EXIST = 19 +UC_ERR_RESOURCE = 20 +UC_ERR_EXCEPTION = 21 +UC_MEM_READ = 16 +UC_MEM_WRITE = 17 +UC_MEM_FETCH = 18 +UC_MEM_READ_UNMAPPED = 19 +UC_MEM_WRITE_UNMAPPED = 20 +UC_MEM_FETCH_UNMAPPED = 21 +UC_MEM_WRITE_PROT = 22 +UC_MEM_READ_PROT = 23 +UC_MEM_FETCH_PROT = 24 +UC_MEM_READ_AFTER = 25 + +UC_TCG_OP_SUB = 0 +UC_TCG_OP_FLAG_CMP = 1 +UC_TCG_OP_FLAG_DIRECT = 2 +UC_HOOK_INTR = 1 +UC_HOOK_INSN = 2 +UC_HOOK_CODE = 4 +UC_HOOK_BLOCK = 8 +UC_HOOK_MEM_READ_UNMAPPED = 16 +UC_HOOK_MEM_WRITE_UNMAPPED = 32 +UC_HOOK_MEM_FETCH_UNMAPPED = 64 +UC_HOOK_MEM_READ_PROT = 128 +UC_HOOK_MEM_WRITE_PROT = 256 +UC_HOOK_MEM_FETCH_PROT = 512 +UC_HOOK_MEM_READ = 1024 +UC_HOOK_MEM_WRITE = 2048 +UC_HOOK_MEM_FETCH = 4096 +UC_HOOK_MEM_READ_AFTER = 8192 +UC_HOOK_INSN_INVALID = 16384 +UC_HOOK_EDGE_GENERATED = 32768 +UC_HOOK_TCG_OPCODE = 65536 +UC_HOOK_MEM_UNMAPPED = 112 +UC_HOOK_MEM_PROT = 896 +UC_HOOK_MEM_READ_INVALID = 144 +UC_HOOK_MEM_WRITE_INVALID = 288 +UC_HOOK_MEM_FETCH_INVALID = 576 +UC_HOOK_MEM_INVALID = 1008 +UC_HOOK_MEM_VALID = 7168 +UC_QUERY_MODE = 1 +UC_QUERY_PAGE_SIZE = 2 +UC_QUERY_ARCH = 3 +UC_QUERY_TIMEOUT = 4 + +UC_CTL_IO_NONE = 0 +UC_CTL_IO_WRITE = 1 +UC_CTL_IO_READ = 2 +UC_CTL_IO_READ_WRITE = 3 + +UC_CTL_UC_MODE = 0 +UC_CTL_UC_PAGE_SIZE = 1 +UC_CTL_UC_ARCH = 2 +UC_CTL_UC_TIMEOUT = 3 +UC_CTL_UC_USE_EXITS = 4 +UC_CTL_UC_EXITS_CNT = 5 +UC_CTL_UC_EXITS = 6 +UC_CTL_CPU_MODEL = 7 +UC_CTL_TB_REQUEST_CACHE = 8 +UC_CTL_TB_REMOVE_CACHE = 9 +UC_CTL_TB_FLUSH = 10 + +UC_PROT_NONE = 0 +UC_PROT_READ = 1 +UC_PROT_WRITE = 2 +UC_PROT_EXEC = 4 +UC_PROT_ALL = 7 diff --git a/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/x86_const.py b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/x86_const.py new file mode 100644 index 0000000000..fc96308e37 --- /dev/null +++ b/bindings/python/bindings-test/lib/python3.9/site-packages/unicorn-2.0.1.post1-py3.9.egg/unicorn/x86_const.py @@ -0,0 +1,1628 @@ +# For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT [x86_const.py] + +# X86 CPU + +UC_CPU_X86_QEMU64 = 0 +UC_CPU_X86_PHENOM = 1 +UC_CPU_X86_CORE2DUO = 2 +UC_CPU_X86_KVM64 = 3 +UC_CPU_X86_QEMU32 = 4 +UC_CPU_X86_KVM32 = 5 +UC_CPU_X86_COREDUO = 6 +UC_CPU_X86_486 = 7 +UC_CPU_X86_PENTIUM = 8 +UC_CPU_X86_PENTIUM2 = 9 +UC_CPU_X86_PENTIUM3 = 10 +UC_CPU_X86_ATHLON = 11 +UC_CPU_X86_N270 = 12 +UC_CPU_X86_CONROE = 13 +UC_CPU_X86_PENRYN = 14 +UC_CPU_X86_NEHALEM = 15 +UC_CPU_X86_WESTMERE = 16 +UC_CPU_X86_SANDYBRIDGE = 17 +UC_CPU_X86_IVYBRIDGE = 18 +UC_CPU_X86_HASWELL = 19 +UC_CPU_X86_BROADWELL = 20 +UC_CPU_X86_SKYLAKE_CLIENT = 21 +UC_CPU_X86_SKYLAKE_SERVER = 22 +UC_CPU_X86_CASCADELAKE_SERVER = 23 +UC_CPU_X86_COOPERLAKE = 24 +UC_CPU_X86_ICELAKE_CLIENT = 25 +UC_CPU_X86_ICELAKE_SERVER = 26 +UC_CPU_X86_DENVERTON = 27 +UC_CPU_X86_SNOWRIDGE = 28 +UC_CPU_X86_KNIGHTSMILL = 29 +UC_CPU_X86_OPTERON_G1 = 30 +UC_CPU_X86_OPTERON_G2 = 31 +UC_CPU_X86_OPTERON_G3 = 32 +UC_CPU_X86_OPTERON_G4 = 33 +UC_CPU_X86_OPTERON_G5 = 34 +UC_CPU_X86_EPYC = 35 +UC_CPU_X86_DHYANA = 36 +UC_CPU_X86_EPYC_ROME = 37 +UC_CPU_X86_ENDING = 38 + +# X86 registers + +UC_X86_REG_INVALID = 0 +UC_X86_REG_AH = 1 +UC_X86_REG_AL = 2 +UC_X86_REG_AX = 3 +UC_X86_REG_BH = 4 +UC_X86_REG_BL = 5 +UC_X86_REG_BP = 6 +UC_X86_REG_BPL = 7 +UC_X86_REG_BX = 8 +UC_X86_REG_CH = 9 +UC_X86_REG_CL = 10 +UC_X86_REG_CS = 11 +UC_X86_REG_CX = 12 +UC_X86_REG_DH = 13 +UC_X86_REG_DI = 14 +UC_X86_REG_DIL = 15 +UC_X86_REG_DL = 16 +UC_X86_REG_DS = 17 +UC_X86_REG_DX = 18 +UC_X86_REG_EAX = 19 +UC_X86_REG_EBP = 20 +UC_X86_REG_EBX = 21 +UC_X86_REG_ECX = 22 +UC_X86_REG_EDI = 23 +UC_X86_REG_EDX = 24 +UC_X86_REG_EFLAGS = 25 +UC_X86_REG_EIP = 26 +UC_X86_REG_ES = 28 +UC_X86_REG_ESI = 29 +UC_X86_REG_ESP = 30 +UC_X86_REG_FPSW = 31 +UC_X86_REG_FS = 32 +UC_X86_REG_GS = 33 +UC_X86_REG_IP = 34 +UC_X86_REG_RAX = 35 +UC_X86_REG_RBP = 36 +UC_X86_REG_RBX = 37 +UC_X86_REG_RCX = 38 +UC_X86_REG_RDI = 39 +UC_X86_REG_RDX = 40 +UC_X86_REG_RIP = 41 +UC_X86_REG_RSI = 43 +UC_X86_REG_RSP = 44 +UC_X86_REG_SI = 45 +UC_X86_REG_SIL = 46 +UC_X86_REG_SP = 47 +UC_X86_REG_SPL = 48 +UC_X86_REG_SS = 49 +UC_X86_REG_CR0 = 50 +UC_X86_REG_CR1 = 51 +UC_X86_REG_CR2 = 52 +UC_X86_REG_CR3 = 53 +UC_X86_REG_CR4 = 54 +UC_X86_REG_CR8 = 58 +UC_X86_REG_DR0 = 66 +UC_X86_REG_DR1 = 67 +UC_X86_REG_DR2 = 68 +UC_X86_REG_DR3 = 69 +UC_X86_REG_DR4 = 70 +UC_X86_REG_DR5 = 71 +UC_X86_REG_DR6 = 72 +UC_X86_REG_DR7 = 73 +UC_X86_REG_FP0 = 82 +UC_X86_REG_FP1 = 83 +UC_X86_REG_FP2 = 84 +UC_X86_REG_FP3 = 85 +UC_X86_REG_FP4 = 86 +UC_X86_REG_FP5 = 87 +UC_X86_REG_FP6 = 88 +UC_X86_REG_FP7 = 89 +UC_X86_REG_K0 = 90 +UC_X86_REG_K1 = 91 +UC_X86_REG_K2 = 92 +UC_X86_REG_K3 = 93 +UC_X86_REG_K4 = 94 +UC_X86_REG_K5 = 95 +UC_X86_REG_K6 = 96 +UC_X86_REG_K7 = 97 +UC_X86_REG_MM0 = 98 +UC_X86_REG_MM1 = 99 +UC_X86_REG_MM2 = 100 +UC_X86_REG_MM3 = 101 +UC_X86_REG_MM4 = 102 +UC_X86_REG_MM5 = 103 +UC_X86_REG_MM6 = 104 +UC_X86_REG_MM7 = 105 +UC_X86_REG_R8 = 106 +UC_X86_REG_R9 = 107 +UC_X86_REG_R10 = 108 +UC_X86_REG_R11 = 109 +UC_X86_REG_R12 = 110 +UC_X86_REG_R13 = 111 +UC_X86_REG_R14 = 112 +UC_X86_REG_R15 = 113 +UC_X86_REG_ST0 = 114 +UC_X86_REG_ST1 = 115 +UC_X86_REG_ST2 = 116 +UC_X86_REG_ST3 = 117 +UC_X86_REG_ST4 = 118 +UC_X86_REG_ST5 = 119 +UC_X86_REG_ST6 = 120 +UC_X86_REG_ST7 = 121 +UC_X86_REG_XMM0 = 122 +UC_X86_REG_XMM1 = 123 +UC_X86_REG_XMM2 = 124 +UC_X86_REG_XMM3 = 125 +UC_X86_REG_XMM4 = 126 +UC_X86_REG_XMM5 = 127 +UC_X86_REG_XMM6 = 128 +UC_X86_REG_XMM7 = 129 +UC_X86_REG_XMM8 = 130 +UC_X86_REG_XMM9 = 131 +UC_X86_REG_XMM10 = 132 +UC_X86_REG_XMM11 = 133 +UC_X86_REG_XMM12 = 134 +UC_X86_REG_XMM13 = 135 +UC_X86_REG_XMM14 = 136 +UC_X86_REG_XMM15 = 137 +UC_X86_REG_XMM16 = 138 +UC_X86_REG_XMM17 = 139 +UC_X86_REG_XMM18 = 140 +UC_X86_REG_XMM19 = 141 +UC_X86_REG_XMM20 = 142 +UC_X86_REG_XMM21 = 143 +UC_X86_REG_XMM22 = 144 +UC_X86_REG_XMM23 = 145 +UC_X86_REG_XMM24 = 146 +UC_X86_REG_XMM25 = 147 +UC_X86_REG_XMM26 = 148 +UC_X86_REG_XMM27 = 149 +UC_X86_REG_XMM28 = 150 +UC_X86_REG_XMM29 = 151 +UC_X86_REG_XMM30 = 152 +UC_X86_REG_XMM31 = 153 +UC_X86_REG_YMM0 = 154 +UC_X86_REG_YMM1 = 155 +UC_X86_REG_YMM2 = 156 +UC_X86_REG_YMM3 = 157 +UC_X86_REG_YMM4 = 158 +UC_X86_REG_YMM5 = 159 +UC_X86_REG_YMM6 = 160 +UC_X86_REG_YMM7 = 161 +UC_X86_REG_YMM8 = 162 +UC_X86_REG_YMM9 = 163 +UC_X86_REG_YMM10 = 164 +UC_X86_REG_YMM11 = 165 +UC_X86_REG_YMM12 = 166 +UC_X86_REG_YMM13 = 167 +UC_X86_REG_YMM14 = 168 +UC_X86_REG_YMM15 = 169 +UC_X86_REG_YMM16 = 170 +UC_X86_REG_YMM17 = 171 +UC_X86_REG_YMM18 = 172 +UC_X86_REG_YMM19 = 173 +UC_X86_REG_YMM20 = 174 +UC_X86_REG_YMM21 = 175 +UC_X86_REG_YMM22 = 176 +UC_X86_REG_YMM23 = 177 +UC_X86_REG_YMM24 = 178 +UC_X86_REG_YMM25 = 179 +UC_X86_REG_YMM26 = 180 +UC_X86_REG_YMM27 = 181 +UC_X86_REG_YMM28 = 182 +UC_X86_REG_YMM29 = 183 +UC_X86_REG_YMM30 = 184 +UC_X86_REG_YMM31 = 185 +UC_X86_REG_ZMM0 = 186 +UC_X86_REG_ZMM1 = 187 +UC_X86_REG_ZMM2 = 188 +UC_X86_REG_ZMM3 = 189 +UC_X86_REG_ZMM4 = 190 +UC_X86_REG_ZMM5 = 191 +UC_X86_REG_ZMM6 = 192 +UC_X86_REG_ZMM7 = 193 +UC_X86_REG_ZMM8 = 194 +UC_X86_REG_ZMM9 = 195 +UC_X86_REG_ZMM10 = 196 +UC_X86_REG_ZMM11 = 197 +UC_X86_REG_ZMM12 = 198 +UC_X86_REG_ZMM13 = 199 +UC_X86_REG_ZMM14 = 200 +UC_X86_REG_ZMM15 = 201 +UC_X86_REG_ZMM16 = 202 +UC_X86_REG_ZMM17 = 203 +UC_X86_REG_ZMM18 = 204 +UC_X86_REG_ZMM19 = 205 +UC_X86_REG_ZMM20 = 206 +UC_X86_REG_ZMM21 = 207 +UC_X86_REG_ZMM22 = 208 +UC_X86_REG_ZMM23 = 209 +UC_X86_REG_ZMM24 = 210 +UC_X86_REG_ZMM25 = 211 +UC_X86_REG_ZMM26 = 212 +UC_X86_REG_ZMM27 = 213 +UC_X86_REG_ZMM28 = 214 +UC_X86_REG_ZMM29 = 215 +UC_X86_REG_ZMM30 = 216 +UC_X86_REG_ZMM31 = 217 +UC_X86_REG_R8B = 218 +UC_X86_REG_R9B = 219 +UC_X86_REG_R10B = 220 +UC_X86_REG_R11B = 221 +UC_X86_REG_R12B = 222 +UC_X86_REG_R13B = 223 +UC_X86_REG_R14B = 224 +UC_X86_REG_R15B = 225 +UC_X86_REG_R8D = 226 +UC_X86_REG_R9D = 227 +UC_X86_REG_R10D = 228 +UC_X86_REG_R11D = 229 +UC_X86_REG_R12D = 230 +UC_X86_REG_R13D = 231 +UC_X86_REG_R14D = 232 +UC_X86_REG_R15D = 233 +UC_X86_REG_R8W = 234 +UC_X86_REG_R9W = 235 +UC_X86_REG_R10W = 236 +UC_X86_REG_R11W = 237 +UC_X86_REG_R12W = 238 +UC_X86_REG_R13W = 239 +UC_X86_REG_R14W = 240 +UC_X86_REG_R15W = 241 +UC_X86_REG_IDTR = 242 +UC_X86_REG_GDTR = 243 +UC_X86_REG_LDTR = 244 +UC_X86_REG_TR = 245 +UC_X86_REG_FPCW = 246 +UC_X86_REG_FPTAG = 247 +UC_X86_REG_MSR = 248 +UC_X86_REG_MXCSR = 249 +UC_X86_REG_FS_BASE = 250 +UC_X86_REG_GS_BASE = 251 +UC_X86_REG_FLAGS = 252 +UC_X86_REG_RFLAGS = 253 +UC_X86_REG_FIP = 254 +UC_X86_REG_FCS = 255 +UC_X86_REG_FDP = 256 +UC_X86_REG_FDS = 257 +UC_X86_REG_FOP = 258 +UC_X86_REG_ENDING = 259 + +# X86 instructions + +UC_X86_INS_INVALID = 0 +UC_X86_INS_AAA = 1 +UC_X86_INS_AAD = 2 +UC_X86_INS_AAM = 3 +UC_X86_INS_AAS = 4 +UC_X86_INS_FABS = 5 +UC_X86_INS_ADC = 6 +UC_X86_INS_ADCX = 7 +UC_X86_INS_ADD = 8 +UC_X86_INS_ADDPD = 9 +UC_X86_INS_ADDPS = 10 +UC_X86_INS_ADDSD = 11 +UC_X86_INS_ADDSS = 12 +UC_X86_INS_ADDSUBPD = 13 +UC_X86_INS_ADDSUBPS = 14 +UC_X86_INS_FADD = 15 +UC_X86_INS_FIADD = 16 +UC_X86_INS_FADDP = 17 +UC_X86_INS_ADOX = 18 +UC_X86_INS_AESDECLAST = 19 +UC_X86_INS_AESDEC = 20 +UC_X86_INS_AESENCLAST = 21 +UC_X86_INS_AESENC = 22 +UC_X86_INS_AESIMC = 23 +UC_X86_INS_AESKEYGENASSIST = 24 +UC_X86_INS_AND = 25 +UC_X86_INS_ANDN = 26 +UC_X86_INS_ANDNPD = 27 +UC_X86_INS_ANDNPS = 28 +UC_X86_INS_ANDPD = 29 +UC_X86_INS_ANDPS = 30 +UC_X86_INS_ARPL = 31 +UC_X86_INS_BEXTR = 32 +UC_X86_INS_BLCFILL = 33 +UC_X86_INS_BLCI = 34 +UC_X86_INS_BLCIC = 35 +UC_X86_INS_BLCMSK = 36 +UC_X86_INS_BLCS = 37 +UC_X86_INS_BLENDPD = 38 +UC_X86_INS_BLENDPS = 39 +UC_X86_INS_BLENDVPD = 40 +UC_X86_INS_BLENDVPS = 41 +UC_X86_INS_BLSFILL = 42 +UC_X86_INS_BLSI = 43 +UC_X86_INS_BLSIC = 44 +UC_X86_INS_BLSMSK = 45 +UC_X86_INS_BLSR = 46 +UC_X86_INS_BOUND = 47 +UC_X86_INS_BSF = 48 +UC_X86_INS_BSR = 49 +UC_X86_INS_BSWAP = 50 +UC_X86_INS_BT = 51 +UC_X86_INS_BTC = 52 +UC_X86_INS_BTR = 53 +UC_X86_INS_BTS = 54 +UC_X86_INS_BZHI = 55 +UC_X86_INS_CALL = 56 +UC_X86_INS_CBW = 57 +UC_X86_INS_CDQ = 58 +UC_X86_INS_CDQE = 59 +UC_X86_INS_FCHS = 60 +UC_X86_INS_CLAC = 61 +UC_X86_INS_CLC = 62 +UC_X86_INS_CLD = 63 +UC_X86_INS_CLFLUSH = 64 +UC_X86_INS_CLFLUSHOPT = 65 +UC_X86_INS_CLGI = 66 +UC_X86_INS_CLI = 67 +UC_X86_INS_CLTS = 68 +UC_X86_INS_CLWB = 69 +UC_X86_INS_CMC = 70 +UC_X86_INS_CMOVA = 71 +UC_X86_INS_CMOVAE = 72 +UC_X86_INS_CMOVB = 73 +UC_X86_INS_CMOVBE = 74 +UC_X86_INS_FCMOVBE = 75 +UC_X86_INS_FCMOVB = 76 +UC_X86_INS_CMOVE = 77 +UC_X86_INS_FCMOVE = 78 +UC_X86_INS_CMOVG = 79 +UC_X86_INS_CMOVGE = 80 +UC_X86_INS_CMOVL = 81 +UC_X86_INS_CMOVLE = 82 +UC_X86_INS_FCMOVNBE = 83 +UC_X86_INS_FCMOVNB = 84 +UC_X86_INS_CMOVNE = 85 +UC_X86_INS_FCMOVNE = 86 +UC_X86_INS_CMOVNO = 87 +UC_X86_INS_CMOVNP = 88 +UC_X86_INS_FCMOVNU = 89 +UC_X86_INS_CMOVNS = 90 +UC_X86_INS_CMOVO = 91 +UC_X86_INS_CMOVP = 92 +UC_X86_INS_FCMOVU = 93 +UC_X86_INS_CMOVS = 94 +UC_X86_INS_CMP = 95 +UC_X86_INS_CMPPD = 96 +UC_X86_INS_CMPPS = 97 +UC_X86_INS_CMPSB = 98 +UC_X86_INS_CMPSD = 99 +UC_X86_INS_CMPSQ = 100 +UC_X86_INS_CMPSS = 101 +UC_X86_INS_CMPSW = 102 +UC_X86_INS_CMPXCHG16B = 103 +UC_X86_INS_CMPXCHG = 104 +UC_X86_INS_CMPXCHG8B = 105 +UC_X86_INS_COMISD = 106 +UC_X86_INS_COMISS = 107 +UC_X86_INS_FCOMP = 108 +UC_X86_INS_FCOMPI = 109 +UC_X86_INS_FCOMI = 110 +UC_X86_INS_FCOM = 111 +UC_X86_INS_FCOS = 112 +UC_X86_INS_CPUID = 113 +UC_X86_INS_CQO = 114 +UC_X86_INS_CRC32 = 115 +UC_X86_INS_CVTDQ2PD = 116 +UC_X86_INS_CVTDQ2PS = 117 +UC_X86_INS_CVTPD2DQ = 118 +UC_X86_INS_CVTPD2PS = 119 +UC_X86_INS_CVTPS2DQ = 120 +UC_X86_INS_CVTPS2PD = 121 +UC_X86_INS_CVTSD2SI = 122 +UC_X86_INS_CVTSD2SS = 123 +UC_X86_INS_CVTSI2SD = 124 +UC_X86_INS_CVTSI2SS = 125 +UC_X86_INS_CVTSS2SD = 126 +UC_X86_INS_CVTSS2SI = 127 +UC_X86_INS_CVTTPD2DQ = 128 +UC_X86_INS_CVTTPS2DQ = 129 +UC_X86_INS_CVTTSD2SI = 130 +UC_X86_INS_CVTTSS2SI = 131 +UC_X86_INS_CWD = 132 +UC_X86_INS_CWDE = 133 +UC_X86_INS_DAA = 134 +UC_X86_INS_DAS = 135 +UC_X86_INS_DATA16 = 136 +UC_X86_INS_DEC = 137 +UC_X86_INS_DIV = 138 +UC_X86_INS_DIVPD = 139 +UC_X86_INS_DIVPS = 140 +UC_X86_INS_FDIVR = 141 +UC_X86_INS_FIDIVR = 142 +UC_X86_INS_FDIVRP = 143 +UC_X86_INS_DIVSD = 144 +UC_X86_INS_DIVSS = 145 +UC_X86_INS_FDIV = 146 +UC_X86_INS_FIDIV = 147 +UC_X86_INS_FDIVP = 148 +UC_X86_INS_DPPD = 149 +UC_X86_INS_DPPS = 150 +UC_X86_INS_RET = 151 +UC_X86_INS_ENCLS = 152 +UC_X86_INS_ENCLU = 153 +UC_X86_INS_ENTER = 154 +UC_X86_INS_EXTRACTPS = 155 +UC_X86_INS_EXTRQ = 156 +UC_X86_INS_F2XM1 = 157 +UC_X86_INS_LCALL = 158 +UC_X86_INS_LJMP = 159 +UC_X86_INS_FBLD = 160 +UC_X86_INS_FBSTP = 161 +UC_X86_INS_FCOMPP = 162 +UC_X86_INS_FDECSTP = 163 +UC_X86_INS_FEMMS = 164 +UC_X86_INS_FFREE = 165 +UC_X86_INS_FICOM = 166 +UC_X86_INS_FICOMP = 167 +UC_X86_INS_FINCSTP = 168 +UC_X86_INS_FLDCW = 169 +UC_X86_INS_FLDENV = 170 +UC_X86_INS_FLDL2E = 171 +UC_X86_INS_FLDL2T = 172 +UC_X86_INS_FLDLG2 = 173 +UC_X86_INS_FLDLN2 = 174 +UC_X86_INS_FLDPI = 175 +UC_X86_INS_FNCLEX = 176 +UC_X86_INS_FNINIT = 177 +UC_X86_INS_FNOP = 178 +UC_X86_INS_FNSTCW = 179 +UC_X86_INS_FNSTSW = 180 +UC_X86_INS_FPATAN = 181 +UC_X86_INS_FPREM = 182 +UC_X86_INS_FPREM1 = 183 +UC_X86_INS_FPTAN = 184 +UC_X86_INS_FFREEP = 185 +UC_X86_INS_FRNDINT = 186 +UC_X86_INS_FRSTOR = 187 +UC_X86_INS_FNSAVE = 188 +UC_X86_INS_FSCALE = 189 +UC_X86_INS_FSETPM = 190 +UC_X86_INS_FSINCOS = 191 +UC_X86_INS_FNSTENV = 192 +UC_X86_INS_FXAM = 193 +UC_X86_INS_FXRSTOR = 194 +UC_X86_INS_FXRSTOR64 = 195 +UC_X86_INS_FXSAVE = 196 +UC_X86_INS_FXSAVE64 = 197 +UC_X86_INS_FXTRACT = 198 +UC_X86_INS_FYL2X = 199 +UC_X86_INS_FYL2XP1 = 200 +UC_X86_INS_MOVAPD = 201 +UC_X86_INS_MOVAPS = 202 +UC_X86_INS_ORPD = 203 +UC_X86_INS_ORPS = 204 +UC_X86_INS_VMOVAPD = 205 +UC_X86_INS_VMOVAPS = 206 +UC_X86_INS_XORPD = 207 +UC_X86_INS_XORPS = 208 +UC_X86_INS_GETSEC = 209 +UC_X86_INS_HADDPD = 210 +UC_X86_INS_HADDPS = 211 +UC_X86_INS_HLT = 212 +UC_X86_INS_HSUBPD = 213 +UC_X86_INS_HSUBPS = 214 +UC_X86_INS_IDIV = 215 +UC_X86_INS_FILD = 216 +UC_X86_INS_IMUL = 217 +UC_X86_INS_IN = 218 +UC_X86_INS_INC = 219 +UC_X86_INS_INSB = 220 +UC_X86_INS_INSERTPS = 221 +UC_X86_INS_INSERTQ = 222 +UC_X86_INS_INSD = 223 +UC_X86_INS_INSW = 224 +UC_X86_INS_INT = 225 +UC_X86_INS_INT1 = 226 +UC_X86_INS_INT3 = 227 +UC_X86_INS_INTO = 228 +UC_X86_INS_INVD = 229 +UC_X86_INS_INVEPT = 230 +UC_X86_INS_INVLPG = 231 +UC_X86_INS_INVLPGA = 232 +UC_X86_INS_INVPCID = 233 +UC_X86_INS_INVVPID = 234 +UC_X86_INS_IRET = 235 +UC_X86_INS_IRETD = 236 +UC_X86_INS_IRETQ = 237 +UC_X86_INS_FISTTP = 238 +UC_X86_INS_FIST = 239 +UC_X86_INS_FISTP = 240 +UC_X86_INS_UCOMISD = 241 +UC_X86_INS_UCOMISS = 242 +UC_X86_INS_VCOMISD = 243 +UC_X86_INS_VCOMISS = 244 +UC_X86_INS_VCVTSD2SS = 245 +UC_X86_INS_VCVTSI2SD = 246 +UC_X86_INS_VCVTSI2SS = 247 +UC_X86_INS_VCVTSS2SD = 248 +UC_X86_INS_VCVTTSD2SI = 249 +UC_X86_INS_VCVTTSD2USI = 250 +UC_X86_INS_VCVTTSS2SI = 251 +UC_X86_INS_VCVTTSS2USI = 252 +UC_X86_INS_VCVTUSI2SD = 253 +UC_X86_INS_VCVTUSI2SS = 254 +UC_X86_INS_VUCOMISD = 255 +UC_X86_INS_VUCOMISS = 256 +UC_X86_INS_JAE = 257 +UC_X86_INS_JA = 258 +UC_X86_INS_JBE = 259 +UC_X86_INS_JB = 260 +UC_X86_INS_JCXZ = 261 +UC_X86_INS_JECXZ = 262 +UC_X86_INS_JE = 263 +UC_X86_INS_JGE = 264 +UC_X86_INS_JG = 265 +UC_X86_INS_JLE = 266 +UC_X86_INS_JL = 267 +UC_X86_INS_JMP = 268 +UC_X86_INS_JNE = 269 +UC_X86_INS_JNO = 270 +UC_X86_INS_JNP = 271 +UC_X86_INS_JNS = 272 +UC_X86_INS_JO = 273 +UC_X86_INS_JP = 274 +UC_X86_INS_JRCXZ = 275 +UC_X86_INS_JS = 276 +UC_X86_INS_KANDB = 277 +UC_X86_INS_KANDD = 278 +UC_X86_INS_KANDNB = 279 +UC_X86_INS_KANDND = 280 +UC_X86_INS_KANDNQ = 281 +UC_X86_INS_KANDNW = 282 +UC_X86_INS_KANDQ = 283 +UC_X86_INS_KANDW = 284 +UC_X86_INS_KMOVB = 285 +UC_X86_INS_KMOVD = 286 +UC_X86_INS_KMOVQ = 287 +UC_X86_INS_KMOVW = 288 +UC_X86_INS_KNOTB = 289 +UC_X86_INS_KNOTD = 290 +UC_X86_INS_KNOTQ = 291 +UC_X86_INS_KNOTW = 292 +UC_X86_INS_KORB = 293 +UC_X86_INS_KORD = 294 +UC_X86_INS_KORQ = 295 +UC_X86_INS_KORTESTB = 296 +UC_X86_INS_KORTESTD = 297 +UC_X86_INS_KORTESTQ = 298 +UC_X86_INS_KORTESTW = 299 +UC_X86_INS_KORW = 300 +UC_X86_INS_KSHIFTLB = 301 +UC_X86_INS_KSHIFTLD = 302 +UC_X86_INS_KSHIFTLQ = 303 +UC_X86_INS_KSHIFTLW = 304 +UC_X86_INS_KSHIFTRB = 305 +UC_X86_INS_KSHIFTRD = 306 +UC_X86_INS_KSHIFTRQ = 307 +UC_X86_INS_KSHIFTRW = 308 +UC_X86_INS_KUNPCKBW = 309 +UC_X86_INS_KXNORB = 310 +UC_X86_INS_KXNORD = 311 +UC_X86_INS_KXNORQ = 312 +UC_X86_INS_KXNORW = 313 +UC_X86_INS_KXORB = 314 +UC_X86_INS_KXORD = 315 +UC_X86_INS_KXORQ = 316 +UC_X86_INS_KXORW = 317 +UC_X86_INS_LAHF = 318 +UC_X86_INS_LAR = 319 +UC_X86_INS_LDDQU = 320 +UC_X86_INS_LDMXCSR = 321 +UC_X86_INS_LDS = 322 +UC_X86_INS_FLDZ = 323 +UC_X86_INS_FLD1 = 324 +UC_X86_INS_FLD = 325 +UC_X86_INS_LEA = 326 +UC_X86_INS_LEAVE = 327 +UC_X86_INS_LES = 328 +UC_X86_INS_LFENCE = 329 +UC_X86_INS_LFS = 330 +UC_X86_INS_LGDT = 331 +UC_X86_INS_LGS = 332 +UC_X86_INS_LIDT = 333 +UC_X86_INS_LLDT = 334 +UC_X86_INS_LMSW = 335 +UC_X86_INS_OR = 336 +UC_X86_INS_SUB = 337 +UC_X86_INS_XOR = 338 +UC_X86_INS_LODSB = 339 +UC_X86_INS_LODSD = 340 +UC_X86_INS_LODSQ = 341 +UC_X86_INS_LODSW = 342 +UC_X86_INS_LOOP = 343 +UC_X86_INS_LOOPE = 344 +UC_X86_INS_LOOPNE = 345 +UC_X86_INS_RETF = 346 +UC_X86_INS_RETFQ = 347 +UC_X86_INS_LSL = 348 +UC_X86_INS_LSS = 349 +UC_X86_INS_LTR = 350 +UC_X86_INS_XADD = 351 +UC_X86_INS_LZCNT = 352 +UC_X86_INS_MASKMOVDQU = 353 +UC_X86_INS_MAXPD = 354 +UC_X86_INS_MAXPS = 355 +UC_X86_INS_MAXSD = 356 +UC_X86_INS_MAXSS = 357 +UC_X86_INS_MFENCE = 358 +UC_X86_INS_MINPD = 359 +UC_X86_INS_MINPS = 360 +UC_X86_INS_MINSD = 361 +UC_X86_INS_MINSS = 362 +UC_X86_INS_CVTPD2PI = 363 +UC_X86_INS_CVTPI2PD = 364 +UC_X86_INS_CVTPI2PS = 365 +UC_X86_INS_CVTPS2PI = 366 +UC_X86_INS_CVTTPD2PI = 367 +UC_X86_INS_CVTTPS2PI = 368 +UC_X86_INS_EMMS = 369 +UC_X86_INS_MASKMOVQ = 370 +UC_X86_INS_MOVD = 371 +UC_X86_INS_MOVDQ2Q = 372 +UC_X86_INS_MOVNTQ = 373 +UC_X86_INS_MOVQ2DQ = 374 +UC_X86_INS_MOVQ = 375 +UC_X86_INS_PABSB = 376 +UC_X86_INS_PABSD = 377 +UC_X86_INS_PABSW = 378 +UC_X86_INS_PACKSSDW = 379 +UC_X86_INS_PACKSSWB = 380 +UC_X86_INS_PACKUSWB = 381 +UC_X86_INS_PADDB = 382 +UC_X86_INS_PADDD = 383 +UC_X86_INS_PADDQ = 384 +UC_X86_INS_PADDSB = 385 +UC_X86_INS_PADDSW = 386 +UC_X86_INS_PADDUSB = 387 +UC_X86_INS_PADDUSW = 388 +UC_X86_INS_PADDW = 389 +UC_X86_INS_PALIGNR = 390 +UC_X86_INS_PANDN = 391 +UC_X86_INS_PAND = 392 +UC_X86_INS_PAVGB = 393 +UC_X86_INS_PAVGW = 394 +UC_X86_INS_PCMPEQB = 395 +UC_X86_INS_PCMPEQD = 396 +UC_X86_INS_PCMPEQW = 397 +UC_X86_INS_PCMPGTB = 398 +UC_X86_INS_PCMPGTD = 399 +UC_X86_INS_PCMPGTW = 400 +UC_X86_INS_PEXTRW = 401 +UC_X86_INS_PHADDSW = 402 +UC_X86_INS_PHADDW = 403 +UC_X86_INS_PHADDD = 404 +UC_X86_INS_PHSUBD = 405 +UC_X86_INS_PHSUBSW = 406 +UC_X86_INS_PHSUBW = 407 +UC_X86_INS_PINSRW = 408 +UC_X86_INS_PMADDUBSW = 409 +UC_X86_INS_PMADDWD = 410 +UC_X86_INS_PMAXSW = 411 +UC_X86_INS_PMAXUB = 412 +UC_X86_INS_PMINSW = 413 +UC_X86_INS_PMINUB = 414 +UC_X86_INS_PMOVMSKB = 415 +UC_X86_INS_PMULHRSW = 416 +UC_X86_INS_PMULHUW = 417 +UC_X86_INS_PMULHW = 418 +UC_X86_INS_PMULLW = 419 +UC_X86_INS_PMULUDQ = 420 +UC_X86_INS_POR = 421 +UC_X86_INS_PSADBW = 422 +UC_X86_INS_PSHUFB = 423 +UC_X86_INS_PSHUFW = 424 +UC_X86_INS_PSIGNB = 425 +UC_X86_INS_PSIGND = 426 +UC_X86_INS_PSIGNW = 427 +UC_X86_INS_PSLLD = 428 +UC_X86_INS_PSLLQ = 429 +UC_X86_INS_PSLLW = 430 +UC_X86_INS_PSRAD = 431 +UC_X86_INS_PSRAW = 432 +UC_X86_INS_PSRLD = 433 +UC_X86_INS_PSRLQ = 434 +UC_X86_INS_PSRLW = 435 +UC_X86_INS_PSUBB = 436 +UC_X86_INS_PSUBD = 437 +UC_X86_INS_PSUBQ = 438 +UC_X86_INS_PSUBSB = 439 +UC_X86_INS_PSUBSW = 440 +UC_X86_INS_PSUBUSB = 441 +UC_X86_INS_PSUBUSW = 442 +UC_X86_INS_PSUBW = 443 +UC_X86_INS_PUNPCKHBW = 444 +UC_X86_INS_PUNPCKHDQ = 445 +UC_X86_INS_PUNPCKHWD = 446 +UC_X86_INS_PUNPCKLBW = 447 +UC_X86_INS_PUNPCKLDQ = 448 +UC_X86_INS_PUNPCKLWD = 449 +UC_X86_INS_PXOR = 450 +UC_X86_INS_MONITOR = 451 +UC_X86_INS_MONTMUL = 452 +UC_X86_INS_MOV = 453 +UC_X86_INS_MOVABS = 454 +UC_X86_INS_MOVBE = 455 +UC_X86_INS_MOVDDUP = 456 +UC_X86_INS_MOVDQA = 457 +UC_X86_INS_MOVDQU = 458 +UC_X86_INS_MOVHLPS = 459 +UC_X86_INS_MOVHPD = 460 +UC_X86_INS_MOVHPS = 461 +UC_X86_INS_MOVLHPS = 462 +UC_X86_INS_MOVLPD = 463 +UC_X86_INS_MOVLPS = 464 +UC_X86_INS_MOVMSKPD = 465 +UC_X86_INS_MOVMSKPS = 466 +UC_X86_INS_MOVNTDQA = 467 +UC_X86_INS_MOVNTDQ = 468 +UC_X86_INS_MOVNTI = 469 +UC_X86_INS_MOVNTPD = 470 +UC_X86_INS_MOVNTPS = 471 +UC_X86_INS_MOVNTSD = 472 +UC_X86_INS_MOVNTSS = 473 +UC_X86_INS_MOVSB = 474 +UC_X86_INS_MOVSD = 475 +UC_X86_INS_MOVSHDUP = 476 +UC_X86_INS_MOVSLDUP = 477 +UC_X86_INS_MOVSQ = 478 +UC_X86_INS_MOVSS = 479 +UC_X86_INS_MOVSW = 480 +UC_X86_INS_MOVSX = 481 +UC_X86_INS_MOVSXD = 482 +UC_X86_INS_MOVUPD = 483 +UC_X86_INS_MOVUPS = 484 +UC_X86_INS_MOVZX = 485 +UC_X86_INS_MPSADBW = 486 +UC_X86_INS_MUL = 487 +UC_X86_INS_MULPD = 488 +UC_X86_INS_MULPS = 489 +UC_X86_INS_MULSD = 490 +UC_X86_INS_MULSS = 491 +UC_X86_INS_MULX = 492 +UC_X86_INS_FMUL = 493 +UC_X86_INS_FIMUL = 494 +UC_X86_INS_FMULP = 495 +UC_X86_INS_MWAIT = 496 +UC_X86_INS_NEG = 497 +UC_X86_INS_NOP = 498 +UC_X86_INS_NOT = 499 +UC_X86_INS_OUT = 500 +UC_X86_INS_OUTSB = 501 +UC_X86_INS_OUTSD = 502 +UC_X86_INS_OUTSW = 503 +UC_X86_INS_PACKUSDW = 504 +UC_X86_INS_PAUSE = 505 +UC_X86_INS_PAVGUSB = 506 +UC_X86_INS_PBLENDVB = 507 +UC_X86_INS_PBLENDW = 508 +UC_X86_INS_PCLMULQDQ = 509 +UC_X86_INS_PCMPEQQ = 510 +UC_X86_INS_PCMPESTRI = 511 +UC_X86_INS_PCMPESTRM = 512 +UC_X86_INS_PCMPGTQ = 513 +UC_X86_INS_PCMPISTRI = 514 +UC_X86_INS_PCMPISTRM = 515 +UC_X86_INS_PCOMMIT = 516 +UC_X86_INS_PDEP = 517 +UC_X86_INS_PEXT = 518 +UC_X86_INS_PEXTRB = 519 +UC_X86_INS_PEXTRD = 520 +UC_X86_INS_PEXTRQ = 521 +UC_X86_INS_PF2ID = 522 +UC_X86_INS_PF2IW = 523 +UC_X86_INS_PFACC = 524 +UC_X86_INS_PFADD = 525 +UC_X86_INS_PFCMPEQ = 526 +UC_X86_INS_PFCMPGE = 527 +UC_X86_INS_PFCMPGT = 528 +UC_X86_INS_PFMAX = 529 +UC_X86_INS_PFMIN = 530 +UC_X86_INS_PFMUL = 531 +UC_X86_INS_PFNACC = 532 +UC_X86_INS_PFPNACC = 533 +UC_X86_INS_PFRCPIT1 = 534 +UC_X86_INS_PFRCPIT2 = 535 +UC_X86_INS_PFRCP = 536 +UC_X86_INS_PFRSQIT1 = 537 +UC_X86_INS_PFRSQRT = 538 +UC_X86_INS_PFSUBR = 539 +UC_X86_INS_PFSUB = 540 +UC_X86_INS_PHMINPOSUW = 541 +UC_X86_INS_PI2FD = 542 +UC_X86_INS_PI2FW = 543 +UC_X86_INS_PINSRB = 544 +UC_X86_INS_PINSRD = 545 +UC_X86_INS_PINSRQ = 546 +UC_X86_INS_PMAXSB = 547 +UC_X86_INS_PMAXSD = 548 +UC_X86_INS_PMAXUD = 549 +UC_X86_INS_PMAXUW = 550 +UC_X86_INS_PMINSB = 551 +UC_X86_INS_PMINSD = 552 +UC_X86_INS_PMINUD = 553 +UC_X86_INS_PMINUW = 554 +UC_X86_INS_PMOVSXBD = 555 +UC_X86_INS_PMOVSXBQ = 556 +UC_X86_INS_PMOVSXBW = 557 +UC_X86_INS_PMOVSXDQ = 558 +UC_X86_INS_PMOVSXWD = 559 +UC_X86_INS_PMOVSXWQ = 560 +UC_X86_INS_PMOVZXBD = 561 +UC_X86_INS_PMOVZXBQ = 562 +UC_X86_INS_PMOVZXBW = 563 +UC_X86_INS_PMOVZXDQ = 564 +UC_X86_INS_PMOVZXWD = 565 +UC_X86_INS_PMOVZXWQ = 566 +UC_X86_INS_PMULDQ = 567 +UC_X86_INS_PMULHRW = 568 +UC_X86_INS_PMULLD = 569 +UC_X86_INS_POP = 570 +UC_X86_INS_POPAW = 571 +UC_X86_INS_POPAL = 572 +UC_X86_INS_POPCNT = 573 +UC_X86_INS_POPF = 574 +UC_X86_INS_POPFD = 575 +UC_X86_INS_POPFQ = 576 +UC_X86_INS_PREFETCH = 577 +UC_X86_INS_PREFETCHNTA = 578 +UC_X86_INS_PREFETCHT0 = 579 +UC_X86_INS_PREFETCHT1 = 580 +UC_X86_INS_PREFETCHT2 = 581 +UC_X86_INS_PREFETCHW = 582 +UC_X86_INS_PSHUFD = 583 +UC_X86_INS_PSHUFHW = 584 +UC_X86_INS_PSHUFLW = 585 +UC_X86_INS_PSLLDQ = 586 +UC_X86_INS_PSRLDQ = 587 +UC_X86_INS_PSWAPD = 588 +UC_X86_INS_PTEST = 589 +UC_X86_INS_PUNPCKHQDQ = 590 +UC_X86_INS_PUNPCKLQDQ = 591 +UC_X86_INS_PUSH = 592 +UC_X86_INS_PUSHAW = 593 +UC_X86_INS_PUSHAL = 594 +UC_X86_INS_PUSHF = 595 +UC_X86_INS_PUSHFD = 596 +UC_X86_INS_PUSHFQ = 597 +UC_X86_INS_RCL = 598 +UC_X86_INS_RCPPS = 599 +UC_X86_INS_RCPSS = 600 +UC_X86_INS_RCR = 601 +UC_X86_INS_RDFSBASE = 602 +UC_X86_INS_RDGSBASE = 603 +UC_X86_INS_RDMSR = 604 +UC_X86_INS_RDPMC = 605 +UC_X86_INS_RDRAND = 606 +UC_X86_INS_RDSEED = 607 +UC_X86_INS_RDTSC = 608 +UC_X86_INS_RDTSCP = 609 +UC_X86_INS_ROL = 610 +UC_X86_INS_ROR = 611 +UC_X86_INS_RORX = 612 +UC_X86_INS_ROUNDPD = 613 +UC_X86_INS_ROUNDPS = 614 +UC_X86_INS_ROUNDSD = 615 +UC_X86_INS_ROUNDSS = 616 +UC_X86_INS_RSM = 617 +UC_X86_INS_RSQRTPS = 618 +UC_X86_INS_RSQRTSS = 619 +UC_X86_INS_SAHF = 620 +UC_X86_INS_SAL = 621 +UC_X86_INS_SALC = 622 +UC_X86_INS_SAR = 623 +UC_X86_INS_SARX = 624 +UC_X86_INS_SBB = 625 +UC_X86_INS_SCASB = 626 +UC_X86_INS_SCASD = 627 +UC_X86_INS_SCASQ = 628 +UC_X86_INS_SCASW = 629 +UC_X86_INS_SETAE = 630 +UC_X86_INS_SETA = 631 +UC_X86_INS_SETBE = 632 +UC_X86_INS_SETB = 633 +UC_X86_INS_SETE = 634 +UC_X86_INS_SETGE = 635 +UC_X86_INS_SETG = 636 +UC_X86_INS_SETLE = 637 +UC_X86_INS_SETL = 638 +UC_X86_INS_SETNE = 639 +UC_X86_INS_SETNO = 640 +UC_X86_INS_SETNP = 641 +UC_X86_INS_SETNS = 642 +UC_X86_INS_SETO = 643 +UC_X86_INS_SETP = 644 +UC_X86_INS_SETS = 645 +UC_X86_INS_SFENCE = 646 +UC_X86_INS_SGDT = 647 +UC_X86_INS_SHA1MSG1 = 648 +UC_X86_INS_SHA1MSG2 = 649 +UC_X86_INS_SHA1NEXTE = 650 +UC_X86_INS_SHA1RNDS4 = 651 +UC_X86_INS_SHA256MSG1 = 652 +UC_X86_INS_SHA256MSG2 = 653 +UC_X86_INS_SHA256RNDS2 = 654 +UC_X86_INS_SHL = 655 +UC_X86_INS_SHLD = 656 +UC_X86_INS_SHLX = 657 +UC_X86_INS_SHR = 658 +UC_X86_INS_SHRD = 659 +UC_X86_INS_SHRX = 660 +UC_X86_INS_SHUFPD = 661 +UC_X86_INS_SHUFPS = 662 +UC_X86_INS_SIDT = 663 +UC_X86_INS_FSIN = 664 +UC_X86_INS_SKINIT = 665 +UC_X86_INS_SLDT = 666 +UC_X86_INS_SMSW = 667 +UC_X86_INS_SQRTPD = 668 +UC_X86_INS_SQRTPS = 669 +UC_X86_INS_SQRTSD = 670 +UC_X86_INS_SQRTSS = 671 +UC_X86_INS_FSQRT = 672 +UC_X86_INS_STAC = 673 +UC_X86_INS_STC = 674 +UC_X86_INS_STD = 675 +UC_X86_INS_STGI = 676 +UC_X86_INS_STI = 677 +UC_X86_INS_STMXCSR = 678 +UC_X86_INS_STOSB = 679 +UC_X86_INS_STOSD = 680 +UC_X86_INS_STOSQ = 681 +UC_X86_INS_STOSW = 682 +UC_X86_INS_STR = 683 +UC_X86_INS_FST = 684 +UC_X86_INS_FSTP = 685 +UC_X86_INS_FSTPNCE = 686 +UC_X86_INS_FXCH = 687 +UC_X86_INS_SUBPD = 688 +UC_X86_INS_SUBPS = 689 +UC_X86_INS_FSUBR = 690 +UC_X86_INS_FISUBR = 691 +UC_X86_INS_FSUBRP = 692 +UC_X86_INS_SUBSD = 693 +UC_X86_INS_SUBSS = 694 +UC_X86_INS_FSUB = 695 +UC_X86_INS_FISUB = 696 +UC_X86_INS_FSUBP = 697 +UC_X86_INS_SWAPGS = 698 +UC_X86_INS_SYSCALL = 699 +UC_X86_INS_SYSENTER = 700 +UC_X86_INS_SYSEXIT = 701 +UC_X86_INS_SYSRET = 702 +UC_X86_INS_T1MSKC = 703 +UC_X86_INS_TEST = 704 +UC_X86_INS_UD2 = 705 +UC_X86_INS_FTST = 706 +UC_X86_INS_TZCNT = 707 +UC_X86_INS_TZMSK = 708 +UC_X86_INS_FUCOMPI = 709 +UC_X86_INS_FUCOMI = 710 +UC_X86_INS_FUCOMPP = 711 +UC_X86_INS_FUCOMP = 712 +UC_X86_INS_FUCOM = 713 +UC_X86_INS_UD2B = 714 +UC_X86_INS_UNPCKHPD = 715 +UC_X86_INS_UNPCKHPS = 716 +UC_X86_INS_UNPCKLPD = 717 +UC_X86_INS_UNPCKLPS = 718 +UC_X86_INS_VADDPD = 719 +UC_X86_INS_VADDPS = 720 +UC_X86_INS_VADDSD = 721 +UC_X86_INS_VADDSS = 722 +UC_X86_INS_VADDSUBPD = 723 +UC_X86_INS_VADDSUBPS = 724 +UC_X86_INS_VAESDECLAST = 725 +UC_X86_INS_VAESDEC = 726 +UC_X86_INS_VAESENCLAST = 727 +UC_X86_INS_VAESENC = 728 +UC_X86_INS_VAESIMC = 729 +UC_X86_INS_VAESKEYGENASSIST = 730 +UC_X86_INS_VALIGND = 731 +UC_X86_INS_VALIGNQ = 732 +UC_X86_INS_VANDNPD = 733 +UC_X86_INS_VANDNPS = 734 +UC_X86_INS_VANDPD = 735 +UC_X86_INS_VANDPS = 736 +UC_X86_INS_VBLENDMPD = 737 +UC_X86_INS_VBLENDMPS = 738 +UC_X86_INS_VBLENDPD = 739 +UC_X86_INS_VBLENDPS = 740 +UC_X86_INS_VBLENDVPD = 741 +UC_X86_INS_VBLENDVPS = 742 +UC_X86_INS_VBROADCASTF128 = 743 +UC_X86_INS_VBROADCASTI32X4 = 744 +UC_X86_INS_VBROADCASTI64X4 = 745 +UC_X86_INS_VBROADCASTSD = 746 +UC_X86_INS_VBROADCASTSS = 747 +UC_X86_INS_VCMPPD = 748 +UC_X86_INS_VCMPPS = 749 +UC_X86_INS_VCMPSD = 750 +UC_X86_INS_VCMPSS = 751 +UC_X86_INS_VCOMPRESSPD = 752 +UC_X86_INS_VCOMPRESSPS = 753 +UC_X86_INS_VCVTDQ2PD = 754 +UC_X86_INS_VCVTDQ2PS = 755 +UC_X86_INS_VCVTPD2DQX = 756 +UC_X86_INS_VCVTPD2DQ = 757 +UC_X86_INS_VCVTPD2PSX = 758 +UC_X86_INS_VCVTPD2PS = 759 +UC_X86_INS_VCVTPD2UDQ = 760 +UC_X86_INS_VCVTPH2PS = 761 +UC_X86_INS_VCVTPS2DQ = 762 +UC_X86_INS_VCVTPS2PD = 763 +UC_X86_INS_VCVTPS2PH = 764 +UC_X86_INS_VCVTPS2UDQ = 765 +UC_X86_INS_VCVTSD2SI = 766 +UC_X86_INS_VCVTSD2USI = 767 +UC_X86_INS_VCVTSS2SI = 768 +UC_X86_INS_VCVTSS2USI = 769 +UC_X86_INS_VCVTTPD2DQX = 770 +UC_X86_INS_VCVTTPD2DQ = 771 +UC_X86_INS_VCVTTPD2UDQ = 772 +UC_X86_INS_VCVTTPS2DQ = 773 +UC_X86_INS_VCVTTPS2UDQ = 774 +UC_X86_INS_VCVTUDQ2PD = 775 +UC_X86_INS_VCVTUDQ2PS = 776 +UC_X86_INS_VDIVPD = 777 +UC_X86_INS_VDIVPS = 778 +UC_X86_INS_VDIVSD = 779 +UC_X86_INS_VDIVSS = 780 +UC_X86_INS_VDPPD = 781 +UC_X86_INS_VDPPS = 782 +UC_X86_INS_VERR = 783 +UC_X86_INS_VERW = 784 +UC_X86_INS_VEXP2PD = 785 +UC_X86_INS_VEXP2PS = 786 +UC_X86_INS_VEXPANDPD = 787 +UC_X86_INS_VEXPANDPS = 788 +UC_X86_INS_VEXTRACTF128 = 789 +UC_X86_INS_VEXTRACTF32X4 = 790 +UC_X86_INS_VEXTRACTF64X4 = 791 +UC_X86_INS_VEXTRACTI128 = 792 +UC_X86_INS_VEXTRACTI32X4 = 793 +UC_X86_INS_VEXTRACTI64X4 = 794 +UC_X86_INS_VEXTRACTPS = 795 +UC_X86_INS_VFMADD132PD = 796 +UC_X86_INS_VFMADD132PS = 797 +UC_X86_INS_VFMADDPD = 798 +UC_X86_INS_VFMADD213PD = 799 +UC_X86_INS_VFMADD231PD = 800 +UC_X86_INS_VFMADDPS = 801 +UC_X86_INS_VFMADD213PS = 802 +UC_X86_INS_VFMADD231PS = 803 +UC_X86_INS_VFMADDSD = 804 +UC_X86_INS_VFMADD213SD = 805 +UC_X86_INS_VFMADD132SD = 806 +UC_X86_INS_VFMADD231SD = 807 +UC_X86_INS_VFMADDSS = 808 +UC_X86_INS_VFMADD213SS = 809 +UC_X86_INS_VFMADD132SS = 810 +UC_X86_INS_VFMADD231SS = 811 +UC_X86_INS_VFMADDSUB132PD = 812 +UC_X86_INS_VFMADDSUB132PS = 813 +UC_X86_INS_VFMADDSUBPD = 814 +UC_X86_INS_VFMADDSUB213PD = 815 +UC_X86_INS_VFMADDSUB231PD = 816 +UC_X86_INS_VFMADDSUBPS = 817 +UC_X86_INS_VFMADDSUB213PS = 818 +UC_X86_INS_VFMADDSUB231PS = 819 +UC_X86_INS_VFMSUB132PD = 820 +UC_X86_INS_VFMSUB132PS = 821 +UC_X86_INS_VFMSUBADD132PD = 822 +UC_X86_INS_VFMSUBADD132PS = 823 +UC_X86_INS_VFMSUBADDPD = 824 +UC_X86_INS_VFMSUBADD213PD = 825 +UC_X86_INS_VFMSUBADD231PD = 826 +UC_X86_INS_VFMSUBADDPS = 827 +UC_X86_INS_VFMSUBADD213PS = 828 +UC_X86_INS_VFMSUBADD231PS = 829 +UC_X86_INS_VFMSUBPD = 830 +UC_X86_INS_VFMSUB213PD = 831 +UC_X86_INS_VFMSUB231PD = 832 +UC_X86_INS_VFMSUBPS = 833 +UC_X86_INS_VFMSUB213PS = 834 +UC_X86_INS_VFMSUB231PS = 835 +UC_X86_INS_VFMSUBSD = 836 +UC_X86_INS_VFMSUB213SD = 837 +UC_X86_INS_VFMSUB132SD = 838 +UC_X86_INS_VFMSUB231SD = 839 +UC_X86_INS_VFMSUBSS = 840 +UC_X86_INS_VFMSUB213SS = 841 +UC_X86_INS_VFMSUB132SS = 842 +UC_X86_INS_VFMSUB231SS = 843 +UC_X86_INS_VFNMADD132PD = 844 +UC_X86_INS_VFNMADD132PS = 845 +UC_X86_INS_VFNMADDPD = 846 +UC_X86_INS_VFNMADD213PD = 847 +UC_X86_INS_VFNMADD231PD = 848 +UC_X86_INS_VFNMADDPS = 849 +UC_X86_INS_VFNMADD213PS = 850 +UC_X86_INS_VFNMADD231PS = 851 +UC_X86_INS_VFNMADDSD = 852 +UC_X86_INS_VFNMADD213SD = 853 +UC_X86_INS_VFNMADD132SD = 854 +UC_X86_INS_VFNMADD231SD = 855 +UC_X86_INS_VFNMADDSS = 856 +UC_X86_INS_VFNMADD213SS = 857 +UC_X86_INS_VFNMADD132SS = 858 +UC_X86_INS_VFNMADD231SS = 859 +UC_X86_INS_VFNMSUB132PD = 860 +UC_X86_INS_VFNMSUB132PS = 861 +UC_X86_INS_VFNMSUBPD = 862 +UC_X86_INS_VFNMSUB213PD = 863 +UC_X86_INS_VFNMSUB231PD = 864 +UC_X86_INS_VFNMSUBPS = 865 +UC_X86_INS_VFNMSUB213PS = 866 +UC_X86_INS_VFNMSUB231PS = 867 +UC_X86_INS_VFNMSUBSD = 868 +UC_X86_INS_VFNMSUB213SD = 869 +UC_X86_INS_VFNMSUB132SD = 870 +UC_X86_INS_VFNMSUB231SD = 871 +UC_X86_INS_VFNMSUBSS = 872 +UC_X86_INS_VFNMSUB213SS = 873 +UC_X86_INS_VFNMSUB132SS = 874 +UC_X86_INS_VFNMSUB231SS = 875 +UC_X86_INS_VFRCZPD = 876 +UC_X86_INS_VFRCZPS = 877 +UC_X86_INS_VFRCZSD = 878 +UC_X86_INS_VFRCZSS = 879 +UC_X86_INS_VORPD = 880 +UC_X86_INS_VORPS = 881 +UC_X86_INS_VXORPD = 882 +UC_X86_INS_VXORPS = 883 +UC_X86_INS_VGATHERDPD = 884 +UC_X86_INS_VGATHERDPS = 885 +UC_X86_INS_VGATHERPF0DPD = 886 +UC_X86_INS_VGATHERPF0DPS = 887 +UC_X86_INS_VGATHERPF0QPD = 888 +UC_X86_INS_VGATHERPF0QPS = 889 +UC_X86_INS_VGATHERPF1DPD = 890 +UC_X86_INS_VGATHERPF1DPS = 891 +UC_X86_INS_VGATHERPF1QPD = 892 +UC_X86_INS_VGATHERPF1QPS = 893 +UC_X86_INS_VGATHERQPD = 894 +UC_X86_INS_VGATHERQPS = 895 +UC_X86_INS_VHADDPD = 896 +UC_X86_INS_VHADDPS = 897 +UC_X86_INS_VHSUBPD = 898 +UC_X86_INS_VHSUBPS = 899 +UC_X86_INS_VINSERTF128 = 900 +UC_X86_INS_VINSERTF32X4 = 901 +UC_X86_INS_VINSERTF32X8 = 902 +UC_X86_INS_VINSERTF64X2 = 903 +UC_X86_INS_VINSERTF64X4 = 904 +UC_X86_INS_VINSERTI128 = 905 +UC_X86_INS_VINSERTI32X4 = 906 +UC_X86_INS_VINSERTI32X8 = 907 +UC_X86_INS_VINSERTI64X2 = 908 +UC_X86_INS_VINSERTI64X4 = 909 +UC_X86_INS_VINSERTPS = 910 +UC_X86_INS_VLDDQU = 911 +UC_X86_INS_VLDMXCSR = 912 +UC_X86_INS_VMASKMOVDQU = 913 +UC_X86_INS_VMASKMOVPD = 914 +UC_X86_INS_VMASKMOVPS = 915 +UC_X86_INS_VMAXPD = 916 +UC_X86_INS_VMAXPS = 917 +UC_X86_INS_VMAXSD = 918 +UC_X86_INS_VMAXSS = 919 +UC_X86_INS_VMCALL = 920 +UC_X86_INS_VMCLEAR = 921 +UC_X86_INS_VMFUNC = 922 +UC_X86_INS_VMINPD = 923 +UC_X86_INS_VMINPS = 924 +UC_X86_INS_VMINSD = 925 +UC_X86_INS_VMINSS = 926 +UC_X86_INS_VMLAUNCH = 927 +UC_X86_INS_VMLOAD = 928 +UC_X86_INS_VMMCALL = 929 +UC_X86_INS_VMOVQ = 930 +UC_X86_INS_VMOVDDUP = 931 +UC_X86_INS_VMOVD = 932 +UC_X86_INS_VMOVDQA32 = 933 +UC_X86_INS_VMOVDQA64 = 934 +UC_X86_INS_VMOVDQA = 935 +UC_X86_INS_VMOVDQU16 = 936 +UC_X86_INS_VMOVDQU32 = 937 +UC_X86_INS_VMOVDQU64 = 938 +UC_X86_INS_VMOVDQU8 = 939 +UC_X86_INS_VMOVDQU = 940 +UC_X86_INS_VMOVHLPS = 941 +UC_X86_INS_VMOVHPD = 942 +UC_X86_INS_VMOVHPS = 943 +UC_X86_INS_VMOVLHPS = 944 +UC_X86_INS_VMOVLPD = 945 +UC_X86_INS_VMOVLPS = 946 +UC_X86_INS_VMOVMSKPD = 947 +UC_X86_INS_VMOVMSKPS = 948 +UC_X86_INS_VMOVNTDQA = 949 +UC_X86_INS_VMOVNTDQ = 950 +UC_X86_INS_VMOVNTPD = 951 +UC_X86_INS_VMOVNTPS = 952 +UC_X86_INS_VMOVSD = 953 +UC_X86_INS_VMOVSHDUP = 954 +UC_X86_INS_VMOVSLDUP = 955 +UC_X86_INS_VMOVSS = 956 +UC_X86_INS_VMOVUPD = 957 +UC_X86_INS_VMOVUPS = 958 +UC_X86_INS_VMPSADBW = 959 +UC_X86_INS_VMPTRLD = 960 +UC_X86_INS_VMPTRST = 961 +UC_X86_INS_VMREAD = 962 +UC_X86_INS_VMRESUME = 963 +UC_X86_INS_VMRUN = 964 +UC_X86_INS_VMSAVE = 965 +UC_X86_INS_VMULPD = 966 +UC_X86_INS_VMULPS = 967 +UC_X86_INS_VMULSD = 968 +UC_X86_INS_VMULSS = 969 +UC_X86_INS_VMWRITE = 970 +UC_X86_INS_VMXOFF = 971 +UC_X86_INS_VMXON = 972 +UC_X86_INS_VPABSB = 973 +UC_X86_INS_VPABSD = 974 +UC_X86_INS_VPABSQ = 975 +UC_X86_INS_VPABSW = 976 +UC_X86_INS_VPACKSSDW = 977 +UC_X86_INS_VPACKSSWB = 978 +UC_X86_INS_VPACKUSDW = 979 +UC_X86_INS_VPACKUSWB = 980 +UC_X86_INS_VPADDB = 981 +UC_X86_INS_VPADDD = 982 +UC_X86_INS_VPADDQ = 983 +UC_X86_INS_VPADDSB = 984 +UC_X86_INS_VPADDSW = 985 +UC_X86_INS_VPADDUSB = 986 +UC_X86_INS_VPADDUSW = 987 +UC_X86_INS_VPADDW = 988 +UC_X86_INS_VPALIGNR = 989 +UC_X86_INS_VPANDD = 990 +UC_X86_INS_VPANDND = 991 +UC_X86_INS_VPANDNQ = 992 +UC_X86_INS_VPANDN = 993 +UC_X86_INS_VPANDQ = 994 +UC_X86_INS_VPAND = 995 +UC_X86_INS_VPAVGB = 996 +UC_X86_INS_VPAVGW = 997 +UC_X86_INS_VPBLENDD = 998 +UC_X86_INS_VPBLENDMB = 999 +UC_X86_INS_VPBLENDMD = 1000 +UC_X86_INS_VPBLENDMQ = 1001 +UC_X86_INS_VPBLENDMW = 1002 +UC_X86_INS_VPBLENDVB = 1003 +UC_X86_INS_VPBLENDW = 1004 +UC_X86_INS_VPBROADCASTB = 1005 +UC_X86_INS_VPBROADCASTD = 1006 +UC_X86_INS_VPBROADCASTMB2Q = 1007 +UC_X86_INS_VPBROADCASTMW2D = 1008 +UC_X86_INS_VPBROADCASTQ = 1009 +UC_X86_INS_VPBROADCASTW = 1010 +UC_X86_INS_VPCLMULQDQ = 1011 +UC_X86_INS_VPCMOV = 1012 +UC_X86_INS_VPCMPB = 1013 +UC_X86_INS_VPCMPD = 1014 +UC_X86_INS_VPCMPEQB = 1015 +UC_X86_INS_VPCMPEQD = 1016 +UC_X86_INS_VPCMPEQQ = 1017 +UC_X86_INS_VPCMPEQW = 1018 +UC_X86_INS_VPCMPESTRI = 1019 +UC_X86_INS_VPCMPESTRM = 1020 +UC_X86_INS_VPCMPGTB = 1021 +UC_X86_INS_VPCMPGTD = 1022 +UC_X86_INS_VPCMPGTQ = 1023 +UC_X86_INS_VPCMPGTW = 1024 +UC_X86_INS_VPCMPISTRI = 1025 +UC_X86_INS_VPCMPISTRM = 1026 +UC_X86_INS_VPCMPQ = 1027 +UC_X86_INS_VPCMPUB = 1028 +UC_X86_INS_VPCMPUD = 1029 +UC_X86_INS_VPCMPUQ = 1030 +UC_X86_INS_VPCMPUW = 1031 +UC_X86_INS_VPCMPW = 1032 +UC_X86_INS_VPCOMB = 1033 +UC_X86_INS_VPCOMD = 1034 +UC_X86_INS_VPCOMPRESSD = 1035 +UC_X86_INS_VPCOMPRESSQ = 1036 +UC_X86_INS_VPCOMQ = 1037 +UC_X86_INS_VPCOMUB = 1038 +UC_X86_INS_VPCOMUD = 1039 +UC_X86_INS_VPCOMUQ = 1040 +UC_X86_INS_VPCOMUW = 1041 +UC_X86_INS_VPCOMW = 1042 +UC_X86_INS_VPCONFLICTD = 1043 +UC_X86_INS_VPCONFLICTQ = 1044 +UC_X86_INS_VPERM2F128 = 1045 +UC_X86_INS_VPERM2I128 = 1046 +UC_X86_INS_VPERMD = 1047 +UC_X86_INS_VPERMI2D = 1048 +UC_X86_INS_VPERMI2PD = 1049 +UC_X86_INS_VPERMI2PS = 1050 +UC_X86_INS_VPERMI2Q = 1051 +UC_X86_INS_VPERMIL2PD = 1052 +UC_X86_INS_VPERMIL2PS = 1053 +UC_X86_INS_VPERMILPD = 1054 +UC_X86_INS_VPERMILPS = 1055 +UC_X86_INS_VPERMPD = 1056 +UC_X86_INS_VPERMPS = 1057 +UC_X86_INS_VPERMQ = 1058 +UC_X86_INS_VPERMT2D = 1059 +UC_X86_INS_VPERMT2PD = 1060 +UC_X86_INS_VPERMT2PS = 1061 +UC_X86_INS_VPERMT2Q = 1062 +UC_X86_INS_VPEXPANDD = 1063 +UC_X86_INS_VPEXPANDQ = 1064 +UC_X86_INS_VPEXTRB = 1065 +UC_X86_INS_VPEXTRD = 1066 +UC_X86_INS_VPEXTRQ = 1067 +UC_X86_INS_VPEXTRW = 1068 +UC_X86_INS_VPGATHERDD = 1069 +UC_X86_INS_VPGATHERDQ = 1070 +UC_X86_INS_VPGATHERQD = 1071 +UC_X86_INS_VPGATHERQQ = 1072 +UC_X86_INS_VPHADDBD = 1073 +UC_X86_INS_VPHADDBQ = 1074 +UC_X86_INS_VPHADDBW = 1075 +UC_X86_INS_VPHADDDQ = 1076 +UC_X86_INS_VPHADDD = 1077 +UC_X86_INS_VPHADDSW = 1078 +UC_X86_INS_VPHADDUBD = 1079 +UC_X86_INS_VPHADDUBQ = 1080 +UC_X86_INS_VPHADDUBW = 1081 +UC_X86_INS_VPHADDUDQ = 1082 +UC_X86_INS_VPHADDUWD = 1083 +UC_X86_INS_VPHADDUWQ = 1084 +UC_X86_INS_VPHADDWD = 1085 +UC_X86_INS_VPHADDWQ = 1086 +UC_X86_INS_VPHADDW = 1087 +UC_X86_INS_VPHMINPOSUW = 1088 +UC_X86_INS_VPHSUBBW = 1089 +UC_X86_INS_VPHSUBDQ = 1090 +UC_X86_INS_VPHSUBD = 1091 +UC_X86_INS_VPHSUBSW = 1092 +UC_X86_INS_VPHSUBWD = 1093 +UC_X86_INS_VPHSUBW = 1094 +UC_X86_INS_VPINSRB = 1095 +UC_X86_INS_VPINSRD = 1096 +UC_X86_INS_VPINSRQ = 1097 +UC_X86_INS_VPINSRW = 1098 +UC_X86_INS_VPLZCNTD = 1099 +UC_X86_INS_VPLZCNTQ = 1100 +UC_X86_INS_VPMACSDD = 1101 +UC_X86_INS_VPMACSDQH = 1102 +UC_X86_INS_VPMACSDQL = 1103 +UC_X86_INS_VPMACSSDD = 1104 +UC_X86_INS_VPMACSSDQH = 1105 +UC_X86_INS_VPMACSSDQL = 1106 +UC_X86_INS_VPMACSSWD = 1107 +UC_X86_INS_VPMACSSWW = 1108 +UC_X86_INS_VPMACSWD = 1109 +UC_X86_INS_VPMACSWW = 1110 +UC_X86_INS_VPMADCSSWD = 1111 +UC_X86_INS_VPMADCSWD = 1112 +UC_X86_INS_VPMADDUBSW = 1113 +UC_X86_INS_VPMADDWD = 1114 +UC_X86_INS_VPMASKMOVD = 1115 +UC_X86_INS_VPMASKMOVQ = 1116 +UC_X86_INS_VPMAXSB = 1117 +UC_X86_INS_VPMAXSD = 1118 +UC_X86_INS_VPMAXSQ = 1119 +UC_X86_INS_VPMAXSW = 1120 +UC_X86_INS_VPMAXUB = 1121 +UC_X86_INS_VPMAXUD = 1122 +UC_X86_INS_VPMAXUQ = 1123 +UC_X86_INS_VPMAXUW = 1124 +UC_X86_INS_VPMINSB = 1125 +UC_X86_INS_VPMINSD = 1126 +UC_X86_INS_VPMINSQ = 1127 +UC_X86_INS_VPMINSW = 1128 +UC_X86_INS_VPMINUB = 1129 +UC_X86_INS_VPMINUD = 1130 +UC_X86_INS_VPMINUQ = 1131 +UC_X86_INS_VPMINUW = 1132 +UC_X86_INS_VPMOVDB = 1133 +UC_X86_INS_VPMOVDW = 1134 +UC_X86_INS_VPMOVM2B = 1135 +UC_X86_INS_VPMOVM2D = 1136 +UC_X86_INS_VPMOVM2Q = 1137 +UC_X86_INS_VPMOVM2W = 1138 +UC_X86_INS_VPMOVMSKB = 1139 +UC_X86_INS_VPMOVQB = 1140 +UC_X86_INS_VPMOVQD = 1141 +UC_X86_INS_VPMOVQW = 1142 +UC_X86_INS_VPMOVSDB = 1143 +UC_X86_INS_VPMOVSDW = 1144 +UC_X86_INS_VPMOVSQB = 1145 +UC_X86_INS_VPMOVSQD = 1146 +UC_X86_INS_VPMOVSQW = 1147 +UC_X86_INS_VPMOVSXBD = 1148 +UC_X86_INS_VPMOVSXBQ = 1149 +UC_X86_INS_VPMOVSXBW = 1150 +UC_X86_INS_VPMOVSXDQ = 1151 +UC_X86_INS_VPMOVSXWD = 1152 +UC_X86_INS_VPMOVSXWQ = 1153 +UC_X86_INS_VPMOVUSDB = 1154 +UC_X86_INS_VPMOVUSDW = 1155 +UC_X86_INS_VPMOVUSQB = 1156 +UC_X86_INS_VPMOVUSQD = 1157 +UC_X86_INS_VPMOVUSQW = 1158 +UC_X86_INS_VPMOVZXBD = 1159 +UC_X86_INS_VPMOVZXBQ = 1160 +UC_X86_INS_VPMOVZXBW = 1161 +UC_X86_INS_VPMOVZXDQ = 1162 +UC_X86_INS_VPMOVZXWD = 1163 +UC_X86_INS_VPMOVZXWQ = 1164 +UC_X86_INS_VPMULDQ = 1165 +UC_X86_INS_VPMULHRSW = 1166 +UC_X86_INS_VPMULHUW = 1167 +UC_X86_INS_VPMULHW = 1168 +UC_X86_INS_VPMULLD = 1169 +UC_X86_INS_VPMULLQ = 1170 +UC_X86_INS_VPMULLW = 1171 +UC_X86_INS_VPMULUDQ = 1172 +UC_X86_INS_VPORD = 1173 +UC_X86_INS_VPORQ = 1174 +UC_X86_INS_VPOR = 1175 +UC_X86_INS_VPPERM = 1176 +UC_X86_INS_VPROTB = 1177 +UC_X86_INS_VPROTD = 1178 +UC_X86_INS_VPROTQ = 1179 +UC_X86_INS_VPROTW = 1180 +UC_X86_INS_VPSADBW = 1181 +UC_X86_INS_VPSCATTERDD = 1182 +UC_X86_INS_VPSCATTERDQ = 1183 +UC_X86_INS_VPSCATTERQD = 1184 +UC_X86_INS_VPSCATTERQQ = 1185 +UC_X86_INS_VPSHAB = 1186 +UC_X86_INS_VPSHAD = 1187 +UC_X86_INS_VPSHAQ = 1188 +UC_X86_INS_VPSHAW = 1189 +UC_X86_INS_VPSHLB = 1190 +UC_X86_INS_VPSHLD = 1191 +UC_X86_INS_VPSHLQ = 1192 +UC_X86_INS_VPSHLW = 1193 +UC_X86_INS_VPSHUFB = 1194 +UC_X86_INS_VPSHUFD = 1195 +UC_X86_INS_VPSHUFHW = 1196 +UC_X86_INS_VPSHUFLW = 1197 +UC_X86_INS_VPSIGNB = 1198 +UC_X86_INS_VPSIGND = 1199 +UC_X86_INS_VPSIGNW = 1200 +UC_X86_INS_VPSLLDQ = 1201 +UC_X86_INS_VPSLLD = 1202 +UC_X86_INS_VPSLLQ = 1203 +UC_X86_INS_VPSLLVD = 1204 +UC_X86_INS_VPSLLVQ = 1205 +UC_X86_INS_VPSLLW = 1206 +UC_X86_INS_VPSRAD = 1207 +UC_X86_INS_VPSRAQ = 1208 +UC_X86_INS_VPSRAVD = 1209 +UC_X86_INS_VPSRAVQ = 1210 +UC_X86_INS_VPSRAW = 1211 +UC_X86_INS_VPSRLDQ = 1212 +UC_X86_INS_VPSRLD = 1213 +UC_X86_INS_VPSRLQ = 1214 +UC_X86_INS_VPSRLVD = 1215 +UC_X86_INS_VPSRLVQ = 1216 +UC_X86_INS_VPSRLW = 1217 +UC_X86_INS_VPSUBB = 1218 +UC_X86_INS_VPSUBD = 1219 +UC_X86_INS_VPSUBQ = 1220 +UC_X86_INS_VPSUBSB = 1221 +UC_X86_INS_VPSUBSW = 1222 +UC_X86_INS_VPSUBUSB = 1223 +UC_X86_INS_VPSUBUSW = 1224 +UC_X86_INS_VPSUBW = 1225 +UC_X86_INS_VPTESTMD = 1226 +UC_X86_INS_VPTESTMQ = 1227 +UC_X86_INS_VPTESTNMD = 1228 +UC_X86_INS_VPTESTNMQ = 1229 +UC_X86_INS_VPTEST = 1230 +UC_X86_INS_VPUNPCKHBW = 1231 +UC_X86_INS_VPUNPCKHDQ = 1232 +UC_X86_INS_VPUNPCKHQDQ = 1233 +UC_X86_INS_VPUNPCKHWD = 1234 +UC_X86_INS_VPUNPCKLBW = 1235 +UC_X86_INS_VPUNPCKLDQ = 1236 +UC_X86_INS_VPUNPCKLQDQ = 1237 +UC_X86_INS_VPUNPCKLWD = 1238 +UC_X86_INS_VPXORD = 1239 +UC_X86_INS_VPXORQ = 1240 +UC_X86_INS_VPXOR = 1241 +UC_X86_INS_VRCP14PD = 1242 +UC_X86_INS_VRCP14PS = 1243 +UC_X86_INS_VRCP14SD = 1244 +UC_X86_INS_VRCP14SS = 1245 +UC_X86_INS_VRCP28PD = 1246 +UC_X86_INS_VRCP28PS = 1247 +UC_X86_INS_VRCP28SD = 1248 +UC_X86_INS_VRCP28SS = 1249 +UC_X86_INS_VRCPPS = 1250 +UC_X86_INS_VRCPSS = 1251 +UC_X86_INS_VRNDSCALEPD = 1252 +UC_X86_INS_VRNDSCALEPS = 1253 +UC_X86_INS_VRNDSCALESD = 1254 +UC_X86_INS_VRNDSCALESS = 1255 +UC_X86_INS_VROUNDPD = 1256 +UC_X86_INS_VROUNDPS = 1257 +UC_X86_INS_VROUNDSD = 1258 +UC_X86_INS_VROUNDSS = 1259 +UC_X86_INS_VRSQRT14PD = 1260 +UC_X86_INS_VRSQRT14PS = 1261 +UC_X86_INS_VRSQRT14SD = 1262 +UC_X86_INS_VRSQRT14SS = 1263 +UC_X86_INS_VRSQRT28PD = 1264 +UC_X86_INS_VRSQRT28PS = 1265 +UC_X86_INS_VRSQRT28SD = 1266 +UC_X86_INS_VRSQRT28SS = 1267 +UC_X86_INS_VRSQRTPS = 1268 +UC_X86_INS_VRSQRTSS = 1269 +UC_X86_INS_VSCATTERDPD = 1270 +UC_X86_INS_VSCATTERDPS = 1271 +UC_X86_INS_VSCATTERPF0DPD = 1272 +UC_X86_INS_VSCATTERPF0DPS = 1273 +UC_X86_INS_VSCATTERPF0QPD = 1274 +UC_X86_INS_VSCATTERPF0QPS = 1275 +UC_X86_INS_VSCATTERPF1DPD = 1276 +UC_X86_INS_VSCATTERPF1DPS = 1277 +UC_X86_INS_VSCATTERPF1QPD = 1278 +UC_X86_INS_VSCATTERPF1QPS = 1279 +UC_X86_INS_VSCATTERQPD = 1280 +UC_X86_INS_VSCATTERQPS = 1281 +UC_X86_INS_VSHUFPD = 1282 +UC_X86_INS_VSHUFPS = 1283 +UC_X86_INS_VSQRTPD = 1284 +UC_X86_INS_VSQRTPS = 1285 +UC_X86_INS_VSQRTSD = 1286 +UC_X86_INS_VSQRTSS = 1287 +UC_X86_INS_VSTMXCSR = 1288 +UC_X86_INS_VSUBPD = 1289 +UC_X86_INS_VSUBPS = 1290 +UC_X86_INS_VSUBSD = 1291 +UC_X86_INS_VSUBSS = 1292 +UC_X86_INS_VTESTPD = 1293 +UC_X86_INS_VTESTPS = 1294 +UC_X86_INS_VUNPCKHPD = 1295 +UC_X86_INS_VUNPCKHPS = 1296 +UC_X86_INS_VUNPCKLPD = 1297 +UC_X86_INS_VUNPCKLPS = 1298 +UC_X86_INS_VZEROALL = 1299 +UC_X86_INS_VZEROUPPER = 1300 +UC_X86_INS_WAIT = 1301 +UC_X86_INS_WBINVD = 1302 +UC_X86_INS_WRFSBASE = 1303 +UC_X86_INS_WRGSBASE = 1304 +UC_X86_INS_WRMSR = 1305 +UC_X86_INS_XABORT = 1306 +UC_X86_INS_XACQUIRE = 1307 +UC_X86_INS_XBEGIN = 1308 +UC_X86_INS_XCHG = 1309 +UC_X86_INS_XCRYPTCBC = 1310 +UC_X86_INS_XCRYPTCFB = 1311 +UC_X86_INS_XCRYPTCTR = 1312 +UC_X86_INS_XCRYPTECB = 1313 +UC_X86_INS_XCRYPTOFB = 1314 +UC_X86_INS_XEND = 1315 +UC_X86_INS_XGETBV = 1316 +UC_X86_INS_XLATB = 1317 +UC_X86_INS_XRELEASE = 1318 +UC_X86_INS_XRSTOR = 1319 +UC_X86_INS_XRSTOR64 = 1320 +UC_X86_INS_XRSTORS = 1321 +UC_X86_INS_XRSTORS64 = 1322 +UC_X86_INS_XSAVE = 1323 +UC_X86_INS_XSAVE64 = 1324 +UC_X86_INS_XSAVEC = 1325 +UC_X86_INS_XSAVEC64 = 1326 +UC_X86_INS_XSAVEOPT = 1327 +UC_X86_INS_XSAVEOPT64 = 1328 +UC_X86_INS_XSAVES = 1329 +UC_X86_INS_XSAVES64 = 1330 +UC_X86_INS_XSETBV = 1331 +UC_X86_INS_XSHA1 = 1332 +UC_X86_INS_XSHA256 = 1333 +UC_X86_INS_XSTORE = 1334 +UC_X86_INS_XTEST = 1335 +UC_X86_INS_FDISI8087_NOP = 1336 +UC_X86_INS_FENI8087_NOP = 1337 +UC_X86_INS_ENDING = 1338 diff --git a/bindings/python/bindings-test/lib64 b/bindings/python/bindings-test/lib64 new file mode 120000 index 0000000000..7951405f85 --- /dev/null +++ b/bindings/python/bindings-test/lib64 @@ -0,0 +1 @@ +lib \ No newline at end of file diff --git a/bindings/python/bindings-test/pyvenv.cfg b/bindings/python/bindings-test/pyvenv.cfg new file mode 100644 index 0000000000..5c102c1f34 --- /dev/null +++ b/bindings/python/bindings-test/pyvenv.cfg @@ -0,0 +1,3 @@ +home = /usr/bin +include-system-site-packages = false +version = 3.9.2 diff --git a/bindings/python/bindings-test/share/python-wheels/CacheControl-0.12.6-py2.py3-none-any.whl b/bindings/python/bindings-test/share/python-wheels/CacheControl-0.12.6-py2.py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..6ef9423855100871b18e22e764ce5dfdb1924328 GIT binary patch literal 23441 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 P81^iH)q3Z2{(Sd;smBbH literal 0 HcmV?d00001 diff --git a/bindings/python/bindings-test/share/python-wheels/appdirs-1.4.4-py2.py3-none-any.whl b/bindings/python/bindings-test/share/python-wheels/appdirs-1.4.4-py2.py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..24590035eb25f9084cb5a610893678047e92c71f GIT binary patch literal 14285 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 S>m9`3g8bKi7qk8A?tcLgMbSL~ literal 0 HcmV?d00001 diff --git a/bindings/python/bindings-test/share/python-wheels/certifi-2020.6.20-py2.py3-none-any.whl b/bindings/python/bindings-test/share/python-wheels/certifi-2020.6.20-py2.py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..9f6ebc60c024b4bac368f060c14dfa42a2c359f8 GIT binary patch literal 161344 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 MXp;i29~s2|0p&<|egFUf literal 0 HcmV?d00001 diff --git a/bindings/python/bindings-test/share/python-wheels/chardet-4.0.0-py2.py3-none-any.whl b/bindings/python/bindings-test/share/python-wheels/chardet-4.0.0-py2.py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..c120243cd78f3a0b447755ef84bfca8ad6e4034c GIT binary patch literal 174749 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%0AM1q|U;O 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 J1u!_l{sRpnJRkr7 literal 0 HcmV?d00001 diff --git a/bindings/python/bindings-test/share/python-wheels/colorama-0.4.4-py2.py3-none-any.whl b/bindings/python/bindings-test/share/python-wheels/colorama-0.4.4-py2.py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..d38d3a004bc254ddd8ab5fbb6bcc1d9fbed1bf55 GIT binary patch literal 20722 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 literal 0 HcmV?d00001 diff --git a/bindings/python/bindings-test/share/python-wheels/distlib-0.3.1-py2.py3-none-any.whl b/bindings/python/bindings-test/share/python-wheels/distlib-0.3.1-py2.py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..6503b96b49bad7c5a887a2daf53cc5913426dc54 GIT binary patch literal 147633 zcmaI7Q

wv#wj}vTfU4wr$(CZFkwWZR;zWUAApor`N^Vd;M$du`co==S7al5l=?u zd?P|Z8Wao_2nYxg$i~ZDWn~kHz5xjc$bY}k+krV=X`<@p zOUmAqn7uy^<899dO91yutbx(bXkctO7(l151>%GI1@@8l8bRA=zT6X!q&QqX@#=99 zb4bA8-a1>Q?^{al|Gq25I@fZ2u5f0feEBU#8G&Z5ZAzyR2<` z;^>sQlt9$6mlCk7P;bC$$=YDJ&Acqh$Xel5+MHdf{n4}USU#oqw$^CZ#M&{BYltpj z+6eY@Lemtd$N;dG!mn9g9(Kf>(G)dJj$B0}6!y;6KK|wOkN%3MpbBQkg*pvo4_MoN=Q$7xMcPG&(O-+ot((nxt)GW@|^US4Wg2sM@f7u?XcnHBgSFD&q}- z=8*Blb?PD|#J*&U^?XdY3b(p^rDN#c*qAqZ!uFHZOrmdOlN$S<*$z3?dHaVKpDvpw znN>L!OqPzg?;n48SdQ7rYPGbr4et`Dn<&NN0Qe2ok|uWhCG>H196>J*US^!8}` zIJ1%Err2o@>H%`C*3#Ox(tGI9RUe18l4{n1FTz_n242FZAkjDls}FfaHpY27mki6a zt6UgnPeDKG*9!lZJcYPOv+fmVJf~cHEZK?=WOq!soU7P-;duEN500?N(e@lbsbi1U zBtM3S6_ zY%=+$02)dei@SKHlh936XK|33yT8}bL2;_9xm->AThVd0j#N$ZKu~VB0m@}2LT-Du zdS&clNdht>=H89LsGYtkyqGxfVYxo?1>8@iHxbCd=rb+~~Dg+^nltAfEbNp5MG(vAy&|T8|+%rJPtxk80HB9=!1xBIKM7nmD_vtKq4C zKAl~~NY(dqa-CMAq^wLPm!Huh?HfA``6XRYTb63ZYWqF&#ndCk#u~*LnYTvO`n4l7 zHf!%(*bGf$rqw7G5K)xe%5B(;^K|KYrPf*8z685}htx1X3Fe%f)H*j*!@uLKR8Lcu zMRQxb$jtI$myWa^D-RN*ClVC(+fqYG`TI0tt`3Zz5Wr5_0iCFPC;z-h>H8OWMAkJa zvj<2NDmcGsqDYP~tHYjw`d}gJ<#1*v0SaEjf$L!k&7)RsQlV6Q^d6t5br;yBm9n= zjDzuB#Ix|Y8QfiM0V;4+o_R@Z)4tGi`m%l0Dq#=a9n^+Ar-;-Gs$Gl~W}6B^f-Gxs z^t;V)(Qd4Obe*_`;+orHw1#}`Z2)$DMNIepgHX@L)m>*-&FP77r>n79ob%lv;MAe} zRpKk-Ab49r{}0;c^0CKodQ7e9OV3i|=X!|=AAA2}1EQfdScb9m6>SBhpONg@Vap?Q zvqRB$gj7Q!m3j<)v1?8jdUQ@l-nX)wbe#qCtDsJu_uK?t36ZO}953tR*(h|Q<26G} zSR|0niQ+s(*R_FO4cAV;7;MZE^i4Zuh%4!Mkjh$de16t@y7!}1;_tL}9Tqn2<0W{P z)hJ?<*k%i4*8abcJM(|(wM5DnMPWX3#d_U~+Cw^bucFXBVo6~Dh<%+vN+nH$@GZ)Z zpr4+5FdIiE-a8^>2HCnXV6M$S znQZ}!N-PFXXir*_OE63p0r}Csm|pWVkWTu;f$>*xRKVq_-klHsuBHOWXJyha+o;nT z2Y*}=Cef!%S29lEB$36Nm9SKeO3V6nNmYpe1>>`Mh!AgMN+d|&Ks;GO5Dpm))?qEf zwPh=*n8n0vxIyT4Cpw~F^zZ{w@bLmKFiBLR$a^^RoxY!~pAJ|mB%_L2X{{03YpQF7 zy^Ugqt%xHf1ewG0OyE%DC*i`%75AhEsOE_T*%cszBmhg)Tb#e#>d%Ee!n2D1 z-ce&`DqyS2*HGhF?8GkR^R?Le!kVfKM|U)(+aq4mcu^0be`!!K5H%c-qKX4e_%xu{ z+kCht$Va*+>!l<6nd^@j>KE*8Bbeq165LhTF9mIeP$|bdlB_6}yc>y}Bo}B=W3QD$2~HRF2{Gst0{^`$L`yN5eFn<6cq)RK zSN~)Xnm`cG7_hDdEtvcuF)sL%=p>&lgxr6@O|exXkKK!2o0NzpOzmcQi+$KDDe46= zTp|tG`1cRo(23dvdIyM#P8u5#v@6P#pq!`au;bqriqocTe*ejqRZ`kQ@~0@bVX!R9 z=2M|p!yGw3ERZCHYHOyLn(GOEC3b<%5?y3+OOzZVg+jihzgD{hQwI1KcR`gckQ>#7 z-VGU<8aU9+3d|ljV+A`mqz~o-sADW>v7{s{VYLS_9^)g=-uL$wh&^rMC~neu1#Qvy z68Z?x9^+ukj}T9S1D@&PX2-rVDv)!rQ#mX-baoW(E9T0opq3q(NM+FHEvwe7Y`30# zZS#+dHWtXs;6b&dz(dwrT9-BOA0vvEMA=it!d^1<6SWwt7(FYbpfyf+#1aTel$+QT zHnQ!!_o>@$Xc1|SvJ|&HA?X&dIEdfN4V9N3Rap}i8|g{%=4vA$8|kM|8F)^~nP&)u z%ufBL?8_38taH40C0|uB+f^GU_MR^%J7sCJhevt6PtY3UC`i^@a@n~qBX?$}iMWG? zJ&2$E{aPWJc@)qYhb%gfF(@$~Wqo2hB~>!7YCTq3(bX$i6b0-C3}ANjq-LY9K&dXK z5}mAyxKugPe2H$E0&TOLz~cCf`{5zu~YP#rWG(7s`Cc1Uc?ylxv^o^>)k3d zisnkz>WNw6!IxyX`1Ho=&yJ|l@=ZAO00>pHxwtbYTAgP zOG@VbETGiw>h$Y!Uw2qZY(<8iJW_M=hurxs*P+As`YFm}cEU3k5LZo;FLby}-dFX7f938&soODzuGJ1|6-j zu3F_v0(X*@n6PnMIv%A#+!P4L%O`5yGB8c1dV?q!MPCTu=0&kp0=T@xgz=FVC=IyinfP>a|E)@&tlmbEM_61Fj%BY^&#FgC#5)U1tSj)No~c@kd5Sq zY=lz)*r*nTJ;z2CB1j~3=O>nSKFBv+u^$n8X;+y8N^0(H#89g9X|qqk-y)VDp7}(6iQ|Sr9rE^#^IBDkQE; zXnSMNhKQ5Iv-J9ZjgEWa1E%Z{{{Gr)e@x6CKNe)ShGH4(19>i3eX+9xN+ptq@;Ipl5pxJXFcTEK{vWTTNO#q#O*!rEwO8jS4U_R;IrrqXwN3@$Je3B z<`{DkIj%_fC0zLKXfEBJ1MR~@R10rD#?FN7nunP>pGt@#tt{kBUlSilc37svML}^$ zdAk*e#IR}x6p}XZewee+%SNpbmrTwpLRU2sD13zg~`}joss#A zQ{MHj!l~eeW0C>Wm-8ZC&nCI}GoyR0{%Z;dGMkAc$w35nYS1|&_1p(zr z1XkSr6mTM;>ip`9L6@GS?f?~}(a$G)fan?OWX`q!Q&n9u?m2A2n*k>vSHJhejLnl- z@hdX4oasqpjpEp$+#bgZ-LJYF*i35SDywKfLxQ+*p$K1c?zqU01T8uIzMs*0zy8a^ zmlWmk=kjxjVgl`ugfs#$zNU&Cvt5o)RQwS@L2$rTDZXNs7VAkHUC5r0Sploud|G#u zBrfZlHtBC#)OsuUEh>`mV?b>s)z+v&G3>wEOL;?mC4uO0moSgGaOZS`3GT~%=#^yk zoj{3~Tj0?{p}iUjO^i`-U`Q z>0=v5x2i4ajWmIB+(@Txdm>x(xQw6YuZ7B5$I{1R!FCWQi~kcFlh(Lv2CY3Qy!g!+ z=N`8(-JR+hYN0bo-L~zt-gi`xA8Bf(-@EN$gm=1%y4BO(2Exz%9>3pr76QKs!T0qb z|JSEE|Ig8^|JQ>(|L4XZg2f*Yh5t}|pbiX00zrU)guwoH#YaX`SX53~^uH7zU1fRu zbwqsSIS@mQ(@Ok`o6P>#rHjoz0w*HaS<#W)oVd$3K++o(N z*tmB2_uEUYLVOlFzMNTnDuItu?fVq zQN*K#UEM9m1~;6|XP;{YZYv1ni!tJ$#>`>OS5`@Uv#6FGI9-<892YeT`E)?^ygI98 zXSC(`5Df%O0(;CDj7i1jq)Yz!LnOq6c zvH3e|ozWvQ{j#VgZNC687Efk@=#1U=Pv3_d&;SA%7aCaChV66V z?1V$#zG3Z4KZk?j7yl{F^E%FCrxJG=M?^AIsh;rUGDc&vT=UkcW^ zcVnrJk(A22xQbS-*b`tD!X>Qs#+uOo{d=l@GALUQN^lv{cqYKK%|6LP3lgIvWMQc`>dpBINE<2h8@jr-GUe6F zM{koXh#nxhB~5D4@Qr0E9{Eq@l6k;OMI=M3lSRBLQIMR71$AcZX;oN#)*)m6dTQUh zGf=n&+dar**g^4HxJz`B(a*lgfCksYBn)T8G%uru=!}4PzSMO41 z7PZU9Maqdshy*kT@Jt+T)!=2Vr27T77Gz%fvs2oRD~>5gUeq$9#D!73X+%eWl(@eY$c4D$HOPIuGWyv;T ziPCN|*>j$0HR3f%263f$9*6#w^!cT|OG4jXmfxRO^U!Zb@^4D&7oSf?!&^qfS;LV> z055iBpC&n0sCGO+FMSlExA*uvDp1eJ3^qJ?{pdw-(sn{UH7*;4O~*b=Ux6$W-#i8y z_o>Q8Lf&K6AU%-2{9g~LXFDcBSQ|17p-=GtjAKsJB?*cD2b}aC{Pj=mRAz- zT^diU)Ik09_w56HN_+>_Dquc5fH0^KCFmGL{T=`For#3nS4Gqe4wF%{!8niOM6CY8 z8T0d*M`K|7f$#j+nG!+SX>T~vE!KAC?ELD!WZC=|38c~@-&rWF5_uG8Le^#&A2pUx z!)o0r0d;lc`i(jw!!0VWu-~(Gp~ERS>Khv`xGqhG>WNl`VB&HU47^zq5vf?=Wme?p zHo_--H6yFIeUYb<@I2%Nj0qEUa;`&!iw&C!I@?_JxE=E53kGrlWAgqsCM^kLU2#(t z-^GGl^MGFul-Cs&Hjqa&zCwWGi%P7URLP0lWws00s6wWnv?toT?~)r0?V$)0st7-i zM=&-laf|J%G@9BqZ+9y%;(iths5mJ za>qkp8#r;T#BUrah@yxU2vIN+O;~M1TkvnR=wo5x&Ea{q=04cr2i4!3#A~lDMva{C zT>N~wxU}tK4iRNWGHBs(DmQWIO~VZ+ zo6dV-v^8F=Cl}rz=FSEs=GO9?lps>7Ho(|2QH2dBdJH}N?0KZ;DUhQ3SNWe1c&F4A zqWQ16o8Ve><*f}ngi*}bB!s1l05tVuAD7Ekj=vL1rlyVG6}rk#Rcmjh2udV=M+MFL#d4{MAK8mSiOTX<6b;(6m9)?`LE zJC{(QPy@eL*$vXZ%?Tqf#$V@b$Pw`k;;9Be^G*K3p6c8*z4{E8>c2o z!RvO`QyZuIggD;+a$nHR!I|k)46ue;*lKz6V0yA+A@%^+UjfK_-VIxVj|n+qzIs0> zZl0Ud?gMK77^Hd6Ss0Is|ERpXj90xD4Af>i23>d&3G!>EzSk~o+Z(6X)#WBG%Uo#f zkti%iY+?Ok&k6M0^N@B6ZWFDIdu`pdtb*@zNn?r2Ma zm2q9cQeB7uPX$Mp7yj1c&k-iAS6d)BcJL8|HKx*Gj)$=RS1YYj9t04b30U z)``NK&4CsoH3-Mtp%3P(fPEFoz&a#nInDtYro0+=>&g(ho|ZYzCTz7b>~vitriV;d z0stFCa~S8f+r3U(j9VW9aPPzQ*XJ!ft6m)$_|V-|!d$xst46s za`ZkO`-t0VTnf$Bl`}E<(@D{eGc+nmNR89dM=(HCDE?*q%gi>%Jbws1G0QOb zM7s<}NhdWuF4L$)NkuJt1T87krbJoJGCw^zE4?H;RTUC}Ng~n?^q-a~Vihnz|Fa14 z-yr*6EOW7U(6=#lGqw4z7o+q)6UN77_Cfx~VqUe%?7IK1Zqa`aP?Z0>7=ymPrJbdV zzCOK!XQ*Po+#n+?;Dm{aacH6HzfNUnBtp%4wQP6P zyClJ_ShQ_fTO03pzHPoy+zK8ODlH^o^Jt-%>eP}w3D}kir1b%JEbTC6#wGJg>~K68 zVbV?dc*@n>INSP%f1pj429zStT;!acmPE0`lg7Om^2>;oBHli&8%c39zP27eNjgr+e&9;?cr zOSprSfIN$0n#xs9#QaPgvDhi&i|`*3ht8psh6p4AXBsCFS3uu--hch4Ye{a*KL`JL z0r{Ut{u^`*`bLJv)(-YgF8|#xWO4m+pub^7Zr&mlXAr>vcM!aUcMz0W9$jJ8%ERzW z-@NpI>1dv!6U+ngx~7D}M;hf}uJ_p4!B%<>K|1N_qQX0-dxLq3YTtli3kkb)snhvb z)B#QEAE%t$)Kpd9g-MMv)C2l!a8FOKk)Zd~o>U#bT3?NIO=*ys9_kdU^h^dYH4SmQ zHXlllUi|^K)TjZN^L;^YsX7EhZMMswpYE=LdKtUtsry2;FVM%MtVk-mO>Fee1@5W$ zd?}(4%CC^KPl5sgVf}X&|IhAiEuD@35$UVkZ@2M}$P1cqu+SX3->tb} zAe_~L;9!{Z1(NcEgkwxO7Rsc;g>A1l6l;nXrK>JTviH2aeB;U*-P!@oDNKL3Eh_YU zBC*&^l{MB%ozERe$JCkIzi#i)pT$G$@$hN_b861w93b^=tf>VjGKmA+!f#!hwCvq4 zf@Z)5RO-D}uL;;+dJ)iv1Q0yhkh<;h~ryb2xLfosj1SiNYHhQ?-*Y!kBwbL z1sKTW5Q9|9C*1HDXT#KR{vq?3EshsIY-ysnw!I)VbB2_x5v}-Wf3_ItOaC&6PPn+n z3_$}%_8O!g2b$ra}3QxCBxOWA3~Dy$KO<`!FpT~b-UN_aPfor0vUtCvw$(*2UD(zuvbUt}dO+H4DQ+s|lUSSAtDt;>N zOHxvCHf;aJfG&UigF7s$>0vtzOjRl@;c@=3w=-_uG{=cww;FN}`B94{HbdcIH^2*f zevktJ?97e;n^RDmhsTt^-keqSHz-8tq0>S^}qtL6J@Om_h6Y3CLg!I zU#iUc&B79wp&Y{bR!PgcIx9Q3b$QE<@7Uv}E45CJo-@wYrw@Mm^yTE_<))*o{L3G% zT&)u^n4O;w&obT|Y^OJby>(NLeOoN;o7OYcRn=>o*y<)w$pLN4pJz`|Y5d*(8LfHR zNh#J3`_l*n+ko${Q3U&Kz$k&?=C}#d+v>6CHvUv8jXDS5cUC31&NGBS7`fTgIoD6f z4x6VT&bw{SirbRfQ-5#N^{enC{k`ew)%dhTbRZd%|Y|A}M+pNkeb##o5-}MT= zdY8@2H(={$VdEc9Y<}$mH6JxOX4wN$B;T~w zPDalCQ%JLx#`;j=B6&3YIE(6{c7C30?!FH;+U&P|J>CGE>;&i69Mr*cb&Z8k1qDH(QNJ848Uk)GP?Et9MOr>LOzbw* zzq+fIubHnK{g!`b$(^-?zfakdg?xId_~w3|ZwEgBx<{IK&}xZ0#FPyD8!0$l zlXjOuN4Wt&obRr{i7?1ZkEJVaS?o16-h&QK>iE8xfqkN#PI)zUpV+{=r;7Gg^&<9| zd_@ExR`<4=OK!kgASe@NPM%JU)QzRTMf~p@Ka*EfJDSLDPNUmg5J+4M^3q0+1JHXX z??-B9bbx-^Q-zsfzLt^%(KBgBa z%zv(-h!F(o18m^re02`lmw|xlTfhzTcVP*DG=^~Y0=feTd3=vujMr>|XtJ}2|Dfpk z9_OCmY_Hlq&@NImh0L*EW9SeAY95x!8J$MJ=&^UfjFJ#t8AZZ5MG;cdY?pLqr$?E7 zvUA6jRy0AW)hYJtkIgC>OPPCnjC;SATCdKPwsg33R$@|ly&m{h3-D1Lk)ux^3K1c? z85+@L#U7@rYp>43f`D|!cQebCWI_cLk(C%pyHuAoUV=UJMeNv^Pfn2FUuJ}?%$hWU z-akX9{g7ay!o>(7-|#+!P4eHvJF;|mt^WNfy@G<#m9vZRbdWuad0;Xh<29NqXLhq- zyr163(%EPs3KODlX*N<3v>KOzUsbKCGUAZ+0AO%O0-+pT#{ieZjWd&>2QmASeD2SGkvqcMwIv>j_AX4Cj`W;ltK|2=maiM zM4J8f_w6z0$V&k6>rRgqaDMVg=1#VAXTm$xsLEW_RE`=N=YA+(M)fl(G9^M&G8(Ef zpv88*`_RKU4UaK)+{cU(6ckU?w_`9i|!WQbSHC=bCe% zfX%yd_L{7#j9%jXz@PQp*tu`<#pwGG=j#ybPKwFh!C!!Xs8mAfk0JS34Y}sHj6mRE z1u#hfQE?AifXxcd$IDD={Hd@{FJ0x)K`AdPs_b9FOu>u%-V)Ki-GENit;{XEqUs`p zsyMz;`LRTD1Clr`hEsPLRt8^Kh;xdjIcu8yT>7vF<&Tb*Bd!DxA!v#I^4dP=ZauaX=RkQ<0Au6SJ*1I<99=RjeUSaf~+A&`M&ux#m; zprzw<;09hW$qKRrLHS(4bkIHlBOs;Vk_?nz!AP2-4ul$tMq}ng@vYai4K|u+ZxgqJ zxWCpKU-mOm=7Z77YXzS02?!J;PAR#fwBJU2)SMihEOda*` zH?hG2R|Eqg)K1)Ts7;f5&xgp=Cqg8k-zKO=y@s^*^NeX6;Azp}yb;nSLK1!`HOLIaG@#7N?I@ki>> zHsG6F*)(Y?_U2k`1SzBxxXpNYM$4$nCBD#3&r{fgoYkuhsFc3)&!nT)p3Az&O@jff zInKv|sr%GnL|GDu9;_+toLVNWl&}cp!*iz^0&O`|3X!X|^{h;BKQyh*JfFU?I7nKa z;!an#A&@-*s%zYHE#>rs!@9=I`K0w^?V_FsWW8u00S|?^w2d}h)-YL7WXQ?*qgVLt z$Jo-$wH(bZUaXA=3FADCcd)8x&^gFm6_G80ltQXxm#!N6^h3)EqS$R}wh9*NuZ9PI zEtDQUK)E_$WfpXZ(bhpkjOKa>jFSTmzc6*(w$EzbvZC% z@cJPI>^GAvQv8@S2Gm@Z%gmo5OP_=o#lzh10~S{$oYwBf1P2g911!>!HQh)xMn?(V znxVaxEM5&*jt`BT{^W<$FcBJ#Ig^<{+Gue7?Nvtm#rilSezZR_gcs$DFNi`Wks&Zs zjTFKG!+k6Vq9%00RQEfan{f+V+Y zgm_1=bH(@>W$@0Z0NST_xuWl4b0Ie!2OJn|1Y4o3{yxF=i{#4CK1)%AU?5Pro(+)+ zWzZCfmgW{Xp94jlRYT@?w+bRc;MlQL&L3zsRFkI%>oncL<6&5Xje-ImAL;&MHVk=s z2u`nF5B|(*n0A|9aR=j%Usya-nUZ>lSb+(Pv>HDeF+hiu)Qu2s>8_>Y(gDzR7#O+Y z!QPD{f5yGytsBw6-NB=1;7sd1zyly?K=xOXF^u{Cc*;!0>ln}9Y0>g0C8x~Zk}jbv z^ntQaf#C1DPehJ5XSVzrZX0dPT*<7)6sU=^_;;6j^jLW@qyoQyZvy7 zj}(ieAF>n$umK)(6R)##4@Ug|%ol@5YB)G7x_u0Y^_w~O#T@@0r^Rbj%n<58hN^IY z@jrxRlQPu7=Ag|KaR~N`aOIBo3C7+^a-pcP`E#&D03T*;1}cj0<@Cv;z?KGXNToW2 z5Vx*S*>_6sJhnOYOC(Pv8UlvNKUx?!bjeXpZ^t%XIMLcvJ z^QU7>zq_e{_k<1p_ZWI&kpRq;tLl~o&kjwv2vMns0}{LVfV`2p1O!i$IFw_hV|F;V z(1@%$sxeVc9pe_#f+R3*!aubem~8>uV`mZ;3aXC>ifk&U(dXw~m&vJ{cv(^XQd7bl z2b7F3!ux zl+VODt~E;bAA3T$ez791ohDgZK@!eeIwqd2P}}ttbTetd1*+3bZNC6Hja`#)uq|Wu zggKnmtQ`VnTFtV?&}k8UK>#=#F_#S-Ar{eZ+cPRB$jF zg1;)JWa3()E&F~-pRnS*(%@~GTX0xV6#*@ z38F8Qh4D1VkdYh{h`EDipt5MH+#-lt3>ZF8&B=ymUI_9RyxS(WZ;RV`9*(6Jy~MNcXNNBisTWeuI=;!qwDHGF}p1&jQhLqqPc6+`_*7zsQzM0Ao99$qtv zcMrsM*-7od80pfFTc0 zWg!>CQ8)`T<0!bV_~#Ecq58YPs6@2vPJFoNM3!%xECQxR%nv z+v%#KQTci-oJMAbX4ImZyeZurJ^z!c>o~Oy$)b*?f}q(gPKQpa-zYr@Slg6Iy}eiqLG}}oIYEI_zCTkgG{nwX zH(rN=PdpcqVQ}XN*E1Ze_=B<@Us=ZDW6*MWUs$rhxY_Tk>l?IdO=m8ET;$OXmXaSz zLAj?XvvS?F>@5Aq5oUGq>Spm1y*+n;gK+J)kG{(~D-hTfa2D^-d5tS~pk3^_{3BIH z@dm?Gqh#l&)3Fx2N`+P&lF@-Su4i~bWFtR;ay8mm{`)EZZe6_2pVcR6AOG=4IUJz0HS9#XS2+$Z*?c${#bZ7< z5PmF0oXdxnqIDQc6ckZz4FVbU_Fc_$=P2z5q6e zKwO_94JsXzV>iFUOA;WI(N>$#5nGDyyN6`t&2i`&l=BOcZbrlbSiwYN^C%7r6K8X~ zITIu@h+HNEwh;=1KIS3E_W@wwt2EWpMzB{lgTjYE6GhCY8vh_cP)*E?A-YwbP<#*r z>Krj25g_plN@gX!U*CBC)P?)gS$)^|AX%0dv3=Lgw;`l2IYqFMhvxNxV>8YG&15AS z8%p0*l#gCaJbr9U%r75LaZi}z;x(@v5C2J7g%hHaFP~dhLL3rCwfJ{U-UGCIA(r7S zV^frrnILR5n0lN3fm_=Gw{>q%P6!2g0ycKXL1uLIK=-!gr|eCQu9JJ_y1^$E8JCnf zkj*4uYi9!BR4R%%Yc>(b+Z}=jcYG0~e(Yp`^@9+!#`V9Vn)j}*Y$i5{pwA7ZvcGAD zi(>o~7$~uUJoLUjRQwFQvxh&F131LTX+}LpPM~rb+>DT#3fJ`Zwf%w3ZRi7wP^!rF zR2JIjo{2}l5PG1}vV(n$+%!T%Gv?(FaCgk)TpxOD`4|-CXr{Vo z`H(BA8rR4@565fRZ%Tr=qND!qU?Tsr?it;VBL(N|02sUeV1OZ>!0%qPMcwH4ye~M9 znS(^%%FD67W(zkLW_0;PdD#ix+coWv*p5gbG4cSMokzWc%4g14kr*-+V`ytzsHKXt z2GJ=E%NdLku?RR1t{gS5A(?Fa{anF5efJlBqK2ptbH`V;!lyMB(LO4NAKV$fZ{Hah zFzU9uKOP$TXbBFU5_EPaP{gd0>*t znLwfX;|34MPx01|dF#zjo9mZ2nnFNCFE~TU3}FTAskrFuSxD^0k>2>?>p>2uC-EmA zd$Za{Q{Y-fZ_3DzKv%8n;XHcYSu`EE?d2c8xnRdlI6gYpcI=GVYxrSXv!sVRLwFqg9B>b%yfd zrA^R=VY3ROB8)1lv!As^h;c;d%LXu4+Tv~AAux9`HcK_GED=6@M9CtTSjo*@xNxhR zn{m$FAg!{9vRQa)u{O^Vu4OdB;5i2|NCk~%YSP2SfryJilnJRvHe|oCBKl;u0a_6y z%oZ5QG`hfMxv*UCcU81B;eKSk$6!^F@YX@O8ATb6lsfLXEFIQakI=}YqqMZQVr^QB zhOF**1jEbS;{vYgZPxIo{Rn+0Lm7%k0|{AF2s5+GZfmwgf?YEcbcub11A#5Id%@QF zkhhv)_(KqHezWcPlO2*AD7}&MrP^FBhSyMZ2sRx@1u%{SBVm7VM8~y?~$}afb>(E zrdY;ISNbXfVn{jhMnojew_AHf>H*%nL6*Y<1pB<+*8~3KP}GvZ3-ed<9>NW zoL!f0j>NgHHSDxyiA6nVdqK?PXJt$KVF8HeqLKZ!&4#&##q=9s$3dX2MYQfAREH=3 zy*W{m$0+5EaPQGoG)*F%!0X#PyT{s0*p;q;xbigGcwj_J@Uh^T0FL{s7%YM7Crfs1 z9!vkBh-8-eCk__RMVZn$TDFKg8UW%v$$|ST(QsQ=`AJ+_Q~UfU0vRijZf1g2USuyb z_tNwE9md}ni-WuDcY1st%B|y)1DmW`HT|s=dwtK#!xE9}_ik^2d^I#My6K*Mc>Rc) z{o&;Hsy`GrLCx~p{IIne!i#2euIHHi(*ScK`!Wy6;u9mMr!fkc#gixG>5{2Ih zN%T(y@0sIgX~g;?b%WU#Riwk|@mb5XHrU+1c-zLafB~RsyBIkpBLbhpgwJH_tH#7Y9b%zUq~;@J~40wyT+{GHUDsW)pZF4~P?8D-Mx zmeih4wFK(QBnE%GdA{%f{*y8bE|n{zBef2bypN|)0{=n_KJ(|@==(n~X=LgpY za8lLL&O5LE@0H>7({Kb$-#F2TRb)#qhX66C_0>Qv6R=pqY-rcjF}z00>=UNZ2Ed7j zSS%Goxi@o3kIpOPk1+ho1{LQzx0lkH90t~^BP0TkIvC?CCy?`HbOQOdnkXU!E%vzp_CREHQhV@;&2!Viy=YT*qJB03-2ji^!gyGVY(&$}`bf zz8NQWh+n?M&*$o@55dqqp2}SqD2`WzGhtM{*an#P;1E{<$ut99Oqpq@u-7GN%#>#7 z8#cv431^e@CXHsyWcoie36wtEz|Yx&>xif{gX~CSBjRs;m3RyjVF(9Wu63^*S~WNx z-FkSfWnYF^sTll9kz}Hn$;SLroNN;*(~l<9zB)ydc_0mSZE_2^`4qvB2H2`VM{1U61hbnN`V<$X&QUYlsz zlZ^$QWkP)GRpaESqqFPtOlpi*4deqdTD)Qp&I*Kgnl;p>39L91*C2Q!dH6sCE&e$y zmXh_8zWjsZp>3=wr@D%JU+L7LFXXdPcxi^!JkG^+2{N*>I~{Zf@QXP!IFTeDjxQtJ z%yOXbayri|Nj1@Yny}(tNk!}C_tSt1r{5vJhOb25Tom9cWX>44fC#FFJQfIO+<;Gn zYN4aWSUIVlM}+lJ{0u*bol?}px z6Aar~aRoI$vM*bz2QeII2Z>G&KV?%w??Cf8ZGj`|vZz4B5b~r@__JW!$u@`C`io+s zr~?DL7^?T{$OoDkvD}TsUjzL|V~@oFNi?pP^I6i!^7b73cb?mN?k<( z*T8!P1O++c&3^PZT_Nt0>e_kGiThs2%VK8gKg(#Tt^hXkpxn*@8PvNIf8{0GxO))i zt{re~!CE zLlk_i5e5inerW-2*D8Q|CFYZ0+Fc~+6}eRI`^`Ved)wq1oQ43aZMGDz^m6VS<3Ex4 z_Pi*n_*eUQZ1Eo@kpE9)96YypZM`?x5_i66>Xq)}rAWCPb@XRyHbxtAmRCrhjXNx# zZR&(19UJHXD;uActo?O=hB2?aOS+-?TX*KA2aFku5c^MJR^i_A&bq)!x`I;JO# zetX5pFTY#+wx*f6d%AScruVWN;4tXf)i02lkGc;t$*ywfqOu##tZg*Q92+8)NCE|q zWH-70@rEE6fe>`mu4l|>0u&*XQT(y@TfTU5GiRm;qfz=bj}jo!7*0Q# z-Pb^=S^Opy6!vXxJx4A505W&PT;N}_)Rui=zb{u!ckl8mMi3ocx-{3^kT33@=n>Y~ zr$_D3eZv}-56waZs1qu;?q84fEVP@1EQDw7vDyyrxwnk3Cgo*=wRMst=4cYshM_sj zMAzZ4$a4lQMHJ<+Wc&Vh{gMaX@pajNPd9WO2Z*E4KnttCe0`DyUkkyIVW74hZ&6c* z&Vx=LXF8ox>miL@aLJN-RY(~|J&JRW0SXfRtH@1P+z-~+R&^{8KPSTkkD(jCmFgcy z7B=_+Ou;fJ)W*Ihgjc$}6j6?tj!ON`f+wQPdDW`ddKsrvV2kSc znp#hi7WKm#MH2r(kaLliIVc(}wHHw`2l=eKO~ct0J_lhqy9zHko)(F;$t1`{7Dt8e z1>TUo8~{^Sqb3pxbe3QICYV9p6;Q*CM!>RjfdBX&$Ppx9HEizvWOvsGwWe9rb&ef*NX&i8Za@g)hOtm?)XW<(%X2-$XtGa{qn(P(2$w5(a84 z6AlQ?FML)^L&1Mv4*-mH4w~ACW`O_|NO)rE?%*W6F?Bo8>g40@$>ikV_#Jf3c`!>? z8S+lkpmw1A(1W>lVr$Cr{ zsvcE12T{RS!ssfsWu>s}F(4EGZ?!FI8UvPoqOhVhuYgB$M|Z-p@QL{x7Lj6pQ2%dCJ!1+XGVe+z~m7sbisfQq6CyaEDRMo@-4Eb9P8TK)&bP3s-f<%c{D40>)_Rh zfkEm-ggFP^m;v>I0XAUvtWa|~bBL!6V4e*hnY2c^9`ZXNI{8jP{Q_{Ixz=o@P1LN- z3xkq#62HIwuC+BbA!#8$SfbkKW^EES4OzpX_qEt=ufMW)l<*^zT9&*f2xXC>4U`m3 z6HLOa*(WOv0sv_BT{006b2GITSco>u?uKcA;Mxyivte~1Ot zb}5n}dg6<)jCC+X6RGV^ssdQiv3q>Hzk9md8!*_;-v9PYa?y)-l8SCL z1a3#qY9p{aILR`o(^D%`5hxu>BgE%neiJ1I@{1hSbd%;+umW2o=yP~`vittv*Q1l4 zdlto416y{odvW(I(7j04LSY|ao$sP)9_Du#4A5A>)2H*`>Ac6DwtzH0E<2{)vo`uJ zv~6_VXzHMc<^ma#)ZtQJhfAe2+94vZj1_YDSjgV1-Xl@mf$L75wM2E7OmJ8O>;x&9 zFiOy(K#=QF&}LyIb%VqYtO>G4!h`@w+Z3Dy0}e7gy8YxYfY(k269?#X3@4T=o`7PF z3LQ5rWlvF{H=6=vmhc+`o^8GK*$KY{BM;0V&gU?a1Pv?{(bfxPK8CwQw2I;o{E|CZ z7mOGx0A!w}@my^O#g=GO#6Zq9(YF62ziqh4J^w5m{bP6Z-xuoN7o-22Ts$9~i)TCV ze?NFOP$W*_5diek0p$G2x$mFBXn=8{{v|-q2ItN38_w~+bqxRAVBE(j^*OZFzml}5 zAJ7vrTxEtLNHd-o1jiGZXxZ^7(E{?0V=PUkbz{(w}}Lq7Wo<5$Kh2c$rKgkuS) zRl~~!HbX70)%TEX*d8XfUTaLOmOlIZ9MKkh>Nw95G7ldiDXrv1b6rsaW?ws!r7{^GQCKvR8TR^t#W50S zEZ*7Cz__{oCyqdxdm#-#T1(H<%B{!c@H|UnnVdu4iaMF$i8vC4uQpMc_m#r`SoI?o z37H4Zug9brqXQ*cOb3{11W}A43!F0rVu~H+AfT^9kW+{gffPK$i7$ya9wYG<4 zl&|jTb_XhAZ$)cXI#{iwaUgh@1Gh`_7v+a3Ywgx1zpbPLUNnTN{v@V!XcOxKlgm0{ z+iXM+0GB&=LZ%+zJHcoWFTNsf7e8n?YD2o%5ezj{!f@CjLtlvhy#&R~azxQNL?bVg z8{PAEdGqzuz=lyF)i83%Hd};oOt}k5y)+ojg!^{Ej0#+=-GfhyPCGVt!F=p!=@;bb z0p`v+?Li!J_GiL^?5p`-_T65|^YaH6fH@)XT!QO5bfrP5Pxszf z(xnQvxO(2d$DJCsxg65AZ4qS6^f=9{KW+S-hRWmD;s)B;z2XX$jUaAwP?rAulMIQU zhnVug!KhCZjC=d)s10g_dqubTaqQV6Y%{PI(#ulR3LR827%j;Ryd54~2COHQ^UjlX zaLU#*<;H*O0Q9941)Jw-hGG~TCjfCnr5_$V=3(3#@`zKu?{xbVFVC3B$~;P84I%@p zPSl}7G^^G2Im4roK>siVME&a_0&56X!td(BGQRc9VO519%D52PG*kKa+RfC~p9soq z4!#S?Kr4=hjmiyomMryn!d1m)=21RTgzm{lBb}k~TWOpjMLRS!S4y@MYBQi$FGiPa`JO1i z2b_9?VG|52K`+s(0w7Tm!9a`|`pwi#RDeAk{X?Gjii$nZ^9-{LXdTG2kYUN=qw(P< zToO`RPzM&`G@R2=BDOdNV_HuyC($Ra!dwmlpl+6oZw74i7ZpC1d#Nh(j!23l%Es2N z#3ky`C;`5U>Y6KwA~`SHM&T9u=uP-J5|0{Ma>n{^DQ1 z(R!}`|BXIM*zVz{p-K&+A9w@y@FmF{Dn-RZh@<3+W4#N=#ajNoH>sn~Mk9PGxEhL} za*nIM<{FOL5LtjUWK(bmZ}}ll4^KQZiT2n!Q>wep%4rNcxqEyl;I}-pCXZ(KhOf2c_;(0jKQ=5&1vjn8Z?7*K z7)5`r(C_mB!{GCIqKpCXt@xr1VEydb1?y9z8wj+opI7N08K}WbNw$`OcNOS659? zhmUZk7xx&T4yAdY0a~lzN0^`YO{Mvg}L$L-r49vQxEj(?B z=(01_NUW}`HlVU5n}z@F9Smjo(UdMy!Qmi5lBTvH)%-GGDbl>)bKj`+)WvWEbNc!+ z!=opcI@+GP&u(vTK~8ZzI`HOh!UO}hpuP%_z8J|iF|3f10#GzcV4L{qf{h(#k$@q* zhpLEh2StWsIV(`RxC&!IVP!$6O;xh}^Jn?<^Jf&6hBc`x3@A2$vK$2tDD&%R&gUeV zZ=yK1&cj`vK&6ajo@3ZkC%A2C-Cf=)m)UaHy6bKnG_A%^J&n6Qrg;v&^ostZ!yv(6 zSCT1H@1HmbYv0t$n&cyK(`XG;sJqr8r-i@GEZ*= zUB!4G&e+iyh}FY{;HsQ0nONaCETb(H3bbg~ijkdhtz_Z|ndp)64OvJSr|A_QXPHT| z3uiT)s)*ORoK~-?!*J2}6^x3?N`;tJmMz1kQG=-r94WFsU{w_w=X0iNDFkUMnfbzf zGRis0nOMTZJ0#O(c+I~xK*6dOAy({3Hzv%vD%6AlVQ#=Ddu^2pI&iv4kyMqL5)@uK zDT^o;8_Hh@oQpa0l(dwFG*zW`bTn2WdmDwUU9j(}OKJ^6{lNhfu>{rQw(lPvoTT%# zH5jjeJ+~fgTy%iK>Xt`r336NA^%46~Uxm=n8wLY@S|*?kqzZ^31T&^p+bGg5+y&n~ z0uR?^8}_Y{pwjM015ImrfH^ggLRz4R;ISxqUL6T;RzxNRW>QnJ(X5ATI>09UfeW0g zhOY*69}v6{TNEf+JY9-MB@I+GGUai4BSj0YUaAs{LqRn!%0ylgphO=@oRUg+npZ}l zyD?U3ov0Y;@qxK8qZW&O4~yFfwwg<`_PoNK2+67|wD#YV_jo)V-Z!D#2VV{ACeyYh z+zr6?vEBU)wDF>Ia*c%+vJsu16pgHKD$*DzTB>~vsuvgp;plfJ9PchyGGD@x!a2T^ zzHTcVL>J$Thz?^vr8lS`hiqPAb-a0eTp-m9N$2w0aFt;I6s*)0UC0L3$;(7qgRs}h z$c!7JUNu0GenXwVpzKG{j47$3voGwSCNbR^qVa}Dqbe}n2ZkS~dpWzAOCLyIR&{xq zty{kKXu5=8KBml9S{Qd5;qbbS-kA)e#r$OlCvfDT`xR{%$BKJ&BNeA2chaA@Wz`lt zAgKUJ9rp>?t`J~p7>{rs1&^r40j8RQ@W9Zu#!=I4&>E#WG+a%nP1|J} zeI59f)j(KcqpE>upv({=J*-se(H#zGpKza@qVZi9H&MWuXb6P`T}V(?Bbvp4=_{4j zae$I~CS+w2;U<7FD@7QEMo`MM=U4X_qyjeTnNT+9LZ{XsV-r|XuBKqp!xVF#!?bfF zPiohM!EWCJDZ@At^eZ!DxEmhFZDjSwD8T}It^QGm&O6Fdq&>4^^90_XxVJZ)8(u5O zJtB;A@%;Se`MJOe8T`k{7(M{NtH_C+{f(U$7tbC6qlGF4CM|;wKV1p+-B?>JXaG=F z2Ph+t=F)e-^?*A*E3lYOVoNmSBKO2BTdEG(MbRIQP7k&vUCZLS5QSrrgQqcr6Q16& z-7KT}MBu_tNYmdqMSLIGf}9x`B2e*$vC6y970xiZEW=B@ycr2)%+4u==72;FB=9`? z#vnf1XrnQ-d$RYFB&Nqyd6AT`sEwjpr=C&L3+S7sX@e8ryXKl7pj1o=`g3JYhrCEDrq5zyR@j+Vmx8v{huY_s)|hT zuZ62-u#Z@nfu2@qr$=sAOjB{pe?bf#1hpG|Fcb)i4ZC>K~WCoW8;!dhftSxVl zDk`?%6qiI&A+kJKa0Nv_j?zbqMO5)7xsVb`% zi|*`z6>xQh$|Y1+;!h^PDJ6{}CgydNSIQj9i`2n$ogoL-Cuq(0b@>BN1*Q-FUPb^N zg(;9*t6RO;uB<>;cO%`Q+$vX6Ox?bhLT?UOM2kq>SLg`lpVkr=y~`+xDTs`w5kM*< zv8+an1m3GyM6*VOBO*m4?J#%WqBgbACkbHMyDXZ?7>dkON+iM3s$PSI%24}cS}|IN zr;qQwDZwO+q)_&1@6j7bxe>;AKjU(#Jn?W=m@k{tbHh?N(xuS)@M!!k}2GKas(Bz${a|=H!;pJioSWhWj_hyY#B2Dhl>3~3KK#fNa_|}I>!DIY#@P2FabjWxy8JU_mc*s-SwUau+k|oD|lz zagjs|J9>*$Vp3nNl~fLBhxHh4gKu1gRofg<{hmBr3FF~72sM^1{ z0QUyHA&Y9jm};~8okh>_W|P8T`%CpUH4_;*47P;G#B(C$Rb=Ejheq=J?-`7QQ;&76 zwRuW^siLE3ucN7>D;ZO)C?>&U#t-`J)2C17?f0hq_BgE|*56LMG9dsVIip;~i>IG_1S> zGfb!F(xG)jh~wOJzB2-&LR;Rthi^*EK@q|J zCIbB7~!qw9e^-@RnhC&#`cAomZJ-F z`lcw5S(Wyd6OYuekv?NMS?Mj;6%y7?Y|5QL)n_f0Nx!@VyJ*T~`_^XfFU@Ut?7G6h zvFoqoT%nbbu>b`PSQW2!1KpB9XLpzOt19Y)P)){+@g(PsoA%z>`8>FIZuq0p55=yk zxX;2tk;|$nlJ)Uw@3I@?*2^{e#xawEE_0TZIS6SkFo)*B$%iuILIyEqZ72p_nOB%I zGoV!U4Iay&M}~E2*Oa6o-g>{gcQpPq**`dbcl6iy2Omx+r@JRV9*`6lyf6S2X&^0Y z%-1OUJH>2CG>zh@xC1hzj1Fn+73LB|NQvTEh;bG>JzPa)1iMUCEs{D15|CKNj?Svg zT;AyW+H!Cg&b9EEEiP4wZOi|t&o-Q0)4fC0S%xLLV0(5h$S#tVXzj)~;hn&IIGVME zzKA5LLEQ<`nNPL;VFN^ct9#Btm#k=u7V5s1a#0p=Y7`(sA4RI|!1l%gxIu)p%*H%F zau?|Y?1YE1G7Qf-qrxM4hrv9ZQ8^KxHvqwkl~d> zfFi>ryyW>Ks(lxz6Y++Kw?3wc*VSuY*7tz?#yr1{K-s9ak7|vyD$kj@Rd+62$%9{Y zfHOoJ8E>x<=dx*fJG3t}8|8K8^~0$D2Qxc&|_3VujO_9>Xf zso;*=sE-TZStRZSp3DZ<;XE@vO7H4v0_e-_?L52sq%6KitRzQmH(5>!lNvE=oNLUz zJX)(RXiUXzua)8TePa`z<0cHK9tLB+f#=L3$@BqbblhujE5OnBV4EWpMGcGu76<~FQT?EnxufDPYllqx=j;19!B$l|zF?FZ z1_Q@@=w*Tsl^xJ5dD+QxYQ@yme5bVjkd32I!1sfoT~DuA&SqVC=!f>P3#1IEJOV1d z`XzomTtxM2zys2+9)=E30oqr>HQo6~qW$&r)Ku@7tLudT@{VGCpT_S}BE9_-gMC-{ z#$i3FdIzcs{)nBYIobR4W7s56T5p9e9w#oiqnHI#Wb#csvOjXV8R%xEjOBw#BW*R) zD6JvA+8Ch?OYw8O{)O+(so$DY)j2f+rw%|kU~AI^=3+jKbF9K!f!7v6fNwl|_G;t7 zV^imQ&t_2qiXsZfdz8w-uahdc;3ADaJB~mThSGAUz0!5_$H#jevATLN{{0krK5W)} zS>GR7t0d~LAWwU&+9b=#>~L6(c!80eu>OjOPEqKLO{?Zwzq$`Bq(Z%8HY%_Javm0; z@wX4~efnTL`t_?*2Y6;Y0ZTc@R}v`Ujl=YN5=>o;t`UMju6SYZ1I_JwPsKK)JN|@# zVWO$_^Wv$XD1_)yHv^OE0zRfsjI7Ll1cntTTC|$XtV8v_D%8g?q`s=8<~mk^yzOc_ zJMS~FI?Kz)YR4Nx8mD0m+*1i5^B%XlxKcxL-lQ?Xs8{kCC+Ho^KR07sa(QS(^~x~M z{pMW!vmN@cHIQJN&|Xg_O1P<=U;95$O9KQH000080Bm|^R764_LtfmJ(TOIWJ9p z>)zuePU@$QoyT@I>880VCDAfBGO0sSaopYg?swh*;*FG?wCCRQ({5vl1O@|OFqjz( zW;VCK{j;$dRdI2d&Xd97c4PC8f%rUM+!pEVs*1YLd(rOB&Te!Z7iIKnSypi}PUg`M zbNKyVGxd8gO@4%W2lIJE^~$J7%B1+5Oa~jA@ZwEUT&HE3=2;Y_WfWZ{MKZpPW<{J; z$+RCumqn69QGOXsuHs^rz>_MEqBy&a7D-X&8NkTLRh(vNHjCmYngE7aJgTk$UYTE3 zH*t|*jcF8@Wj;w`031#8$?`hMs<=uC296Un->t5oZ0B66b$ZfXG)>|;V3;9XC~>ag zMmK47l`kuR0X?gVbb=83SavdBP7y0rB$}t!DYqzKWJs`uWtsE=2BP1OuJdVniGLI7 z@?trjr{z^Ynx@#ocv->25+6>I4C-O${+bt2nat-5BgM|}C>YrGc^I&{1&(_a$sm^8 z%bTnG8fyTIx`Q|?m&+mp1Yq2#+!X#RbASdo{3DrE{FIuy%;)p`26|0+WYZLTTkg?Z zUqFF)oPSQJmrO=cmRHaV#tUbRF=L95cjZ+Ky-qPLE$4;`3*RfbhYy<3nznI{f_j z;OKQfdU5dj;MF15Jq2ih3KnIg0mi$Z4*4lGe*hR9JijKRvlPgWr7^g|mwq z!n>pML+J0p+0i+m4=`SyokB}E>`)B{_5|ym95N`J5_{^P2>yP1j$d&+34<4h2gd;K z9IKnfaqce*An?)Xa#<~lWHgG>YowHDQKVS~PcvG(8ylTY=PZe*5fw+uyeZNuK_Ul^ z3M5`!$5j*;ldJS|QVy{A#>Qy$8I}@M2eA85=ReMp&nf==4|peUBK*L+ox%5moetE9 zm%tYasDMv3AN_T?vy(hRUfT6|X%DMSfrDWss`ei*;MX3o;^A<6XLoz|aWwpH@6qnw z&MP>7KD&L{FbSe)_}$`j~qloPa`kC+k>3Cx%ccVu ztW9&jIid+F&4)5)OU8q=2bTMQ_>U)uFY*fYV-Ug1X zJew5@V8A!ijpWNDSyY12VNv7-mxBV(V<-Xtp5z&TfE}qEl(*OOH2W06w7*NUX?{~8 znE{^yDta>5iHbNafpMMW6+nc6OmM#=up3f!#4RG>>_^>$N;p52>4z%zYUDRnqx2%` z4j&F5^lLDAJpaUE?y2|HM+65TUdGfqSfX2>mug5v;ILCn6 znDAEJlWM2|+}Wje1?T)=S| z$&Yda$TqPG>Ngvse39Vfs3!yZO@jveR1$6B?=9Rx1!pR-@eRTl3w5}&(q3173#gN~ z$B299*PTuj`fL()WJ}y-VU@;{Dk(Oe9iKk``TXebhfwwLa5qqCo@BG?3U}T`kxvrX z$w34f&-2Nra^vjq`RUmUsa}J+51huUT_lsdn3fx_PTr1S?>Tx-$Rb}UqR7B`6FAZ= zokX*2Nn?W&5{MtLNglm-EB^Xr2RaYj>e240|Y|QE)UA#Pcj{qJ&URfRIX;CDX(2+}Ev(pV2=vNoN zyg8(vho%>W)!A~6yBXlUabT7Y(1TO__~d6@dKiRGtgj18BAq5`HTO2oe|fFz?FQ?W z!ej`*#`B-fboqzD@|rNCa*<3>^qa!IJ4t{}YZM;^s}f1N)m|LcEj|ubo2EdRRbJd~ zygYh&stZ5y3!_qTnY%5ZG^A^N7pet|yGm!vd|C2n;|jnUqvO+)S3;HJ&T+@60wsuq z=Xo|GDl)4GC5}5kH&vm*R#nbH+&t$t&N~)js!F*4u_O`X-v9JMcjrrR#AqcBhT9;F z45Q>r1^YZ~K35=M0Y7>F>R4Bwt*$iTu--@cX5Hv5NcCm_Egtm<|Fazu3n60!ncE*B)l?QNXDee(u%fWsFf zoUe0~YlQRkqXYd-*h_!IAE!uI2q+}HQu~owhQZ=$we=(6CH+V^L$J8J()y9RkXozp z(&=pk#zKi+70FU)iU6i>GSva{Y}E1EhQ@Ea?%*0opgs?4w^DnCO{4=NYu);t$0CRT z>_(;NQPA`kS$~X94kB5B8$=w5AQ#0-${p~MIXXOkLD+N_adm}~9r7ymy-a^6Y3~}v z`;I$0on<=3w=@2;M8-(J>B}1U;c&&5m%#2}!p67J)wICtb&jU4G2Xpm?MBl`}L=yg6#xa0lkb39)rG-Uat zisyrGY3h7*^zxYY#hrY(i&A0DifthTOTzl&k~?-uODe*Gz+9-{&J(*J3mYN=U_oGC zT=0=yuof?{6fi9=od?a_-Uq7sjGOsRAeNjJi`psyug zlF5>+AQ$JabcNkOg;#!szg@hS3PY!YGWr4@Z_f@7UT8cbyHrdIEx*ZNkG^>O?(FE| zP$6KIWP^-R^zi-RbKSI6lo3q+S|(_*G3vak5rJ6Co zKt$@C8i?#X3Xcdf04Ql7va=gTL=`m<*%_`tgzJ3U(oM4zZLjI?Vp9KlSfH@yNjF}i);~)sGVR}%6`D#WIa=hqy>IO6 zc@DS%o4Q=iBV{)HCgEvgnp^@O&$__yS+DCy3fn%Kvht#o-gi2k=XnNOC+ri#`q8Eo zlcv61q4hkAdB>{IGENryfvU=88XTMf!B4tgGgPEVs%4Qy<%c~+=VSCB>Wap>jVN}@ zUJn%alefo=JV=j-{5nXtA zt)2{~Nke}H|FqkHEpEiT2QhYw+v*BWC+LNZBpq^~*nm6qO^()uG97~yrdlmg*m9uu zfuAaCTM9JL13__~-wad-OEgzOA17Bumsnx1?yI)=sSnztU?=X%e01V+lBOgq(b-g| zhi4_79eDDzrz?s{?r*rk8a6rgA$wtTl)(m^PJbdng-UfhDL)hGMdqYlj0&M0{R?}a z;<7H{Y?gGHkbt6wJ+~V_{IDNAb)G%g2kfWa@1qw+rf~_2$ zv9Kx21lLBNPsU<=St==Lxd8A2r2F*#{Vv1U9`<_M!+{pQ?DUW1D;Qp0BjE$R0s$*5wCG+cu8RN|BEzGN z=H@B|(F~@+(DTwu1thJao0!alb9hB_TEb8#C9K&An$1NhFqQlgrXNb^xh_$LsqoOT zd%n!*nDZogf6=26;C>253_qP^&=#VvK`@fav@uOVS1w@b%E6LsgQPMmbq)p;$`C{a z1Rr0eGmt}}SEyTqbQRqs1{%1Ls^Nw$ZJ7x!fU@okJ+WD|DkrlU@nU>{^U`%FcKGP& zGoVqx?!z8@|A_0|De~~~6IEpR_{rYm;qJ#C)6ZR68Lw_-9BxIZ)ag{;Ze2(?U)-Y5 z3ldegEGDoSmX#7K_wk6*-2E1}>L7vnGTB0;b_BJNjMPtnqZj|trK*>fbne+_*BhKp zpnVoYdXg1W#ThoU(%SOcv_qbf$h8+Lhm-R+9Wr_T^zQ|bvL>xzj- zz1gRM`!BI)PD2)C;et(zd3q&s@^O*J$NP9v0X%7MIDf(`%(|zk>0QB z-PrF?(f=2jcTmc&Ta!l$;<@pL>gcwWNBad2h7v55@AT|y`U(g-@`}EE10l>U)ZExFDGIsDe!#Udui*n-{S|?B;ov@ zwC@5hhCe=E#Ks5lVgakMi@KnN_lgEhfC zv|ueWjEL`86s#=#{cA?D%t>&I$OWt_;E_Xs**>8|NrLUat#YPU?Ur zbC{D@d?+)dX@IglPd_D!RD-RVwgJC2d-kYFk(QeW{_uKH-I`#H?@j$ku#SKZ0tNXY zLlt&U zyVGE+jg*YYpc!SbK$+tB_a$gH?G?EnqvQPMIQg8+kv_X@Ppnagm)moE%L6TTQRqA$ zPLru!bp}NXZGeQ&QH7$YoJ*Az8s*7r$2|X02230n#vZ+i1VB7Nqe?9a(-+)dMkBPF zk4D`xnO}k?8($~n{+$@|AMt5*lBYhfBkrM@{B(5o;`HSB7h8viJyG7iJHxlpzxda4 z*ipJ8&5TIGgb z4KC3&GH36&Zrwvv!k;{O(yIq!GSAC|M#5+UD%Q^dmQkMMO&&NrgY3E51lHAMpsEzB z0k8-u5bQSVS6or=Pa|fj9(_Hhw`4`5v#yd1exO)S;{}@9#z}RPB$?u$ruG_J3|3Jl z`OEt+fFI9qRn06(retTlia#erkkDH)@iP8B*&BF?mO_V#ZkcqA8xXt)B#R(b&wfuA z2w)6#T#ZQ{si7BauR%W~^ePtjMRuhd;q!yUR}(T|mOKvdyl z9MnljYBql}ScPpE^V@qFgIH!64^?MT@Y}k-<(d%> zGZtV#Aw_2|wCR|HFiomtQtygaiBsKU-VOcX(R)+mUv9hd*zQY>rmBVqdg^J{Fo;sG z>Bta2`!ut3(&0=5F)J>DU+tGhbLy+Z$prTrAN#!!Ohi7x^woPGN$X=5Ggw;c-HwA6 zvcD<4ac0sx{c)zYGyA>c3-w+tU7H=8f1jsg?|qZ|yZd1^-V<#*2o#5WiK+`bnrmUU zOJM1z|za4Y^i$D_KyRuXr7LOkqd~+k3CUe<`jQz!sxp;@_FvL!ec5saWs1F}=kIXcRfRm|NsPrH7=^DjJt z#}Whlr`zlG?9m#H#CqBJb?4!CzwQnn{<<^V!T&nZ0|WE}^2guV{q^B@-*qg5s^0Jh zAO@8)(c;IeY!pvqgVd|aVUlBNdFX?Ps@9i;Et@D-I;qQ;mAOhG1mt~ccWi#0pf?R zhG*=PKtdm`$duG)ecJnQcN;C$s8r1GLg{!}1pwi!zF|k6n*z!IZr{AFFw)vJ&<3s{4`BW2`VK1I6H{8A7{(1+Gc8UcOZ_p9fa$85@Ud`Dlx8 zS^^&=la=tpCUr;oiwSS4n=6y#lL%+9k2ULvQFDz2!9H{BhrP5^V8KvFr9N@y(t_$4k$1S+QoOHo*sB)nx*%Bfiqle8D`| z?8sO}W7s)JAk%gX_NO)YPvhh{X9=dClutGpY&BF&IcVmWru$S#HHpY&is}e*&}y1t zj3dE`c3yUczcfIY5ZLwg@bqP~#3>ESgzxi0HrUDx06;r-a8}(&*lRQUeZ*(ctRJEEl zwK~JoIf;IVe0|Ftq*e2IH%Gak#nNaFwUJtw@n>3DFHajRm!5E;PMQtzY8qE@C%{#v zq88!&nlC3oVla@NvL1^1XHd$j3yIRHu?-Q_S5cHu6LJlGDlYD`c%dE1$Wk`ws#u)y zRZ+fgxE5tv1obuaq-5Jal&Van_Rh1{G=_9HK?!f);HR|GK}`TYa>t&eQhnA*fH^nhvmn)2d^&tabXX zck~df;8tv{7xReEw~wpx1B}Q}vE|b?qq;Uwdl!#DK7FQ)Zt4HMm$3h1?yuZ5N@FZsAj6lj4Iz3=q@vI7O?cu9-eyugD|nI zTQ>b`;$lXT8=WPhN4`)>$Lsj^iI6iRQ(YKmyTw3-!?ydH_hnputXtRc#xC+TZYvA^Lsu4tHl!*w$!m zK8$dF&>#a-Eo5yyqfxij%oX~2qrzsMh0s9lHTILoF+dD5Q+l54f&mjHO=LT-4;b1T ziBZ~D#(HlE1C49ZL~Ri*K9v}!lwF%iurRiS<|+amdGb*fbfi4M`+>E2U3P>RLqVh^ zogJI(8n-9aG1#^~r&IKd(_v`vav!Z7c9z4-rBGxkJD95IUSeO#*7M8D5`D>V0rYuq zz_@>|R=*CrXHaPKqc8SL*>HSMA7w%!=4{f2#wDz!*RMfa$BT-@m_C{&UDE;WI$p3P zm&Xw1;rFD-WixcnXQAu}tt(%A4Qa-ac?<*B%`UlsV0+ z$PYE01XyHS|8zawMT;$?ycb|S6u{H)ATb0GRm&A_`PQ&~I)IcUJG3dr$;c2#wme)+ zE^P`6gyo$qvXf;&I+9pN?_h{cIe4!*NcG;88W%|f&N&p2fDUT0$}Lrn#}#VQUE6-M z>(~)vwpi5qd2@rUCOyS{G$-12216AL2xDCJI?cLFV~s#O0mPK@Sm>f^Ke~qIHtQmG zA1{f+E`WdXgzwB&g5%*KYkDuzE#Nwn~U9DHT@v|H!9nYz~{_x|gP zBBeBLU+a?#W;jyao_!zM(p%e9R*9xaUvN)t7Gq4|O~v$9+dv};8XsvucK8vk)w|Rq z3cXVW3MlFb-l9#bG8ER-Dj<3|Xt?%seU{J8($+f}XcJS%6TlMOtT$qOHICR7zYp!j z<}PXJEG>l%@9ws3e%;M>&8;Y|qU*e@f=3!eJI=wCIkOV11y6E01^`>3=X2iRmRXg~ z@w`er%#o3@`!!qoCm+lSp*1fg{XVee#R*O)~x6O>u zg6eAaVc=#3)AMa*Pa1jmszbJ5B@r2vIl*8 zMsTrF#U?$mT+SW$hbB^_iP^7>mBdGahGBfO7tkiSHKo>LDX-UDE!qjLoLE=#dGUO z8B-RqlsHYQIGvZ6XKsmOsW7+o^=Jm)GY+$fQCd{cJ`^+b@T!>fj0Q)n3@P`sq++Pu zUf-x|zbm_ZgU@&&N~pX5=&JIvq=WZpBBB2pZ?um_^TU~X)+(EsV!p>Xa@7rVL zBUBC{hh@<7LItr`P@N$5Apq0l^L6}>yuecUJgo&kH1qdK=Qk!g|K2=2N)Gj#-m89t8 zUQw~2*rClYl@-{*8EMTfyveyOF@h{^5R{kAs-wdw?ih7seX1akb`80)uTmIUW=0 zBUOf3QOpNNnNfh3DYHd(bkh7xf~-<_Qp|ogmHM2%qb^K82Q~Wuc7l)E403w1sX}Q~ zV1DD8Z4E9!FR(ZsEEOodR<~W%W?us{`5C-ac(x&>vx}LCSrz*_1DT7Uv0yAPs=AA; zdfvAhv6wIhdG_TW>_#BcgKu^u8&DzY4Dt=W$0Y5> zVJoe5Lv?uemCekuCM~od{C+C0QWoS(1r_uU>qaB-B^QQ>XLt z7BWBvILy6mj8B?H2j|a^j`W1oJ;PQw!rvADQ4#SoiDzzpYTJ*b95jfy*N?iEA;-u} zXe_^;Z)EA#HXrrn;VyxjIkq5t4z%PN6|YnZFJp{-10x6jF!VaS2tujS)9Thg9i!~H z*)p+P10V%Oui=nG>e3rI>y$jmHmKmOMIs?Y`lFNo#YmrIRg$}lZ?hCjA5xu$YmCMi ze4v3r8m${#v!5o@qI$@(m^AL*q)X>!vYB=09B=^s6lz7K7~z1YWl9NqCrKAd^?6hS?u8}+C_I6Vu~Og$x7{0KI-k>K(!jf|j8W$1 z+Bov&Rb*qInqg@6bfWevqk2D*1kdY9$bmR0Yi_Q#Bv!9|9kGld{ukLH*bgPN<8qQ} z1$r`v4j8>zYtzM%wsKvsC7ww)p~dw_mh*4-DU;$;%U13QxIrTpp5|f^Ojw3%4#go! zEQZO^bow7Y?In@goGm)~;F@?PB-}5_ZO>M+4|}q0y*WF4dGwwV!ReBG3MS|1=Up+(_Y5Hn|XnUMx&PCRGS=WaGa1QmYkd1S1HtV zR`Ld-Ae-}k^uZcD%%MrHowKB>#i9;u7(k4HF4|&GYSos5w4GjUoQrfq!Pc6*fuUo- zdgW(#EH4`&Rv7NCRYPr-tyRYQ>;g(|8Y#|jzxOm`1}tZVa{A%`v}ePVLf4*=x(6iL z_f5IJwY8<$)WhACE!nkacdTuXws8`={-oANrmdB1JjF<%M)dLHHdEC{S53DfuHBoK zXl&P4{rk9wN`e+#YsNJIeMskK%lojctM7fJgnxE}bZ>S0;gd&{PquS!=SlwS4z|iL zLU|Vn>chw1wFKE{=XHzt7UZL8SN^uefkido6^_);CZMw?T?(Rg$`pxQE;7t^Mb z-}c;K8pnQQL|oA1V&>rTuG=WQAM8hUxCRgz>!3=KuGbkwsn;UfG+8@HvSIO^1Lv=~ zfw*=N+!@$ z|ModEzat*~Q9L+l)4z)uZBqnFPu_mO{dBGG0nzU+Mn(YptFORVawIPz?#OCd3CNwU zAna021GNyp9}Ks5cXpoEqXCp)%f}&lAw3k;FjxSk@*M>+<5O~b18cO;nk(Rx78Lj* zaaCFPC<%qRLxU^=Iuh=cbhEXVWJHnH2!ki>$&vW#(N+Ec{Cu3u(kwHQxbdLa1_0G2`kZ%m zhyGOkni&e{R}Q#TfsV;Hdz?KtzEFR;&te`c60*gxzFDNu&zOVGIf<9C=f4r2TcJ3# zCrL7$CnbfMF44ZrA$70nJma+b#ItgJ^4?{{1!EvOv%+V9);3cGCfC%%vr?#w-H&D=EM8K0v<9 z9acmmYa~*XUZg#3+aUR>6JgAGMQZq9A0O~2KH&Qj=En4nvhAQE7#CAcZO*&ooA?VS z4eN+004;!YdN9o``=F6SiwH4Gn1DE?IwF02jc4YDJ!!pty2jvZH)b;T&yFmtf&FE_mY+jFg9!jTZhAi(I7SMadUmo#N6x6ueB4eX5Lsn+dHB>46)P=etfj?VH*u&!9e z+t~xf{bQS+iIv1(RrhBHt3CZ_7h(_9x(xJr_{1qQv&#&h>oZy_s9@`|tK1?tZ_rp5cM2#sjMDJ$(4xN57yro$7)Qp^Zn6je@m-R?vXgdygBa z#Ne8zAawohJMuOXgP;~e*rEs=4CIwtHi}EOMN>@Hk<4QZu$ay%`311KE9)qtp1b|% zK=}!y;H{{;%|pwWcyaQw4$g4@f?l8_h4{5fPx8rRxk!NnQjm4o>XzBk$iBzEu#-ex zev*4Dl#7*Y3y%~`=DEwly#28monz8J^5nfruBqBQrufyM9weNa6Cks;z<^Zrfxy5h z4gCDg!P&XP()iZk!>3OiOB6ZWM_ycECpz?_z|mA=o-6IGtf&od7E&OdXt?{)mz3~> z2bZ+jShc8zRJAyU`iMGGTiWj;Y)R*J_#PEpNZ}F?>Li?yWQkWF;L=K|1=f=1bHe}8 zQ@67yt~-40(bI=}kG_BG7KBN4?nL1I5c%3S8tiMJ)~Y;9uJg~R5lolaG|nnsQ7SBe zT9m0ti$Y6j32$8`#@IsidMi*LSkP*-Tb>y51$&;L#mP4M;NfC@!lP+ewna3iAR(VO zVj-0u;BgTAtKPT*8b$(Z7o6l03JXv2qdjJLNMuq8LG5Ur_))<-!j?obs5-bY~l(gb)RR7s0s7?3MjeBLm**ig6)W+_T#CS)uo=?Mw`SL zooRiBF{EJqNR|p%xg1N});x-*Acz5#0}(Kw`#QcsIfxFthtX@$4FnEmwPDYw%IvAU ztnoc#Xp7>jboiQ;7dDCX1r#qqGFssnFc=(E#zNS_X0*#q zDq<1pX>O2K2#@7fsnn>ObUv@!B|H@ThlA*>cCJK|pbCG@4zbSdt=dLuPMQ^5$I*4J za&_So*yL4Z?yE8(ChgdkM-zU%vdMTl4h7rUWdHnfMq`RVb&+0pq(-mL5e+BX9&c@EKD#qlzoSC}=x zVlnm1tM@Ha|AuE8HX$i2OrhYERG+f^Mjp|UHxWXD#pPeg?M+j3T@_i*zHJrevNcDx z0;U;NJ=#i3XrI$U*OMgM<@wt;Z%)rH4qt>eO%O3Zr3;(Gn0&#u=czDa%DIQi)HlD4 z)QL7n(4jRAN#KpP96DPj`-OtwJCy?fqmq9y%uP`U(%Sr%4kzeJfBTQjfc@SEZVP0=AU&ZSEA#`jncM;f{dA-_40?qjrjIUV_2+Ca#vURM* za}8x$CSx&QQgHi1xgRwi#J$6WW}M7&0xS!r7dQo};GQat>`zI<{} zNp7e_*agfZwJwjCZQirEzsTXP(~x-~CLj*A2;M8}2JX{nH;Kx2yf>UQ+*5hUNP-ky zM$6I}GJW2hE0}omyuO3Zc6D6eymPZp24?y#Fq4XrLW$s&+G_(11aB{1Za=*`dP$n4ZraLY=zfK>RP6^!)|Ie@4#|OP%vko2fSqDoDK=RW05hKle1f3ct zorhA@0R_6~4ttdJB6Qjg0;S|NqEnc0LW0Cif(ezk@G2ROlf>+~#ry&=qom4{BYB5i zDmtd|n=J3y{5H}m|5A>Tp&t@24c@B3AK!`}0?BL))@Ylx_I2OvA=h-h@5x^+OVX&tGhmCqxSxar zXrQ%8j;k>va4ENUU^Ms4VY)Ys?v?xZN{pa*FOp!-eaEwB=^|hO)4AmQM(E?D!ecHRHq37<7P|7&0GW&fDESg2oskud z4jO;hkA{QYhe(`a#!+k;R~c>+WqrBT0j8L7QnO-V$7-z%N0W9|w8T;nw420%c1bx#W7vYl)Q!Q6wn}?O ztTDIosY#AyBIfpFPob6C)to>(0`lLWOx>ip;wRN!n#h7Nr~)d;qnAgA$1l$PWumCK z0WErMo<(~)Z4kj#NVdD5wd1nR0EOCb+?Zv!J}SpIPsgj{Y&|V|99bT*eJHsxHoP80n?gQCsOx(yP z?T5tF<`Ybm8J|=J&O_He9P~!(i9!g8?Cp zhU1I&Q;SuGYTCox8?$(M$2YyXw&^?Cq&;{+OQg$VJF5weyLogO?2Gs$)^Z+8Y(6{N z#C$b(NiXxD^w770R%r;D03M1=y{Ll>qRv>-zVu>E255{1~;J$Z78l=|WisWeDkA`kZ zQQr86o9~n^5pepP=m%BdezfZa#Nf0C!rQ`0xH-VlU5*YoV#qpOsfW&7MqJ-rK6f&0 ze-pJiTP~BDMP#e4G}88`#@&fo*o=r+;Ku3N1`q|-G zV+H&)o|@rDsg52jt=)A4Bd>8^?=Z}4$d6j%j4{o37><3-%Jk@TOWIT5SPR{RLDs24 z(`f>&UQe}V_@1(lRu43Kj6#nBtNI1O#i*B1cCNx5spShxl`WRxb?MEo2y{G3Vjedy zxVaZ4?Msr3OGtkXfL)`b#_-h#TK$MFwm7_v()unJ=mUk%Hi4J ztGA#OvR+_RCqqa}ebF|I8t5&(s=+$AtWmMNyyiEPr&7qnvk6xa-!&5t(G*ni0qWZ|ooR;MCI&{#L&h{1ZeT_?< zr(3K78+C4rU{dW1HXx8AIAzE%l4WO{0pb!R4fLLk3zg2I^ipDvmw5PNMe>)=X7rlV zjM2#|GP9kio8{4UIV*bz_hHmU^DzBZ;ELoD{-dNXe1+Tqhb95at4wapIiQ%)?Rq?X zl}tP0?Ay2v)xauoj_i|Bm5+b`&VUg~XIWk(BMie)x|kS*DE2Y4<>N^dcVk3`XJ-u*t}&Y03^3?o z4p*AeA7M^=6nFDSY=z;k0SZsS7MO2ks{F3W>786Ec%#8nYU!T}VjS7=qy-c^)Kdun zg{L~04v524*hx{IW6Tw%-fLHV=&u?j)@qkz_1CW|Bl5i$X zyg<3%!Dv9zqCA(F-}J+x0O_3+GuEy=F&B|M(t*N#XiWjbE&WKh1vIKa>O~UBH+l-1 z9}-G;NqG*71Szv;aBjp-pu?)S3K=tFR9t5Z# zZN*!3$%53)NL_X?ikh%EqZ71pLzN~fyJzSEhm24Qks&=st5`5Q$C-+UJNrjnUGIUq0N6)y00X`p|IY4!3K(tYIoj{4|qhkeRKss1b<@F(KymnL ziFU;Rnxb$0!y{$fp+a}nIqgMDVs^=rM|O1$QpM|1FXPfVWOWjrjWFs--2wx-oX0cl zE9o`@=n5~+kCPfH3%)R|h{#HLEVv7(|fsb~2#(J8J3j1f$A5>Kv_ zTbz{5{UpaeCU(lhQeTSzbuF3JGv^tsye+N>T2*Oqic<0$y2m6f<(Q)a$L;pK^JsT( z4Q-#V@p8IoGZIzB7~s&+^EkOwL9f`Eid94>d5QY8#cndXlBVuV%_!>BtRYaXP7&0{I+nSbx5hx=E<`bChX>FabCmxRJAbxUw)RBua&7Z) z`*K=ddD2hgLT5xcGj2dgv{T)huW9Q3tV{mSl6ihZMh-G<)L|M_V%S&$s?RBdDp|25 zz65UfhIMLFD>wv&FZ+{-tS{fIyr1NAp4GnQ=+(kHn?PDD$MbZ8n%w^-N*yI4ESHO< z=xiGpu*)VUt7TIe3?)T?&jb*V$$u{qYp6T6Yodl2Qw;2MLN|+*K8;(>vQ+!UHL!y^ zDgf!qr*95VMz0Sqev)hSs{44!0$QMzNlx=6_cE_T7N{XoUac_csv&3|{a)41#O~SD zV-sz3GY4o^PoUW%nV}v;IU-Q`iShL1bDYj`U;{H!TwayP~1*G7t<3CkCP9RM7AQ59Y6UdmaT6K_vvT_|+=Pa~s_qYiJ&ymnuV*r6@+>hR!8J)svAR@x2g>-D zLm3nYp(cVbcw_b)XwLA&_INbFb6_}Q6gz{m4v?EQyJd{OQ-9r#G(%Xs8zB%KGtG%? z>iWuCqZKQVVj|~DSHM{j3nzP?5~;exCp*ApT!YJI^fEal?VAIuO}`*_0Ht~>uV?fU;hJ>FH6ck+z4r$ ztIs#;qYYcLVz$2@jn3(tH-ZX7V=P5#wfi3AM-|6|^MG=zO}r?iOV+ps6+}G-HcqVv z3sgs9=Q&i-tlk@em0`M!^NrQ?=Fia*B*tcEd5SIR~KqCwYFX#G%NNeua zZf>sifGE=3v$~qq7lYI=k|&<%T9l#5#$kmL1#?XX;SAW(X~0w}Qn%4Vl)cvyx7zG# zw#XK)@3U1;?xGptg@E8ZN7Mh2zSwxWtiS%@vXrWYV}RQ9XRr)U`TAeYGY0E04H!v} z&}+u99v=W0>#%`!^pKh`M$i7f`=-dh+}5Z4wv&P{!5U5uzwZv9zQZeAVz7R-jn%g? zj;?TyJ|4P+`0L8mO=lMUDtq##0F%&ceqAqnNpR1|Cs@W#jePt%g;Ej2}#fXmFQ-9J8(vI^7c5oZ|0x7AMN%6=SB5tl~dMHlrJ%u>T;padUZCw zZQq!OQLrd%O-^2|(e`+EhnTg>Kw|@MxpZKlh?y;w{omW4Te9)Bk53KLO3>HyOnL3+ zbFF7{fpfW9+Dzq47eKiVarr zZt>Fc=duhi8YBL^as#p5B{)2>afz#GT4rNo<}Hai&z4Dq4?f!k(Izc*i43 zL(}br$(|4C_we~9zPU-F8%#%lHtI2ki>opBmj%^^ZB0I#rvDFxC}K|#@&t11bD_^> zR3dd_w{EWTd7yLV4H0R_nk0%W^zBmwFH+8e3Da{#vth4~(8N$>Mvz0H8AQ_y&yRHH zgK}%oqOYcH>8lCqqt#<6>ASW=$BbX5tnO@+$_qQdgv2XcpW_J&%Iinwut>$)#ktF8 zB9m5p zFmLVN({cPT+mCayE1=a_OD)Yrds`KT72#s@06=C z8MLPbw9}QBHM6Ws%eR5dP#0>K$)X=ww{iXA^KcfHll-R7l2=1m8>6gw!Ck0zq6pJD z+zb1;*->hV)ri-d6f`k=3mYf)jaF=dU+4I}Z|CrUF{ltLqoTw{D{rsyR#_R9S20KF z2kjxBe>OGqRdyG{2R(WRNwbH$;=>ltZ{piB+VRUu7bLNo_Zl0*_CM?d1{6Pj$7&vBP_{{CFOJSaWai{) z3)jUvx-FHh>%@L-n5dS@=7nqZF{TifAd(3_@fNPx$n&4tx@X?8_Uk@7ZbQ(#X3cdQ z@~Mzf-r%4wTlNE#tsC|Pj0FnI1EfK3BN*7bk@s3oYq2lhJ5C&FFadbr6`Hw)W=0{@ z(&PfU5Uw*`0z?Jboc&>en1UXHcft1yS=y*7Tq0S%iwWn|n0Hb7s^-LUYsKew`E;l#@LwYhSWrGSDX zNsN@F80TfOk7gk^fEK1ULvL0*_s%cX&3?}HeT&Ua^JT?m9|1!*M#<9Lr@h5hNI9sk zC9<he7c-89je&&1^qan!Fm%e zy$VoK!GXBgRibM$svcgZp6+i&TY*FnRIPKb{Oi4v;%3|HuEq%0T5}{=k0@qtbzB-P zc+`fe0mt*`nUy(GM}UCME(~|L2c3J<{=I3ZLE3UZMwitp=zFByYePLO+}N0r&CKM! zYfQ;>``*qj{}bUqd-|W|zTKk`_Jeso0gW8KbuGr;`>^-qqtWt)->kL&J%~{Mvgd}9 zX!3p1PN&xdtc_AFa2PD6eO|YJ?DV~@WnD_bkTUJyMNVG74X$SdD+0OIFeAAZq#{9E zF$mfF%s$9deQMa3s-y^*mky>=&wk{bqNvB$96BY=ZgqA@*TmG-*^04AOymyZKF;yH z2zv`yRg*#3&SQdE%jDB;=Yh|QI!P<1pdh(lj}wUh<69M@KoV?76oL42o>I08i;I^@ zMZV|1X%IA2#Pea0WWZ@3h-(i>x*=0uq0^@cO!g<7D>miDlPev3T@3XC5w?ckc>WyN z_7)SP%fbRp*1oYYE@n&0lUYZrVx#Ef3UeD-nAuhPtRv}4Cw%KCAOe#u!33nxuTzVC ztu^kB<|IQ5KV%%pKE?K86R2R=Y?2e_fSb5LXKnkWO+6gomu_cSs>_u)Q}u|aSy_R) z(-7VK1*ZC*kecw0;q~1Ez#7k{XbKW!eLU;#_URBB>1a>&eJakWzIg>d?un;lJ49tF zzfQ*a6c#(JAXM?i7H>}Fz)!ZL=E=Gw9u6C@zh74g9P9agLgW@LU2?lqgBi*RpOuNH z$+oTtw$*rNSobZ4Y!hzlF_?v3-OINb2GGpeI$OIJukJ1;7s3UNGL0f`f3doDKBU*i zB~W*oONMXNuIgyZlzt_dvU?F{(}@c+H=~)-*W)5;(tC=N1AF zc09-1S|3N-RtiVP2~dOsIdJAK8&JRl4ev!##-4J3x(+tcz&6b0x@vv?BTrSgFG1?E z9t(Zbn^u`u=S#A+}aJ2%Q{7QIWeDPB`m_I^;qYus7v zP*%(Ox(V5<+$bMUis;E=ftHB`)mqopi{OQCzV&TSEq-(iX*T`j zn3U3pF4DIHnW&#m@c&h}#+z=68@%O6GJ9{8nwfCcxxR``lzL3G{cI0AEQM-6J`+=$ zrq=e-u~of>2-V->rBiz**-knxgiyTEz-Twc9@RygLOAHT1C^B*o zEuGZpQ&Tm$1cr#GuO-O&*ct1{8;V}inNL->Zpr<*8WH%4LD;x=9QKTSz~VVyC>Ouj zm@~;JKME7pS9nO69iedW=Bm)HFd>Pc4t8T7a5}~*DdyRyS~pSrpzP1%V&ce_`lyUA zlZN;?cwJgX3 z1h#<8Uv@$mwSdCuAT{`XP9A0rq{Ro%5_ARwmXAu1IWVF5A7Cj=eziZ3ugB9kiua;; zV2g8R@q&TdxiQZZynbC^DZRGzdaCcNWnpX_+A+%5^Z_q$C=3?^l5co;Ov}_7SIF4K z@CuNv$64Tqf`CKerD-r*Rd{k`u=$3h07qm?Ar2S{GUZD5(rF;~cMdf4lOj5VaZ!ZFjNCE7dUY?5@<#Fmxo=kM#$;zP=R z+B`Yxs$AAiftD;(7#y~?#>FfVcis3?LMP6>230U%z*R3tSaBr4T3Lp&=(gRV(B8~Q zOe5*IUf-+u(HbWs_4?7@ysH?8+2^##Gc-^&v5O$92#Ah8n+UTAG-D0)3TQME)I-$7 zNd|b$mTr&y5zI-f4&54T-@voyk6b&GU6;YGJ8E}js)lAPi|LO1ll*Ln{VBfW@G*Luxbc8$dYM>#=4-| z@rw3fV@O)Zo0`(OR`U=1E-kw4B93IiJ1`8jt-*on*2YPAOp-WO)rQ_aY6S~ z)Ct=pE+xbp9tP0K4+_%(XoeaZM_oX(M?0|QCflZEJ;pV*jijq%o7vDpA&Y!XO|qv_ zS-D2S35zu<79s{ybF8^dUV{fUfGw*&5(G+(%t7pT0Xz*VaM|==gqitF2}3K8ZEMOh zQDr$>sp7KCCn-^>&bujW-aMC`T%o5--+?!tf5NAHK-)JR;M55gPDk9_Q_p3NBKtdX zj$^as9=lp;v)CW7;wtWfhRbMibz7#Bc<$~iobX|4v=lXN!qt;liX6#u# zM)!Ow0<67CGu9i_xud!`U^4nm*XDvN!%ysUw{Fb3+*DU-2pX>oc=5FaC)1)s>VA%BnVk)YCrmCKYKG9f0Ckz(VZ{6x-<7;FlG$eKo<<>}y4q~DBkvJWed zp?IEFw?^h2wNM-PWqTif4aLbArA$z3?yH^=gl{n!c6hzN8iES`q^oq-a&ZH5MB7j> zdp6yC^9IuA=bwZlX^Dd@@Ly>fp`7C&c>RSk0c82$@bLZ7`9+hWZw+|p^m@gJ-$jTi z^JOv7cV*n5GW?cf8md)jyI6#I9qRN zc^4r*VQu%q12G#Vv8=HRvmR?27BL>erJMz7N3iiNUOlLi>yFcDm8o(4KG#X+a#61V z*ynVzHjj~Q+!>54pLR$p^4~F2%_%9CjZf%G#i%9E)_Vc=l#@Vtw>j@BPwXBaj`%Yz z7f{580#XJ$C7I5_U9FzZ(`)e$>QN*K*y2&}xE2BfhN|>1eP?y*x{F(e19im3EnV)oDqPzrqNRl4UxT+l@do@}Nu3>N_qYdY7o^Ii!~5 zIYs7~<{)PeR!`>4yx$4bP~Yz)%KET-#~YqAgTTytzKsYh%?qz40?V&MU}+$*Tn7Pv zLHkoOC(;OAE5#4;KW%)_o9?n7&F;eSC@uyt2(`U|Uf0RX-Q@a9Aegn$^g1oCuUO(I zw3zCU@e^_HN7}bnkBZs0=IS9)+;UL-`h{f9))ry1b;s8pp3a|S ziNndGY5Dr3uQ<{hVQ^J4BRMmZ<2km2coF<`T-bG7 ztYX{HB4(XBn_#1<<+?Tk>C#pK4^$w5p`7bo+kT<(v>s6>g`nk2-kd3~(ry7(lTC>S z+hQf$Eo)#f@!tx6Ni+58DhDIH+_dxM-h1VxV@J|V+H&31e)6?@e5i0R+lY!hV|tr;M{y44P6*mQyu8*N{RZSW40^+$I79Pa`A z_Y#==>h{+G%egKCmX{TIbysEWY&!L9$X^+X{PD-1{2ZrM1&rz{nd8y8a@aCFA41_} z^~Ni=@-y@=o@0VN^0&1&F3Kf^_us7)wrBz4TkvS^9p_brPHpJ_u&@-4BWv{K+8Vnu zElW16QkcvNvnPUlvH%siYVwLW%*tgUotk>OrBhufjA|oO`t(;d!g{u4~dQnR1b5Q#$b#U{dfaBA*k3mk9kBr-zQ7 zsUU9j-0Kk?Fpoy?4M}4(vIJ9gOzS6yHxoJdnXH_q$7m5&9p^=b)!9#5gEO=?-|%HS zSo&O5E{)^)W~~my%J_&U7&@|{66Zz zf9=4(jb=uMhFsSXyhBcg=QVa}wHj%}*#&!?OyVUS=4*4ib7vX={R1x_#`RHv9Wc@k z582rBwfbaj`}na_t{dC3WzMHfnP8Z^GI=6eB(62C)gjF_=59!H-*Viq#X~?{QzC0Uw3PTYpfam!=8{BZfmfjzr2Ty`VIWWL}4b#MO1Zyrdz;@g; zr_1$qRe|v(tpr3r;!Jd(F)2+SbIMMVaXk5CfYxF68pDCOISfJz^B{}#35kmJKOLYo zny&eh2?hlbh)81j)pO-{^GqRFy zZ*1tFM0HH@GP*z_M`|fNx6?&C_d*3)cQ6>(e7Ri00H}~QI;*TK#mt;$wk|juySi&4 zrX)IFZ&{PdW+8E&<(vJQq*Wb3a|Kg4tJm>LhR{IFC?d7N)R*r0p$Lz1tQQ*My^8PxM`TIrfgVpPFreexnGZo=FRc7=(fsUdhN@6aWAK2mow)XH=Al zr7Jco006+D000pH003lZb98KJVlQKFZE#_9E^vA6ed~JMHkROjJq4!Jo?!r26U$@CNp=)2Ig^8~w1W zion6aIS1#418`p)JUkF_nPkPyk!YL6!RPqP-u~XcI4)P~D!aLDL~uM2PaYpWJ%E2c z6W?V;v=-;l&)F)3X5VCSTGVMGno=~ksd%++Zp%WPmy6~;s#5WyY>OmnGWZQXq?L$Z zyrQYH>$WN3+f9|G%d}_^)_IzWZ%&U-UY?(X&AUcKMIw&RUS7OD{p!ud+3R!s5!&r7 zs&Xmj^F`aVRXU%GY`H2cXntLndE2CO{=T;-e=lqGsY=zSdR_1BHP!lPPr#r0vCj9> zcX7IE#3}u9QdMPjB*ea0Rng5dIub<*=)X-X>IUt_OuQ_M6vkR0%n%;cFxPojEXvEr zSK^Q2>2QCV$2riVxH&yjNT}~T0)3e`>s4CAP}fnN@=sHG+@$XsdI-SfM-KEH0WyvYinC$y1YYO~34SuC=fS5Z|X8*Ahvd|%P`ZrE^s z9mRLlL6P;gZL+*;(N;NiQqR@*DSm2;?(@%OR?vrfm1m87OrI@ad3LQp1@N^>fkNq9 z0P#y(HZHAYHo{PQL*`y(r7`TO}J)LFgbcCwl}Z zNf$hBP*09DWRQ-m268r05yO+I;+D~b*bRKF!?eIh!I;+Lc%oat#M-KmlL7F1CUck$ zPh7%0(Uf_e{?dZ*(A>*a3WR<0`WrckuP-iM@y9`0P@Au!I*XsR&F$B)H1kyUd==I8 zeOV>n-fZh{b$gvAS(V0(`f<1`3S%-yq|=JYfvc^~JL36fjANWj;3lPRn+AAxZT|Fb znOCdW`SIV{v?b~J1wV9z{rckDZ{#A-BdK5Pah5sAOV+GGCU_xL(LHE}Roeic_2&+9 z)}&QambqTF87$R?7aM=t-gXh?b=q~HuPi9@77$!j<&Dx=N?b{V@nLXV){P;OqtU2) zHpYK1k5%Oas2j%j+obFf!Lkhh|n2p7s*y zG`ab!Ikzee<8rwIB^QkU8iWrgf5Lx$H#~{w6Z?^O0J4^ew!p z%61hTPQ{bn(nNHp0-@~fr9k*Csy{1tQTp(;ukGPSADjzla(ecC6|GiUZ2G$1r`&tW zd{}wIIteWW9&FLzT8N}d-mui^&s5Vr)o-a>7p;nwoQW9gI?FDYs&EH0p#Cg#u(kDX z)t3gCk5rrT?}N0bOTnHJHQT0WGR&6JoOFi8XER-09D?G7HYlwg)$E`JjZ8~t>2Cv& zSD`z49Q8BH@eYbSBu$>df+RFIYW8hf-PFv~ik19K>G;ksrT*w;%ATZv=BQstV#P5) z2E89gP0FP8d9MQ+D9%9)dk})}qP*pgh9N+mzSsFT^Q5Q|fjMi66TM!7h8^oXb! zW@)ns%j)J4vf)v+h_UOw?0LXFjUkOmG^kE5uS^ABl#NIV+r&s^{-QIT+`XdyNfD9) z3dW(~o8{@ES(d0%s&pA;6zR;k4}<|z`RVZJsspHr1PxVpuv~2cpdXGcGt@Qx(x$JC z_`W!;Yw%h24?p|j^T$UbdRt}*aIDB+Dx!o{pH^=nUK7`AKy%U7NdIN&*wTH7z76(9;Ms-;L^i;&RB_JN!e2Ox#K|KlXDZ~-T4Unx^8+6PH5qBNM z^OxrW#rH^z#3wNBssgE=7#_nuSe+DbZC9{_mM|j`Lo6olAC(hvdi|*1%#jC{2YhX>LAfl&VQ93zfFN8d!t<}s-hA_1yga)QQ3DNEL}+$?BG3wH z4Xuy?32L)NhKjHd_qSPmYXHQkUJF8e8&y$^>I!|V3K#$yQRLBenu`)zRxCW&2W9#I zg_<9SCV7;+Xc2iZVm)HXQ zCD-~L^+AY^QjrAX%fE(KpBRfS%?&I=5OG+(_gQlrjNcUp2jKC7;nO_pKXxaym4#13 zr^jz%qXX+6_>vCs5lEaauhS#}2sj0h0AS#=1Ibet)*JO;fz2`)xGHElM|t))0Fjia z{%A1`lN((0w2q@y8YmJ^Ow!k>OTRM|t@{kUN)!vt`a-i@dd*XjhBu+OzrTO@A)|wT zvC1O|&8zW`4>5E>c>pDBt-~luh={D=QFkiAf`C4|DL|15OUrZ)R>|rUmzTB=M>`;h zolGR4l^x*#zR$*}vc^A#;}G5L03l8Ir+*)#Jql0%{POJe$?>!ElU~FEjg8m`xi&Ej zz3p!Vs4+WJZ(H{7(kff5arHOiAvSy{0+?-*VlE>=3IH&4o_JvR>3XgEgK;=7K2=BQDVDY(Jl7rjCz z!h)&gV^q`Nih$3=IGTfWRCi4bt{E~u30O(7h_bu|DvPrMmNNjWK^zd_MO)BXcW7SY zsOSWRCjDIFffR7;2AR9gKMO{z^8g=o>^dB$4&OupmJ9Xd$b#4V8=;M^i4O=AzhSBt zLP}B$BF#v_Z-a7t^7>+a_UhvF?B%m>5D@+F`sClnbOo~PE?rN>TOMge3=LR$$JEFMjzdepyn=4= zgSgpyq-LcSGXa~p>{YE?MTcr%&KRYvEb=wz>J)gApc*I33UZsF5Q*gFInqRc2CCK$ zAS8uFIqNyn4IV2u$f7ipxIbqPt=nrqY4*hrKm5@5@o~ZO(jeOp9#xDY3sE9@R6Q&^ zaLX3jgNN9{nhU*c3^ucaM$+O(PqzOkuB*QR?yJA;QUDQTpju79uRrvy_MUS)(u?|m zZzYbBGOdfTbdJ=D1zY`~?l*oQf-Y=BqsR7R8La~JfTwyAlgXs#8JWlf(f=OUKd83g ztT2EGYoq@#gbgeqU{6(oAB#aiCpW0IJm}dA3c~d?2fccqlAURbWQ4ZU4&JRzm~#q% zuy4HrX9KMvCP=^?Nd2Mvme^hO4NAhxHpu$(GjW5dA6RFx>OgCm)XB2Vn+%ZRMO~Ar zkkObj)Lx*J2AG2v)Xe7ryW|fa-rYylP3`V!z{7ApPqG-AhV3c=QUGc|mA?nFWsh8( zZB+E)NaRBZvyaV;7?YvOJ;|psF@U%e=pbH#W3U7VWmeYVi}~4K#D56*^aFgFQo3)3 z8C{#Mf6m?l-6E0}a6Y*Il>CrLyn1%=wf01%i-r0QVL5>nCS1WOs@2Bn4Z%fV=v2^F zDgc|JC?XQ5GEChMo<;`?v%H4C@)JV&3FjFBdufE;{6g^)B4xK?@3w}o8W!k|61Sw@ zW>r~`ofsirqX}UagRurtLHbu2gb)RL3P2rD*vQW`sti1FL<3J#p!UTvnmKkLBS{UM zml`7?&q@XehC1H^iM&^^I0cEJBA}`B346fUid;}z{A{0C;BM@O6vp}Y^ zbtkzuh!uh(lVO%6&f6rFgu=E}n#00;n@VXjfW|Fw6QW&%D-_CwkLtL}R-Hs<3q4_I z%Yn%J@dwxQ&w={xXUF=ix|z|d)dWgm4vivtt6&+=SE7>9!Yij%2tX~!+A z;x=p84)buM6#_xsp-aiMz_7a(d3Kj-NkjUiz#wSaOPfUvOF-Q&7TG(QGXUnqE#|y|+;u5_ z#?XXpLrE$GY{^09kk%$f7wV=d#=12KsvXh}uB8HIg%4+11>!H5iVkQsIsC-csk&gi z2IKW9kH3adk_574-_{^XKv*J0#*3o%M@Qt_cgRsZ0U)QUyWfuWl(pPR@@s4LCpBbY zMva!r-rE=do~>RG*0roVLwN$XExO@c_ml85&I}=?==-O8TweCKY{m0sS5yH4N8u}= zw){$EU$;>mHBA*5ze6lFpJP(Cn$O3RVRuzQomoJ4s2u2Hck-@r5R|{{84*MGYhX7r z$n5d#vFvOYuO8d!9}U&cASFErrr#gJC%sg`MKXp{TPkaEbGzzR8%F4)Z^y6JuhwHy zxLrl4S01>fr^PKnFAfmdCbs?*Qr)5KpfSDA7 zN|Fjj=_Wj%>i%~AqUO3yHh=1ye8k%z>Jfiavw7BVHFUZ?x52ynUwm>IK%|3--jZPTfdS<`?-#%tsM09sypV=eiUrJF zApg*Dq6*beyr4`MEDtFyXjZ}Pifb9}ag6w;$ll=y+p#*5i3lRT%r@6zgg;^x+5h=p z|C_=Sh(9qrk?^c+bC<|KkG;s}c~sn>UJZ`Vo}ZkbTuj`=iiF~8N;^Dh67nLSOV;8@ zvyHTc0b~vDo?N3=Us&PG>$o;nI6g)hKv!{$8YphKtL^RD%|N-AsLXlCaNNnX62SnG zb28d?J+f*5mVOQKgkMdlMs_0D3q=#bAnTkb&mb3vv22bE?#`jsjPNonygB>bnqlg=T++LrH>ec%cI%VkM+ag z^5Dn&@amJv$dH9OEcQ7DN>NVp3(6|HLELRW;&hpTZvZ58nxj*o=3j+zXlFprXtxhz z%;>so8{S0DDu^!+pL7))Mr;Y=qD_$~Tpk@hxze$_s%RL^G5K*YvT{z;aVAEjo<_)M z_{DS^_zCp#3ZCreLWIc$BU1PWhd5|fmj|DJ{)aERZ|Wf_#~)45&iES6feq-ufdm*1 zplA&4+H*y)j;)Ri24MPWTKm%zxlNL1 zb;A`zYk|!C1f3Gn5awrhBXhs_Gz zUc+NufFsN8Xe^{E#424KJbv=&Q>6F;`ctwT!SDoBuS$*AbX3Q5LXy>VvJGe?&e!0S zz2m|WA}sE$QS}G|x27QhW4@v=_y|r=#zk9oimWEJZGi?@FY&Y!30G!M9iY+vWQQaB zB?onOLp644tIwn}W{*0&GJ`&p&0&_y%_F3>balEZYM8ziq)meQQ-*_7GRqv85DY2j z^UyYxUblG&n3X^WV$6vW@Zg26zb2raOK0^^6P#JDoP0u<&dX|EH{~jD^iXex_$OD3 znxPZS%bA@6w9^LHWiM_tU|f&KcKp7`ie%366F9nS5TAYVTxy$IfYn52{v?xSoGC3$ z7R8P}pq=uFQR^Q)*w^)=TppV#J%<9ZYGJiJJ3l@>CEpt>IAv7_jqCx2RmX|}3W0w0 z2Vyyw@%oIuDSxPUg3ciF8$N)?)AxuF{w-^SI8w_GkX~jr6;1od(JvUt7M&!@UB~{O zkv-V>{jZd6eg99@uj7!b<^p&$)sXrrRMrNDF2D}X&WhT@n03xihynvTJ}?OA%^=IZ zgOX@17$R}En0o$_1coIW+r^VyJ7g-ka)E;DW#~85W`L7&C+0f6joxNpPl1ukEUvAb z4rcE_nKw@PtR#fV$ZbpbbkqGr z#J^qSC|h(&)LTkNEaM#3KWU{oR3-MCm?8(`XNUWGskF|K0LKBy>Jywxd~vyj9yf&Q(7p8 zq5m(R9@(3Ju06H!mkNiv`VUQT_bC>P`fRsp0||;7_3(1sL@(@$&ZTBa9_N@lAf}7ii4~Fk@oP?Z5-T< z%&^W1-$q&SZM6D`1Vj(kx4c0N&{ld4S{9BvVwI(Fc|n!NEfC@@8s`Fr4CHZ3f7_^&&=Bi)DBk4ZGvbZ29YhE zZ5c*Rhd5M5=<&pkBdo^&>{zDgOegZF4km&M9Iw+j!p9~08f62XX|w|CUbku811Fga zq2jVk+8l3#nIZdVN8X|U?a8Y~<38>kq@;|ufT0!}VQ}tqBM@$~5lU2mUE#x$TYyAjJzz>FN-FO<_>t%6G#8nD(CXLUI9 z6k_wJ;Vcw*jjQ%L&tl5##75Rd7n}mNJa8iT$Sj>kf+6aMcp#6n5CpU<&*kaWm;^Sk z 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 z zUE14{(ras==WTyorA)@I&+s@S=z^X^ZviUq<*34=FVEfq#Uz_%Xuf-ZKTKe_^FPu9l0(mQ{5e7l`A%)PbXTC$BB6)*^NR1(GaImt8Ll^6$`%TzFFw^MFwk$ zb;nu9-(>YCkNS!`g?mjR$Tkf}W3-FsH=4wjaT)zBs?dxDes}6!tO+602-`m+l5fvs zq(Ee&Qd#0FB5Bwv79>~O{lfUZ0x0aHLi;@ikBU8!X0&`~;QHIuP)XGoe;V%l*Le~Z zbuJO4H zedVarI>Iw|QX*8=38~AL!`O$|!kT0ISa{Asdu z{_b{s$4&wJL5N>QI67&1wRUZEZTp$;tjbs@76{qrM+}}zFtR-Gc=q7x-iI1Zbk|2W zq?BIg+U3QnW)K}Y+NsmKKmKbts;D6X->%%GbOb^C>Cu4+X;++bp6>aDdS|O|oGnV{ zL0JR3md!h(C@KpOaM_Br4M#z)al4Tzu3Wxq`?jqAjpNblh;+tYNS|f_k*74cQ zehM{rs8r-%%fn%QFqZvXpVkJA!2vy9t;xglWPd>%-3sr@x<=$)Fn-SZUJtYux{5~6 zMCoNg9ds(#IY}VxlfB950=*X@=b;4EiexTyp&jv3)Ae-irL?9CS%m#npbn{sX^W^5 zxPM~Ot79paANb~C{B}uRui=Zd#f~2k1xaQA!H5IZ4sW25G1|vRt}~(g0`Hs0`Hk4h z$bN>ig;=QpQ~3R_GGCYz%2~}26P1*&QRxfkt6G%_FH%}qAA{WdT*MC=f@1xF2%>4( zev-()T^=LP4xdlqwE)X>Onnp2%eLvA?6N5)T4&oI;u{t z3gFweG(<1N>XesSP%l3_mmCDOd&3t(OnK#3&tr)B0zLofiyz12*WQrM#Iq~cn@Bfs zmkUv(29JAbslgw}WW-1_Rf-NZ`LHuFXf&f+8r>V~)ioP-2B)uW6}PG$+`JW-o7$e~ z+j+RTU1>wR-pT*?vMs`ls@3_AYneJdpBmChvxa!aGSf|D)wvVPPF7NimBz8OqEi=! zPeiR5jY`+LMN&^Usi?8MU^7UNgi#c)R*UeS(P9i}nfhROd`n?U8T45`Qxkxpp@iAFv%o$1d>OhD(cqafgXOM&yR0FbItqkG3w50P zFZx^vQ($Th-pR(nMw9u?hpsGE5uebu4JcK>T&qFkw$%I9`2F-Bz55<-7W4c-FFCm^ z{}yb2Q<^~bWks~MOhvMp2=(gq`2;o6WY6NXF>UDaTHLqFU_p2G%3yh^&vy4TW4sK) zmpJpq68?IJ)+5zP!eRBy^`>_*V}Cxh*kujO?sevy0L@>j0;RVlrqqa(++4T`Dx{c) z9h}4!5f`D!g8wK~%QL0;5O7Fo*^=8b@^(Rx_#xJ1 zRDZ#?e{CR8RuZn#c^(1M+FOZT4=%I1=Yo&=E;Gl;$znQ#ZAv{gh{&#{u^YbNwvj<5 zt|}V)TFA!|bjuHHWO5R-J;QMHVs>U+#>t;6CX*gB>^qA0ZIPBH=?n&~|m#hKm z8bZ-4@K19G5CtM0qgr;PUFYrwV!p_|tcAh;+$0jcFyfUn<>@j@Y9PW*5KjRs@gkWM z4AQ;5C)x+9#~<~(g}$7@M#Dmg6*>vi31W@pmYBkxir1cLc(T9>Nm@r2;|OdP@%11d z+y5yqd=L=#!6|m#z&-yoeZk184pw;Q1=G9kpK)eDhoBD<(K|CQY6s92MP{lqVhJi* z!ed_%Z68a6v~#hGB^j#-6YM2Z$f1oBE{|hoG1y%cGeKttv%rcm(*L&z;nm)mI1E@i zfVp)&E2!7m#tD=?4-_<$zJZ)9gyvxouK@dKTB!W)360&}UPy#KHSoUKs@AxdF;CLx z*L}TElaG!Q-d>VI(cQH*_mUBkZfba6lyv#npVp69EJoChZ%Gu%X-o0@Z@B^2S#(ubOfjU#HnE6H4fZFf;{SGQ+)`!0gwP2Q?&#_WeP@%vA6h-wBYR9Dp~Ou1!^%w9vJm+&smy;h{kk{f&t*RTMXS) z4LI1TW7KcnftjF4=c!T_nxK^G%b3^}8lhD%Em;cWT5~jxB}P z4T@Ax-jkIhsI>YFRA4maT2ddoBk3 z2gxcZRIZeuXj^%p=z-jH5>MI3{Jw@1}l+e&gWqbpY|&W9{;_uM04DsXT6qkMW zjE(-L>sD>bvMKkhq$Zp_xVeJ2J(6PF<0BmD9A>EtGsj0v9mO*MrT3U4zf zwEU;z;`t?;7E*KRAnOe5|3>Z$_IedfFlN7mNN@MIN!iX> zCsdL)3yd(H4VEOkTTA{Zyw3x13s4;n!4IT%yr?2m#D1YL-JcahjSJ)VywC?PTIQV2 zpp+7tU4n5Zha(RYjBk-50%QxWZu%`tUCVgExCABn>9vzvI6l%;^BVGveF<-2cAHe{ zcD`I1fX>Lip#64uZ_%!H+R1snnQo&z;STmL$W2m~7oW~{9XYI4sm;NJ&mKufdmWKz z=@1Gul$rOhhGF=H@83d^FBo%y5zk!gpXHBl$!q+IEjk)<>^Pwt8$&$t==}T$z-tS! zFWlZHRqnzk1d)e>*{)bUq>ZtAM|q&Aq07> zTi;p5K)l+VB75ig^i2F+KPK>Fa?AtSKblRS1&SLXA5d=7y@oh>UB>rfx7EMjiDiLs zX^wPMyq@xlLw^1;I`i{uruThkFD$dU{KnF7n!@3si0CL z3(s^)IB}qOabMR0uffo5*nfMEVk56vs1BU}AVp8`F0gm5*%q%>Evd7<_tkq-F^0(7 zBj_iabP!z^La%5h;?Qg{mpm9^rc9=eqgzXA6|3Rexv*VOPE%8}oSniEsU)p@>Sr_P|?Wd2{uTPM9;=u8Y}&F{^qyrA>BC0Br?S|5-O#)4D7 zF#WrZoM%x4o53%>sk!3xb9YX3^Y}lV6lDx#JGDMK6SQ;&ogCN9d)|O+#PuZ5d2b)k zdedG!Lf0G4FEC!UV0>$RE0}x?=8O@a{vs2vv1FdQwV_V+dV*!l8JNchvDWf?#Pn8u9Xx#R217eMd~u_qXi?5mHX+yAR!SQD-J` zpZKpBLbRNAk>9+Vz-}h=e3kW(X0g&JePDT?1yG8oK?=Bx>(gIpV$n*$Bm<{HxWQ&a zh@`8;li(KWDGsfX$1r&VNC~2=d{7&-{O8zsLC}=6>9F8T=i(cb_laldH1NXU?~(9+PoEfH3i|>J@BLA;`#W zd0f^kHHNpVT<+&pIZ8nAGlIcsRsv&%UdoqTfuvboP!^uCKF$vB?Y zYwY<#Aq&p?`zQ^ctBxiKaRX3xoX7=VHo{myAakNp4c1s3et41Y5Y&5EP8s^Y9zG3n z2MQF49u{b&#Ti4MB@9z&@S|ygGK)F%BRqXU1ycg{!HI-#w1M!s1i)n;Wx?8Q)u=KS zAK-1eM+rn($4dn*YK?0SJ;u^JL(D5*Le<9Mz0WX+wKKk`UZPg!77 z{hU382XwZ%(BSA2hY&KiL~F$ZgP939kOx!!KL<}LpOv!`oa`vfU*u54E!~T1o<>c^PkFWC&c-i@n5_i zF=>&z$aB45i*mZo;R4Thod_n*U<-wghFdq~fvyAH-=6p*?%iq3G}5v>-&H=YvluRTx(6ELo%L^_}xW4O!v_xB_iP-OK9TA@~!VZdvM)9eO*sCjt(9o3N0VoANW0VxN#kkj*9cyJ<@g&v>xTNKU}rg9Von7>QnR zPZ5I%C#raalr^jwqb1NHrd{r1P9swC8JJwN$_T=vh@rwIwOHOZ)_5$;i-8bvSG?wQ z#YG7xD0AaGUO}f7rCa+*6%VV@tQGUXRqoO&q0~TEjBPgV4rAN@l1J_f!ZFf1ZWB8U zeWa^Zckc=oCDk!Wg0jxnS}#F5=3t=(5UqvHWnCNDK@1@E@_Psm>i#1>b5ViTBk*o^@T-zp=_pUeH$9Z%Au=-TcGBbNc+bTjrXB;o{* z>D1(7_jZ%mvFzgdC>UV0~jMLKr664_`H zj=^b8FAsId_9{Nn*Aet`9Q)UN`4onK(VO|;{&odrgTCM!592nn#SzhPNP+x*!#iEO zkhn29LaKe#;3)?gtUD^(0-KAWg)3iQ@l4}$LP4vcHZ!oi7+g%zgamk%8zi{B+Y~j@ zAS)8e6?SUdTLq-;<|z$qRTsTira)(w5k515iR3Fqs=i$d`HUh*Xo1)&Ay<`P+|W3L zgldhMr<>aF7fgr}HY_F@P2uYzQtk&eP^xgQ%+N{O(6E2F#z70N#j?Vua+Z2A%gPm#&=>1W320;rmd=^9 zr84p2#j|Hd%J4>e^3`aCxa!ZNuapD zS@q+dz8&IBa|!Va_Xs8^acY9!E0uZY|5#D{l)!M>*ec-Ch9YgFLd1GAM}oScgk6Cr zg3eIA;Uc{JA>;57>FA~e+>N*5c?JI6WCf&Pw|-IaF^S>h_gG}|4#6hDLxU##Eg6ca z4*s;XULc6eQq8YY6iZ4dNMZY+wa<)!#yQ^>?U=|La}Z zpMjT_Y?Zktwik=%*wwi)=4=nVa1XCAyd zhVKrf@B~1GCa(s0XymTr@(gbCp4)nErPSE`gu#~c!VLzn>?$XalhuJVaoHE(09(34 z)0d^wo!Nc4E*{!3xU&O)xVGfJmOK?5F4=$qF9tN1B1boiD0Om^Qbn`T7(3IK89Qvh> z3E_}-hTedXs-zTqAj=qB(||5Y#}bE~RohS*U+4@RNe%{T#)gWP-4tB>Z}0PpG%OY{ zg)V~27*#PZfv5if28cMKyS9T01jvh$61`zz-Vs)A9NrBlB01TB{MV4J`cn%Uk%{Dm zRZVmV{Mq;W&Ty*csA~|wBhZnlQ@=)`o}8WR?B?g??EDd@`}=;pgWd&E)a&o%@-e0? zFBTkSl5+EP>wg>S{{3>?4Lw8TFqhZE^@wJBLSE`yOb2Qs29}5|m~RX3fbI)6bd=l$ z)J@o-wn+lPHpoF{Qk0||LFWt*Cqj`Ts}yV+j$$Bz-gS*OV?XftK|a=A&j7YUxJbOT zaw8LpaukQp>WJ`5EP&In3@**yJiT3*`A+qA4wugJ(MW?AwS2!-3(~H&?rPC)Y-E*#B=f+k$i(?}fH_Vd z6j~Yj7$QjTWI_fOi)EHZe!i{N%n?tb8tN2A5I1LGgzktXw_D`O)*W%Qql|AjSbYaR zM5IQtAgw4(V*bygtC1(zgg^zDV`-y7p z`v&;TP6DJtp_ZJm4RPAgQ0 zuOzcP<)6rFwc@^!~K*eHr45|&h=)da^ohA3~ z|0X$qR7u|SR)ce-TANv*s&y;$zf!>|R1&KzjNFEG&@3WtN|4TPs1ofb<8m-+mjsJG z!8jHb?Wf^-$;8pXRT)(i0z+wpu3BXhK(BDafl}6!V5U89qr5SdtxCCmwmebnvLH#VDTulf z>~pHzQyhpumiV(!(6#7;(T(R*E&9s+<=Ra;Y;OHXP!MJM7}qC#^gc<{n3rm?g#0#Z z05b2?Iz@JPP%OgPW{5IhcZ)6TGuC#c>*v1%1t=P9iL*NmFi=d0&~>qFCG;1fb%|43 zD=q3++GWx^7&H*%GA6*dQ&P1_ zLiZzprqTg73kTZ=KXUGcIHw6$d+L@f<>plA(g(uBWG`}oMGYc)RdQ?zau3kFzxmr2 zpR!-7iSeKb9K$07P~;XgpCdm~k-LLR+whli@{ zgNXb7)^WmeFPqJYomU(mgEz1IF!a_fYlEYY+r(mKvr0N$jKa50EwRj6X*GaOnF_0! z5Mv@<%EW`buvyFuHnmGbI83zjZW7v!z^1GPucSLr35aKOvBeXFBIrdAQC@j$x#HN$ zu4id$2>X5AtDmfAC+|}KNaL;M`#p317Z_-Dwz(p)LaRxZR645dEc9jzHR&_?cQe$-N`?*6S&K8kKyH^8qj0r-YWy9s=cf8V7>CVYGc zS2&4k8??w2k~ror78N8al`OoNzA1qhcp3AARvHrXlH~f*}H;G=k}K7COObtfsJXs*M81;Svzp7a?hQJXRG3yxN@k)U7QUm9J2S9 z;m7&WQpA84S~BL4Y*1G}TghkMA=Y~LnsI%o;JG-22VO{cVo-tk3%wP>VW6UE?RPyN z06AP_s0!((KJ-Q6ef-Mu9gw@K>7%-^1XWYsuHhR0oOJQ{yVm$_i&3GDC?rCf|dLxnK|aqB^7<{lDzH`Ek%~1bqY`2z260 z;TVFU1by}{dhRd|TpuIO-Y&UrYoRa7?%iMHSVrl%Tga?P)>cqbp13n*Cm=@`TY>_0vubzQ zOi-6N_vt^U7S#eTt~dp}1o>UL2%y(y&;`Y%iVD$1k9xTs#wW0ZD>V2pE1HFj>`jTf zVcn5h5@_?h1)!vzAhR&A`gl}NXy0{Fnz%C?6DuKfN>K#9!Eh7Y1j&l%ouMC>UoUiW=s|gVq)M7dh7>NC(68&F$*Y-UZ1$jANEPD3 zY+MJ{pgAbOA*d2>(EA`&O=U@wsg*D(bl{7e`RpB9?}Q1D@hy5(PVw6MBV7PUY&;%L z@bzfs!L~CBEv_b&s2fbfWqJ56ygv z;`eh_-fKWWmh$iaQm-h1k5HNm00E7h0|6oa4@kk)#nR?KK4HYy*MEyW>F)Oj&8C}6 zDN=^#{P|^8yG2&l{m9zrvmy6pcY^-UpXQk?V)UekKl(3C0*@d89>t_>U42V+uB0fK za1g-*(*}&|+v{*ImG8#6`g_en%^XmAx7}Vn;Ql|jIXfl;_r~7JsRNd-E4$^c03U6- zNz2VsRVX+U$)|BudKfFE)yYfo&CpqHS@VdRymiNI z`B{}Id(jsk+zfRmbCrpEr|}adSu>Bqh6+{ovTPkQo3X8a+dEMEBm^8I;xCL7r?w7) z@-LU2mF~d?EE#L(&$K)6lQ7&)=_@MmlaR&x_0&B5(NmtfvW?<}3%r$ewp#j@XU!Wn zc0Ya=x0f!x2m33#%bMIHdi>x#Tu{*)S4@h83C(!C8-n~(>>jj6nzz~A0|T4o07I1# z>k`Y&NnypMRUisWf`6JUCG_*JhV$FBo86cYu3q`aCq4@Z!OGO$4y>e4Idi1B+a}Ph zNvtbHc|Zwbx+;&=B@6ioN4Y&~M z@G{UD0KNji$(Y!R(;C#Ifib8iXH{S_7{l0_=4V`Q!k z#(}}n=Xdf_aPg3dX(|Rom$bLUZTw!&6@|o@-NBrM{?7;am%zu#^WD;(AKvd#a&%(F z%g@Y&`zOV&mWJ(}^%MBdiuF%cdS9BV_ip#}AK;Vmk^0IxhWC@~A^V&*#*c?*qNk^) zx0&5uT}p0&)Lb6$K=-#bliTa?q1zmzqeqCy`ziZ{{gJ7eoj$m>zOQ2f0l(++e*#}y zXKQnPoCNv$gqc9oqob@{MS+t^np%5<0S^Mu^4Y+159V`u3w>i6jFO=As18^t1Foy| z7btqN&Pnx_-;xh#8A(y&)Eh(H5E~+cIf6yQYZu%9#E57h+BS?)d8iu6nF?*Avp*7)_hF>A(Vr3{#{X`ayjOl|kIoS3SKnO$bu~$v7bzY^?qe z4MOI)XiNwSWI^x4E@(r6`7?x5g>|liMnrv+EH@>Q1=ReYiG=#K3S5Meluiqnn(t;oZ6#HG%gNB`XtFRI)(5bNNBQ z2_&t=4vInsLd0;+pyz`ws0kJE5LgRj&4S)73JecmFnvgI4Lk-P?=;&lBZLsKJ*Aj6 zj;eR|Jy9X%KoQ~Zm5ETm1ZtZ+6XhDuju@ea2CzfCTda%VmYQq}hTn?czkVQ2y$R^< zi#x@p^5gfBI0U3GK?&C9v#k{|w z9k!!3W+oxQE5}Ur=OzOKF1%teWJ7cZrqkfwhxQ>G3}f>R@9TS~KuYoAZUV*e_Uy5zt(hpC zWWbZe6;<$0N7VnF?-+VXxj(~+?#<%_MFxo-Trnn+v_qLKdrh{mjhp!>;nym<_|{kG zEGM8Zton#w(CmKLWDQ$Uc88!bcxf2h{|n#LYN^pf5giaXYVZyNE4guq!z+p|Y*Dy; z0ul>sxm{2g^di_|5fTPDNyopu$y@{C48%X$iP?&7y?nilNEr#g9n6dwnq4?*A0%^3 zn8e#ODdXTd+H_W+h$^|#V_uqhD*NZehuIy^ZVG>s+ZN1yfi~>cU2`WP+8&MeLdUBQ z?2ra<-OyTe>NRw)if|o<(VtQWXH132kLb2qZ`4edQLoqd#=aVqt#2`OLL!ad4fl1~ zSTgrEh=LVML!D!2zlx5YhcLGYLi?1tqYX=X)E4F!?Q7vB8rxr`DG?&HA#No<^xRx7 z#g<(fmF&36vdX9XpRc8u2nl_KFXZe@ z%vMnr!Ww=>D7+Y#I((Yu*d@Qgpud0RjoTWPs*~g`>1~goHWF`e%?;LVwdm(qqvlX3Yw}{Leo}z~jhOxUz(H^jsf(UfTXGrGAdjIIO=Q?xcu*%?BTP*wXWFH+-)YYn!Nw-dhjOv zn-I%2YFJ+}L#(y`QI%Qae3NNI;l^7SU3+drQ;(Eac3Gk6_(I;zyRJWfsJj7mH+B^M zh`Q@b{%P>w9Exju@lEow(fbD6ZUS&PvjAN`ZsH}_HA!^tBoXbFm@DGR@s<-)7uskp zG+ivi5wulAWpMp^ee9ct+R0H&+LeEd=)f6sb_r|&S|uL7LxroslJKR>CGkIam3Vmo z#Bio&togyP{1`{%3y$N=$AzMY`BpioWQbSKQTSJ&)obD!Ex)*jco^XkC zphKDEWmCQDYbec0`a}Vu7m_mOfc6`^iQ(P zz|TOc$WLt*%+4t6d=AgQ^|pF$Fbx#3&PH*SQlR26Y?b8HrDPncB+tztJSoQes)l0S zAS%%6ca|twbY;P=n_;U*o6eS=6_b8>BRos{&IRCv@uIuS!)`#&wT|U_8o#22V8eX-xU}DvEGh_PMPZ^X+SlNZOv#?V(o z186~&jnMGDS~k!=D2F($FpjT!R&spTHGX0_VOOlstbcDscQlaDR9hgabrIsV9;H~M zpU3ivDe~V6FY_G^%3XPNY$J`^95?mLtJG?F-2VZ3K!v}J2(^A5jcbj)R`qOb^y-2n zx@hMmLW92&Q(Z>sAcV-sOHdlhyGV>IQ1~Wo(8(P&kL0Y+Qh-DJ6&h3gF0b1~3iIPG zKmhtSmDsyNlhq@AH%TG;_npuB>OsS?m>ZuE)irdG300 z{oJHbr6f82pYQh%zfa!1`Ea=RcK;+vV6`bH8L4Y?*8GY`jB_li5`*T-ESsV~A#DJ} zD0*#JU5-wJzr#}=2kKmr zw&)fj|KK#O@AEPwi5i6-{6nt0@&nTv*bj)G*HuXbv4=o=KlZkC!IgMq-TG#?xqvx1 zMsYbAaM_r^2uEgpW49u2a#%odpdz^wszO1H{SX0L21$z2{ft7vwAmDdX*HX-L#eVS zFw!-!9>sqo{Bnd+1m*fw3iauiXd3-6T!w!7d^cnU$w<{`t}SI|U9Hc&ii+k;IYp`t zztC%0D(-|d4ev*;#QfHo;5)Q?z|Mr*2E;`P;}WCmx&k> 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%0D>n*>5rXVVLO1E(edHVV=AHzfrN~W5uVq3? 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

h}YdkNPzmmE%#k6LG2^I19kAh zj5}BB>~rBiwd*-rg-c}pfYk!f1BBC~W~!HzZsrNxDc&*23~ahn3%ON4GN2Pq>CZpQAQ^y%5~72*U9jYAJwVXcoac0t*PNv=GS5j7bG}&`71!-L zDRBH7q#o3d>vnUGg3&s@umts$p+uCwcS({e?q95=`<*!)XC~$jMFuny)5ap^4nxQr zjvS1L6tr=aLbs0G(*ocmWE*mG7J~Hcz*?i`%VDxP18$?og~MiX1{8s@8Ewu0jNUP) zB)ve;m?=#{Fg&qn{J~ zeU)ga*%gUA{BSobtlLv=Jvr|W>UL|QJtu<+K-?)1Sm+0XXXu78G&W6<#TJT4pdxZ% z4Urmo2W{Sw#AOW-_4ld*n6azSa!Ez_hq}}}COksyChymPZET~*1tMbUf|!T;nW;wA zYri-?D2FoQ48%F)>27wD8<9t;W`v$aBM6(cs78oN?T;aF_&wZd4nBCBI)efQb~z9e z{Z02XxY0EJl!+CNxKoq~=r~@HueP?9u~^UjMKq`mXogG4Z^Ax$oS;HmxtK`=n9EVv z`ual}M2EIWVTr;^Dq)%vuY+b8!3$|Nl;bgEp&5Q{K>ZAqLuH`7e@1}qk6PG*HriXM0ugz2r`pi--Ubv3SrJcg95$~u3ic~EDu{m*BjDf&o5MKG%avQaP%1{I~d1 zt}iwyCCB~dKe?JSRVdZ@(d`7n!4FIS9(^Sxu_qk>b6tu_9G&U=pIAV2%*l>=%_(P* z28-M}#33!+n6Q)_7M|4K#1jqQZMda&ly-&#sPxAQGT7>;MJ_$!cMVMOi233|hfn`x z#*$JA@NqABve_kKr6&a@!{Y=^T*GvnDXVk&jz(4-;5J8}Xt?8E2Oo?0k?rFeIUk{U zBCC&#!`u!g^9D~Dm(X8H%iHNrk!5x+vXGJ*`b|)z!Qx2@#w>$Oco=X9$n>L`FqJ0b zLx%p-^_v3Ur&ev8;q)U7ix~s1zl_nclrRvJz>z(C|Ebpe+2uA?u@6~g&iU;)=>vCK zV8Pg2*hti3cM_=2n!O-d04LhU%T|>5fT1I)Qjo0b<=yeRa^`gB5GRJESC^sBo$ka5UVBNP-}v@G ziy%XmD}DJX;(>_m*x8i3o%7}N#)!)Gx5Q?{3!9srBQ^$ZaZ1dH7@2x(9(c64T;Gzh2N z#XK+uJ0p^aYO^%-8Gzf$VD+cN&iFlPN)EdVF_qVn*DuI-eMuJ3dvhT+@_f0ipCr9m zEgRyks?^)mVA&5iyJ+{aAa#(@K+NyJl*`I6;b&z6{#NdnYX97lnm-w%)S%{fMwS-T z`ovwzR(2aw@EP+>@Ku?JKjvd~B7XbJbVr&O^IHhMtVVOwxpqj@3!OauAqzR;Tf9ExGPRM>$av z=%c{r>ruuw_SrdK9HnpXIY~p$^utyE^qKz`R`&y&EpV7 z9*@_nWpYT=MIHM3Y2OV*O*;{GTb*_w8d91>3&g z^X%>IZS?kHG>f($bB0`ezi0JM*?-!o@%~j+`PTH0JZZ3=j_c+9k?S4mmU#XKZv|EP z>MzfKX0ZP2_hw65G9J(GrHBXCyRDk>vT|2;o~~I2vPs>g4?YW`+Lml@R}HWk3T6!q6|#GyRw7N%h}W8rnEm z7#f+nSQ;DJ7`ptjAdOsIO#eeLa#b3P`h`LEeo=$z9|bVpwl;)^wvD503BeOmAbaOT zq>>WHHh1XnV{*>jvF!?uEnW0xmXp6S!WhW#KcY-xGz?=90plqVQfnBZNdlq#rO};4u zBAIIbB}hC0G>tKaF>aXv2@2*L$wBMjjhe`jP}==ePP~l2RtsX*gFxVcOA)hye2;;t zeTWA!`H(?`3%4I!J_OLhNOJh84BSt#hIAngVTi#%9^6T0-5`7ctxQV5>Kx`+sZp!$dt3nY2+_zh&!$o8EHtg2+aJ9lW$1vs`upu5c zcP4u^zw&1O3;e})F)4l5|6>mSx2T^7_|egR1sw(l6+uK6t)w#f)~oIji^w{slpaZ55SpZ{E{#wHQlvwm z%CnxAhBR(->^;ZgnU8j^W9>%jY=pYlt=SBBj?X;(`rG;q&1lu8lZP6RGm2Tya`16F zJ9?kL&ff1wlMeF{1sNG-I)GfJlvdF}Cs6d*&H-5x)|UWOOtFwqu3V<7JIY{604qMw+^);LPXdo7`4!eEFU6}!k z1h1{^#ceG(ukBM`y>q}35GNNaR{Z!Nt*j!o%f?sAC8bJ>{LFh1j*zuWv1zCfE&K)l1ZDp*b#H7lg9S4Q9n3hr+I-ot z@Tpn(#n+nxamlpA9NdHbuZhC(C-6OG2l%nS0ct8I+t0~&E)Uc!W?bXhM|+EKJ?5H1 zn1gu4`WG81LRYMQ1~XKEze-7v)^*}SV1X^Si9Byzu*8WA$zFoatlT98e#qcm z-UC{!mI!6rjq_F@Y7<%$j=9v7Nq%5QMouT0?D&uu)XiH9ot&o^9v}27H)hK!l)2W6 z2@Y=`-DFhZQQqc(JOpv$DS&8S0!t591$E~p(cj)a7@60QEU)S~-@|!*EIcAr2Dg8N%e5|KRe?2pRtJ=Y_cn(0 zq5(aD{I)bR;Ay^*>7v2qJZ{KE)*^Y*lfSlJ4Aiz$VmX`$l#nPKUOQd?&@zg?SS%EwBt=w2-BKy>L|GS=;;;`b(0(Z>mtBw?RoqT(rxWw_oZs2rj!^IEt^}-h7 zA=y39Cf~8_Ev($0Xgu5d?|<$R+I+maroW~|5BLAGOW0W2{l}JW)nzrs3PJVe>dS6b%tnw46$T59}kTIhC zDG7%XOwd6DlbwtXq)uB9xyFjUd9*+U_{KNU4sADJgH_}y1~p=O0%a@(Yf&&>v)3>o zp)wOI0O{LeY~{1@o^V;v!7cEA{^Z99+ERv5mpl67gVvJ%%Qq5>L{Qq`fP+2T#EGeL z336U|Z$?VV3)(gzJ~6iWDAwG&g2J}P&XIOnAnIJ)q27Q**WqV#fc7u-;_cX!@LswA z(qof2XH`HcDblG{L(~#=h!Whd-`!Fn743G5s}oPp5#7!0m2Q^H99b+uPuZOODZrOt zS_ElACs8emX*5I|+^KaZN?+%~5fX|=SR|zR<4i-2;eQv`N#BM?5mB>7IFqoeQFlx0 zZ{loFUDTr8jz`$FUx&hVvAYJ*uZnLaN8vc%hu6aJpepGSSu*ujnrhIIJm1Zf_k(*A zT-_>cMf|RuC=X^!m*+ISxz$=VatEwTBqY8uXFh&Fw=k^-++~cdePY8~JS1zaPvQSl z){@_xXU$5P){Ap*X>9kv5`$}AX4g`G%FCz2xy%Nxiujnsx7;8P+MaVezLGkVhvjOY zr_N^-JR+hZ8VX5%vDf|fwcw28kR{8z>TJQ)F#*!21WFPcuaHHYHS3NRMyj**nqvucpn}d`&Ca1}JLewd@ z`RV&Gv$hcI1h==yW*xdKH*$5*q8NUw@{PR`bbf?x#s8geT!Cv1hn?UQs> z>Y3Q9-G|EA(uVF?=drlGFra3Tw?-b!|36I=-8GBq^q0M?LkR#t@ZU~7dj}UwdpkoL z7en*^T;OgsrQ?4|%zgLu2D!m&0S82q$-)UJ6hS8&!pXt`EDRQjdeDg?=_T&R{`;AQ zCW8}&q11g<`|64*poG%bkt@wX+0oTSfnRenMCLVOA|)A&qK!5r@nP=XPn;d9sYoS z{Hvt!BG#nVZJZBaV9>;FUC6ZAJWj0Rwe2)YX&Nt;4jbo93AZkn+;pzaE@K`>xe&xC z6(WB@qfEHT;V_O2M^2G@+H}IqMWI=Ot#FjpC-B=^GnxK-xtn+GO^Bb@F!^TtLd9HJ z?ly0ePPY8d-J1JF0&d#Oh&xZ4>}4+T6`TtYB+7Paun}osne04NdO-x0tP1qA%5^=I~>f|c#>(h9cGya*r8-J%cM%QUN9^pE$N=Fj5ZSdyS?cx&M6I0 zIC<$aG%j}wAN#lk2Y9Hr`O+W0G}k_rOt{PYZqE+ z4udi6Z2hv>ND72=ghu-eDDl7#bhO=__adKuV$S z$vZws2b(#Gm$9=oSu$Q8K*^kIXFC9W#^|O$2}B`AA6IXX8LrZiN_3`<<{k3|gJ+N) z=0lmzuL+I|k7Z&2b^_bB?Eu^!Zv ztOY#b^$HdA05AoW&vJ=ufaYBUM1)8^&TgfGdIez6x`nrkqSc*$sJ^cmrFduOq;kwj zP9Ys3&5YL|Hagsh&XYlP*Sc*>gs|e%<&F~SgL8do^GrlavQB&LPRNDsW9;=L_DqJ|Glxd5^g7qlXqKm<;wG;{7&YaoF0bZ&B# z%=rvJmNZyP&<8_yX#_PP;6O`UPt>DaTIX(WHO}U#Flap6~^^I@vjK-xIU;!OWGro0=4(+XbAUD{+y&sIAy_0yu5h2$5cvBD1*r?;`7 z|G2`m#*ty^Qip^}8d=p&7I35XrU#NLGBdq%zDgx& z?j62=U`Jc9nAvNfjY_eXj}Vl*O^bjcq)At0F+NL>lq#W7AkmG5cuUDL?qO`H2;m3= zTL=byQilKuVuT4k{1dtX>4Sj@_Zg}Qkxftza6z&Hk0R=r8~ri!#v4vN6J1E!m;h#l zZ60dFrxOj1uzVda7z@K=29^N8+H@3%4Yiys8jI!n*(Qd zUr?Dkz)5E>Dc^v#H}RA;yaMeP!KAkh7+lF~{2}jK!Y*=r*+bAh7vTHL@Tu~#au<2O zoFPPBInfR0uD*h6>8`(G>*_APyrJd8kg>(oMxg$H_$rqu${C7+AQnY)A;ZY25)l+V zo2bfKsOPmD8Gt&fIM%}5^vYA11pO5mk7d#4uX|%WGqm=P@ooFrT^G0YFBs{RHB52f zW;x@o>Tz4Bev9812nCU>pqYDO?v!3WtA+DbTn-o$RqgTW|2Rd*=xsiGQg0toJ5h{k z%kzoaWvmnN+$EU}I0j{Qfx&aY+(!5%t@Hk-7vWL-J%rJ?wvPH)s7U2XyK+ygIA*V- z{)oO`v1VD)6~0^$$FfLW;cZ*&R><$#r{-L>gYJ$@#C2R?<|T}i_wD3Zr>B=B^^aPn z!o6&ZQ?T>^h4YYIFNsWlgQr?IgVSo%n}8wv)Y?)6bk@}Sd-bYPnfghaL?`$1#QP@S z=It^hHuUPx@FQ+uwfIO9TI_1+jXb-FjP%A?ONbMiWQW&k^YR|A;qE8LN=J*OP0tTh z!IgRT|8WfMhYAP&{SGbxwFCgb{clI4v!R`(i=~&T)9?9M%lqSw-JaNgUB9v9D5*ts zT4H&hYqL4;n8J3Svo-IO{6-QA<}Qe|=!ZFwR3!hWe;?)zx;umPMCU`v)13hTEcgSY z;lhIDGw(bpT;`cVW0D4vjVtmWPPFO6>-Y7?;v(t!;5ckLk4N7km%4d*xpg7?s1u&q zq-E?5&&*Trp2=mULe;_5%eMW$m!o4M4bvxyk_UD}EfdRuG|fWbLRAFSUf>tahsW-W zW(VQ76q9^WoaW_H!9cP25U2vHG!mZJ0FN>tumB!2Lhyt<@OuE&M}jjh3KfTJvu=Ts zz`ch6KuR#kuF$Hm4dO_+FkZq(!MNb{)O z!~amVMyl?Ds}oLfuxaw+q+Cev&LrDTVf6@&GZIASNEiS+fbY*bhw|4wqz#lnf#k}i znpN0iOK1+Mz|*Rd0nDsO?>H3tG#WU<6!XE=i}6KZfU$OQ05`H`MT^!H_&A{s*sDg3 zYk)?T3P;W95@!{Oedd8clh~l*uA%zH*MQQ*1oz~;mvxx`!Dk&WF1}n;Jq(t~oh>|o7Q%K7t-B&aNr(08pbzc{ljf zI?Q(v)nRI=z%NZP%O)B)o+b86c$^{Pgn$pSqro?KjFUakN^HvnK0@;zH6iiD4J3Rt ztidBW4FIoS4o))F6V)`Wh$D{RhBLC|i1zuh>3dK0Kc@VfRQ_9A{ym@f)bBCHdyJfS zG(vnyGz&d160uV3)b7)NhS%At>(#<4ia!6v5L=p=pTx3EmM1>>yDJ;C-y5wBz-)gs|up16S1p=(q8V4f&t9LYijF zp8^k#YQe#8$5yn+c$Ak2yp-5_oSaHJW-PL7Yf?=M(IVcc*uxtxs8Wg6TLT*te;+?EQ*FUfw#23g-A`I}To%}9 zRxxj}xjhVg@Hs>{NuG2|a?!^JzBTeo`ngO00IL-Tvl`@jaR{?jMW2mhvYF!2{39u9 zeIoua1;d#GCYd(i6#-kODGe`gy9SP?khP>LV3da@ox7&NTlj-_B)?e@duiyGK%*L* zDG`e^M&!?Kl88k@7_O#ol@d{JF=~Z~Vq0wmf(J}_?#lf3u!7z-_oXPw0b&Ca1Z zfJPN+$)&x(7{c0uW({P)wrxQ(gVb||_RA)~*sEzr%T2UL3$ju|1RnN|I$d6_b8ldlQ%S?(9f@DYVt zw>U2535xB z?pAcBt7Kto^~_0dBeMs#9K{9rlESjEfZ!-D#kK$zoUF5$69PEm!;%d08yAo$gx$~`1j4q=t&AB^`_O_@#3Or#kOpzCB@;AYN3|*i;fAGHgnV`- ztTQ>sjQetq>1jGN5S0>VHz-4K+tC@8jEr|4#Eu%VjCvlW(D8%39dhzGh7$*PKBvPG zS}a}D-LaDnP-_G%&$`mYMaWtPljI~+vpRKT4?f+&=j z2fgNqT|?2(jOu}t=w>NwNSFXwS;AGG0;bZT!BZ~f6F(y+` zkud&A?q0+L8z~DDnJ6s!OV~0C4e7844MWmh*LSd}d&&OdD^KZpRJy3!Ea(1U2Lm>9jHX@Naf^K6E^8`9u**KB5-*{HfV%( z*X63qV2C-UWaiaq5bXf0qbRu5&{DW~6xa?B%z)iiQeZu!XJ!X+>*{f?*7dm)>~SFK zoaZZg%ynzY{t=^Y&2|%l5uR8Pc+Z!-^B6C>ogwI=4dPqO(vmh^M+id=J;hp6@=vC zAO8U`2!d#r?YQBKNQ$;;B~rr%Pk}Q_TljM7!@Wq8&Pl~AsSk20gB>#90qNsW_Bos2 zHDyP93)9h_kgOa`iI}GKFLio&y?@l`LT2Xx==&rG{7Q!&-uc7Yx}}8Xd!IGJ_0!t{ ztQ?0g(BUW_`UuRTAVEZTR6;N%gPf<`1=*d=F{VNV6In291j41sbw@KR;cg}cCQ`$5 zQNoUJQ^y|Mv^7FNmqK^P~)VuKHlm>CUv5qV%YMgVDNfwdOs1YF;g+db@R)mQ@t8Z9AqrfM-k=s$U{C#i$IYHK5}%PqL`LSoLF&-u zqScEH!FZ3Ow!au&y}c3;+J(WjV`w<3Up*I}UNfKZ5x zrvPEXcPaC-mzmN_{4c+m#xqsyeyU{kMpsy>830lTqLregpf9{CnAQk4TSK|T4dQb4 zyzmyp>&MeW(#WDZcIT+*4;&Zti9VG}ucugD)vec}!x|2}0(6CcKzBHDi>4fTnjs-- z$q3bmjwjW_Onj_fZnUQmbCe51vZB9ewVK1_Y?t?WI zTfuy!EA(!$Vff~}zYK)G#QeYjWf|L4$(u76+>HiZS`vvYWy5vh>|oklXPQE#sP|4# zMmpmcO%jk7jdqhuE7XKl2d5+>HM~rMC_pqOp0)D;9F?6#5~x{s7>yFJ7^ePY5YlrAvEI$o7J-ZO3Uw7TP%%X5&vfad(GQy?tUGze~hCQt*R>y`a*RZ2gQ zEtfr`_+jR1bUG`FaW);1nHcqsdF`IFS_`v*o|#Q((>5S2r(enLz)U60S1D8oRRYT4 z7>M#JcZuLl+cQ%&-_zB=6479MWA{Lhge;5UBQ{jWr4l>u(Wlh5KBjw?t3Ec;BSUg- z8Vm+|m6qK4FZE2MD9lz7b3ny)d&i9Er1!G6&9ka~7wr4E=H-HQLd1eRZff`G2Tr~s z0ZLtaL#WLh7dG)ji>px zFLb&jwaRL{cP$J*E4$+$a?tdWF<7Kqy^f} zyLA$L=`qDnMrp3t9!pX0vJAf`Y{1X%MR%eB72%;LwynfxouIbaXL(LGtIgIDue>KS z8dpE;wr7{rGZ@^U`WDrm6zETkH7IJVfK;nI_7ID8O_LI>aP6UFZI*eP)didRiWgY1 zPO`oMmk^VJfs9jDdt3p?ciJl>ja<`$v8HV zQ-lGz6AxZ?Rk&Zj7vs#DYX_u;f-7o9U0yQ}c(x(UXY}$pg!iGX_0!X?bM^tcIy2kX zx7FLey!k)QeonsIJ~@0j?JoES57H8T9WVy;vrp)}AZEbuZ-Az`gMRZ>z*%RHHV8nj z286%ip5yv`xpMlSkkCP2ex<>@JSDT@gVd!1gPGpi4zde6*g#ANwVq7=N24E8(SS!T zN5VjlUmr(5?)LQb3IelWnhJW+OoC!F)906&mgX#2x@zhL6#~JXQot~z8b~J92l*Zm zUmZNGjy7x@hGKwpGnAnNAhv5|)5|&o+^)q;D$os^Cb0l0#V@J7NLz7V_#myKOk1CmThFAWdPozp`Sh-+jZY;y8V4U zFf{{_SQhbd^w&*jpK{@{9{=ob>#9j*!!4@({y>n9%PWL?*`!qGPBxKP0CV;9UE<&S zs|fQnwa!?MxjmRF(;7 z&`U7m9voWqq|ty%;jjT{B22(D*a}Q@Kd+Fpm{b-P3S+7qA85U$?A`d0xQW~G#!k_h0l=eE<$zqeUxKfCMht-655&PQhVd=hjC zom11IOQ<`g4O|2rPN^};2VO?W35laGq~5wr1;Qe1NfSwU)K@k8F9f?RhNF*xOGRAW zaWR7OH38EnOj12G`4DL(G4%n9wjB+8D1aIx%7i{g*85S%=j-|R*zNDr(UAk5-iy&@1qU0@na9ACo6(Ka2+dO-cLwUh zcV4Ig>7jdkqP1(T?)N=)dY)&zcy-Yxy;GoFVVYkO+KvJ&`{T&LHf+b$@8MU z&9szG!;Pp1beQ|6&C3z1Nnd+lCp3!bS9N_ppReE4rwNa_a766b%gGiAO=(3C8%q*Q z4%DMnCk($8^wcdZ>J1x^(ms$*SfxT8fQXUlPHNmAS=Z|K9-mIE!|jVQ!OGV1ks!Lj(4ScWDXOn)Z2+|Xeot<1Ly_Bb%0hy9F? zUq@6jgR|9=D^Y_E?iC;QKtw8prJqR+_pR3YmzHU!-X|v-SLbagcnHf_jvUk9tAA0B z;GMU|dPOo=CxwX`X>Q~%WEST*GMCu_5ebeK8rA$&{7Hv^j3f!5ap=!#witfg67fO! zgC=FiWEk*)2n?(ig9M&~(P8V~bm1z1Uu^EDK)T(tdnn0(*P;*4o7l-tie+*8W|U zO^0#~^|vou=A1&~pf}Q3WvGEJu8pKB?;vfbeT=Jq=Bv-jkEt7kttWaRB~Hhp{Zn_4 zrI;Df-q+!&$1lT`+j0BI-7`%1j=6dXRA0xn!u1K+gb^D!Dhm!?L)aEDi15u#e@xI- zj^t`r+Tkm{Z1xe%d{;bv3!x*@na;Rl0fgh=hH@rWO1^=F=YpYE5_adnFzN6GPm1F| zHcaaLjCgHggUphbBy(M7?Slv#EcT{=b?Qc_raLW#Z0NdU)rS+zp4zK=2fB~)_B%2LM4tEXWqFeXUEKD`TA{*y#i^1ss zL>kQXLOt%8P=HO=<>&rxY#u@WM7E2m9vQ<(;-Wi31I zBR3%#?yH|8X<(s63NPYjkI3oe_o>l~0U8gk6v1{@OZ^Ho$JD`@F~U*_jaj9)%aLY~ zR7Hi$0of*7qK(Xeo<)vO11EElbL#OkWP-z^_J0A8KySb2qsM9l52ZdBW2u_V#H;`V z!WrNoO8tK>aFwc?ru#}+x6|q2$sRp_tSUx!br)lZIi3(GWoT-r6aBn^YV^R|)H5?* zLl{3o$$1@Tcso}=N$j`V4Y1ERj8rME@ph z&nrix(XW%Mvy*3!9?!2;vuFH=oBQkC?0R=UzPeV|yPMtS9j~X; zCtpDm?)WU`SjPn1?$H!au4dRUJe^(rdiLz*$>bS4-+yHq_ch&s@d#krRWwvGTwOgW z6CPAC=z1m62sjU&R~;iC&kP-=_vt{N74fDiS1B<>5NW~tRj~g$I(FlV?lzX&D2}Pt zbVLLJ9-o0LKCVUtug}K)1%SF&mB&I)#gkP5!HK0;i!`AIIJeLW~*1TA8wBC<$4LK z6&*i*d2$^+fxj=!KiAWj_t(+?JU#j5f3N2+tEWkr+;jNv?p~z#Nphcu_qo2$@9rPp zpWI*LISZ87ini$)>R_6E8a_g?l$_nVX$854gTMcuKn#gtZ?Expi(%g^f>kPgf@xrS z^nXxG0|XQR000O8YNt^#K3?z61aO9smFUXmo9CHEd~OFJ@_MbY*gLFL!8Z zbY*jJVPj=3aCw!KO>f&U42JLe6@-eSHsTrEbwCe8;O$y0Jq*YKL$T>qoh%uW+>ibG zQMTMhPCH;a7?DUmd`XGvtm&byyU~vp>l$P~80$eCol&F5dfpe%$_MGB(FKfJHm1c| zNsm@2$5@Q9$XFL}F*tPact`b448W%b2Mdqh34t=)gMB@eUg~t!D0VSQ!(pKdpJs?}`=;p#L)OF@iby0R$L(D`OKE51w z#)3D$Q3vTK<(BVJ!t&2UpiFUl9(MeYILI#g&;{*CxW5&@IQ&d}mK7S5y=YtH?70>_ zVS}uopnS7gD_u#In``*Q5;Lk(U6pPpRU!Wp^bg#Jw{hC|SD%1-tvh{NbD~&VxH^6~ z&vpT~=sXymI@0JF!h18NliFL;j`ZlJo`h$JmaNnFF?`7D44nc=AMpPw=cJNz-D`Lg zN2Hz=W{Evd^FB*du`x7$;Q^mT)8mgfZIS+yG@QBeJr1y}RX1;lg#n>9^0|Az8gQvo_$o!am{ihNW&e9)RNT5N-F>Tq8kt5NrIH- zkv&nlJQ4{sfJURy-Dq^ri+mZKoz2(Px*%s~QLjm-qFHj4lu4eA zqIH%`^BFlylZq5^THc!jh%)&i>TZ(i;%rH(c$&s#If||lvbiMN^4>jOpxUko{e|@= zqSjfGt=3gp6(nAoud4i#WXVsYFe}m0n{^SC6U?Ay4wv>}|Wt3-@p zGR?EHinFQ|Z-@6H`YBiOl)Su%i+DnvUp><<5ZQ7zLqSw$8_T+!M2 zkSAGHH?WMWi)DI3zEs{a2qm8VYh6~a^MWLc?0Hoc$vK_-l8)2&>TSGItG=ZTz5TI% zJ>t(l{p|gTV)Y7R6Zvr6US;4droU5hXS{JyBjn^CTtX zG`S>Ee*P~qts;t|oR)wl?;)ro3Kps#Mb!nV;m}em)U(%DKnkB&74F&U%oTrMD4CtJ4+QBuD{+zeBE9px(JxE`Ul7W&x@f+n*HB7CgB>PWV zKJ<%Y1qpJ{SCS->`%llGRdN*x%+O`zSUlgqfBDz9uil-Uz5e+82l%3`m2ba3fB4;9 zTr0i>A-~xRi3%AP`Rr^)ra9f)@}ibHhVykcg>8i|2YeWP&kB;+5JV9E3O)er>?|Xj zv$G+<7_kb__fZ5wM9H6gpG+&I9(?AMZep>?y~7&vCnepw-h>KF%W`N`hfgx6^O~US zF(pG;d&DHA$!k8A6TQ>wr*82$5<{;69p1Al$zGjmeg91B%LiH|K3UMCNjR$1d6w9x zv&ZH`At1&pYUYItkF68d`5y=+ff%I{q*UNI>nTXBY>^=gqOxO@&!gcgFU#aS-BJlC zrSr2SvqNlbQj`6?dft;Pspx1*lFk|9J${#GbTq)zrq%IlD&&%8<~yT{W5Eh46C_rb z#=DMq{{_opm;fR#@-zh=icF278AgI5tuA7as?#(&cg+czO(IyQz$mpv98D?nKc@_O zO65i5R2N0QUR-D}By(z8{z!fI^Z_vp9YsZ)u&ss&agOoen6qLiQLQ^MXyORC7wGRgEJO7lfY5kx0I0i`o^_yoyBHC=1hVO^ck zt*wqaYKumiz{&|@h2gcbAxPS=VBs5FGM}ors7_+Q9?qGcBDEKkec@ABlzB#XzBMp> zT-Y_MQx@*v-|M8fECS*Cr?jHsyOD6OFX! zST?{Jb)m(Ai?Wq!K(Qqwo!BV1iBUi@9y2q{CwQqrAgQy>Vn2<>8_;;dT0K6F?ypjc z9H?@?Z<7*&tI?9!U=Jb&DNgB1oNa*_!_e7NSLvW`xRDK?6_tc+LP1t5z%aghIyfhk zxspHAt#|O*Q-$J>@{J10@X2zmU0jH7uq0jeK&vWqu+Uux9Vt?lptRm z)2)uoDK*c=S8Kts#ww3XwpGvyurC^~8CE{N{GOt?p%_&kmoV)2 zf1&R|Dq!#bdsXBsiifRaV#3VzitgWF$u#*H6$U_QKtUDsd0`^O4S&XDSrA?0lCdKy zeu1twWBNTBeC1}A*W1VuRR)g!2_63@3IYIw(jEwKGeW^H0k6@P7-EU|POCssI)2b( z_?~%R65xG9h2q)?;>mbs5j1pl0>cy-Ieqwf0$Y5|_>&SS!?d^ZUT|Sm=>hHRkUvF0 zu+|50MhAeE$5bdAQ2AjDi5U*BgGDW;FOW+>>JBLl{RgBic z)<{Dj^q|p}uFfwrcDtRYg$RBEHzTdI3e1+^~2S)Z*3Sq;fLwz+3r6cktQ znBZ2+p_Jigf~-!*k3LH=%X*88%N~jW{9VnB)W-^!q7A5>35Mzb=!aziLwkyXpwj`T z2A3_46+Sj8#BF(F`?f4ORGI)uXYhGcw+dS75Fb;aprkDA=U)yFolsQ@abm|UvGD;6 zQ<4qc(|8C2d#LWVEqIrEOAU{5o_L*B_1gw$tQ(C?Q)%$fyX!DuLoqcv!)dI<&s0wb zKr(~RxR&VZofr8}Bm*ssZk3C4ySjiaW*Mi+B3s7ACEYj=n+C>dib5J1Tyx~)rH9Sn zhCZ~rw)Ex!TMH*pqRmoknq;**HKV6r$djDEc7C3%| zb}n8>qqAcCW5S}WVo0G_VM6sLlFf!S3(`qLZBH?u2ugnc+OV4>b6fjoS~K^1KP&fYA42P)EY=LK$o z6lZYQ4h4Qr_({J`-6yn2j<(`BtqmTuB%OM&fh^Z$g{N-yM1c*r# zy_qv-vUx&gBUVo~z|R5EpfWP%aS<1@6ecpC(?zt6Uc7$`!vk%+0Lx1nvptsXz3b@( zIBq0xMhC~ZhB=TauoDxD!km8uIHtYIa`s4`!XeCg^#7=U`-!~);}1KfjPgu@=l7z3 z&kY>BIZ8}+abJSZo?MtX!RuB#aRkJd3O~?cC)MV5w~8#cQDc)5B~9we~j0e zILN>kR7n>03-ppvxWPe0%Om9{GZF(<==xa3m!t+se`YiA)Y?u|3%G#jKJJ%qkE5S| z(NV&VLxq4dbldA!R6<05s6%CN^)M6*j4*z2w-$W?p{GM_5*ofbmB2nnzpKI3sCD~B z3qKV{%9qq$o5YeNHJHDLLG_iM!EO0J!oBSE5?@Q&^raq4Nt zI&oWP!1cy-6|AKrr6d%wfI$Yej_LK|Kf{!MfD~PCOO!;J+aVC^GB2xi8_|c9rYY-A zC(-Z?$n$h_&e@q)f-=7Xjx%p|jxni;56PkIo*IRL%%1XQzzswfk5Q~=JC)d!PEKqYb zbQuChnPfgI{n63*(Py1EWwnhw6UK4tz+t5x2&*sgymV&Rc(o=Ues0$D+~OE~I8keA ztfSWrP!6J(7i4;gn4vP*3|V=}ZKtP(uQGR;SGtkFW_xx!T06hfD_r3pSm+&S_ zvdn3V=-O}kL>|crPxFQ8-47osF8`P>7H|m!SVIBM=ChJJvaaC6$#tzRh+KLQFKYR$b0DDQH0Ns$H=2%!&y`Ga#H|5nsHk6tI&M%#el%Io_65u z3?`$fN-#{lO&a*Rg z#iyw632heu*8GQ81?)Obk-7dSFN3%V(O~1QldO(_cyUhuHJ_#n`mMa3LnUMNhOqp4 z2CeAcQCd-_b5Fx9AOmm%D^#wxus#Rr7wqx|GOD3|?Rlt;7kStSSeJI~6R;GDH$&oHO%0iITVUyx_2?z8& zKa+V$!SY80&#l;lL!F};_hcAAc3%1Sr5$O4KpzWvhZoUZySrIh}%GFq3F zBMd{U!zo9N{#{uQ6#e&$tG?La=AkEjtf102cJXd;KWIue{dSVRF2Ws8TmpL zT8g^yjJsI5n^4YvTUt?u1^yyK&6~yz+&o>rh8Dyba?|`%T4JuPQ%aD0%4byFig30m zbGl&SNfy1x*V*hXGOq6vD&kjDcojo&tOskK)-m801l;ZM3fwd)rt35==(9O3m{AR) z%CT+aMkACI&{{#4NExkH>@GkYmB}JQ^Mhg{VbF&!KWu4-vgq;Tn^E*+^6g>t!%IrC zl%CZKwP3^Bg6dUN%!OCCRx>A7-66zi=uH-V*ghUn)(+tQnPmS2MA4y5FTqxu@e4{R zLseC5AMjfme<548KppU&j4VhSe&9-RAHy|4vcZX=(~(eS3P;mmvPt#9SZAFXSEgKi z$=)qtpAmwtQTP-f>9}z3xUDe6OY!Q@bh1-YI2vFGItOZ#gh?n3{KT%N!cMXwus74a zDW2ikz&eAYbQJ7L3l=+WmhNRn??57z?~KkJM!%09O&+-=!~rG$D4+taQIKU)!7ZVrG(h6iG5^NN1dgY~wgxxUuIO`LR}b|)OkZDFEj97NG%P{vrFWBbU*>X(C(Kvqw~gh0_r{`#vTa*} zQncr$VnGnE*o#}-P?Vl&+lkh(+uTf)4!XAzbp(MM2~z;yuv=eDoy7kYMxd&5)tV|S z!(ByTyl&S|)d^NVgqv1hwWD>>uh!>uPT$PxLgI;eF>qVb?L#dno)`tWj#iiB4@@Ur z@NUHJeI@p2aHo*{QEqMcyr5;5)UrT{@&$m7>0+ynQnHAr+cCR;>pFq(D`>d-T-q5Q zh1zA$C6gM->h*sYC5S2Y_=8GFFfxZwqqHQxDmTiki(`%%$tyX7;(`ryH=nxX#*5O;L-uz2(+di33Q-#mQu@X><@O@5?<@q-8P z>?$tvIpvzwOab*|@{sP`8#1H&_8%h3Xh;S@_{so)UDHALfn=W5-3IRqs3YuGQMhF9`*_=1h?P01y3=3L`P|%M1XLOp6@YG6MtA6N-$A zw&cqy%?qjs07vLFj9`d+0)>7r0erp{*pt1F^vV84`lQ21X~l9HuORy1aybE6VKwPO zSpNEaC4_;=?-wKi6%ph;Dv0OP1RQ_6ZW--haJQe48P%8e3$Gw){3R4F%-+A(F+~#n zvY|>dhnQDzV#Z{qKTRGU9f6Ph!#_QIbaYgUDPVxWw!iMul%s{Oc?;iAne5>XEv&W+ zP}mI7Kwu^Sz`u>EvX|YYvvQq9Y;mB%P-@hiSO1Ey;y`i?fz*kua}hKBH=;Ur!HzoN$8HI*N3CKA_4uJk{+F* zKRx{BQRks+K&Za+_2U2;M1t5n2_~pqs}UyYZpe8>rWZmE$bX{DE$+Zy{q<>G|Bdci zr3Mc%SMT{^!a3C#Hj}8w_(|+>;LMBFaa*Q1l~{q!0Z!z*P%MkflYn zu825e{E$W7)=JKo5a;XxoH{_y9$Ypgvw{uqaq2=>_gjNT@W2Z1L=hYB=023kuQVa(oB$^Dab?6v-IA4I<4WHFf~6~g85}2R(9fd(lNWE--zj?VBOQ%&j?x>nbm*p$uX1{Z}1A1xPSw{ zi*AQtl5cPpIA)YTX7EDTA$)>ZqWk}W48n1rWGt5nP@8xro-pL$8_4( z_l+*Qy$r=c(t*BS>h)`b=Zc$8R(PUD*D@GJIAb*K8M%uB5-{8r`f2-phI{QSNoMXU z>Tuq}{&HJW$Q(4l>w6o5*6Zy?5GGuL5a(m@oKD}EcZ>P_?IzvH%7z0c#A;{vv`qK1 z;i=Z3lup#8ayT-}FubYAtr7S6(ANubH4<|MD6~{}TXPf&?{xE4Gct+B*;lP;rKieF z>;67Hx%mOQH5CLs+0^?Vfo#5uP;7t=XTZIbf;-Gz9c!@K$+obl3X}A0?8t^yFfG z#HQrwyI5!k`+md(>x!KrUlE6NP2mmD>Om?DtR3WW+|YQoATs*88gW~+@!1w2nK^O5 z?-LunXb7f>HqbG60$+t!Qv0OT|Ew8Tc~{2F0nO*NDaHv-%5fR}_2l*VTjt=50d_9p z=_RDzM8219!OfK4|F`l6)G&ch#^)5BlxIbMWtmHP$R*~<3?WG{XPg?OLx;hGbV9@u z795DBxQVM#L?BI;o-m6wl`3bY`YzPL_g z_SGeA^BBaYZ&eV3kF%7icI(fFH>P%0uumw3aNjUX!6iC&R0;G_0=EYh(76p*Er_^> zgOSC1t+H0hPtJ0~0VLK$U6lTv)14TJK;}DZK$% z|IRD!qC+*2InY;V3Oy)@Ql+m~=KgegG2z!|sdZU|^7%N)P$3;lBHZyv%;-`32~DGB z&e)?FvD~GY@8eV&B=9{Uq~UNT*rwe9GbYd-?i}yiQW9>t=rZ0=8%=-^8?eVIhy8)O zTqkL$x$z{^=&`LE`yp1wtOBsNPkBq(2y2;3Mg`uN+~q!JU;}O!w(jg)-9b_AAbJ_6 zsR&-pqUT`KJj*X0CV%CV{v`2P5@5}mZ%PUr`s64So*)enlY^SYz!qVuK2WF!IC63` zrRHtOPs%#gr^*ujP>d2w%iZga;?i@WHC>;i^xI*F(Hv9>Vo<{Es4?9-$_G4M z;~0)C9~|&_PGiVUOm!$2vI0=JuB6+s{^yDm+h|(EKW(EW<rY+Yk$bhT{`BzhHCVSnkeINUq_0`!3Hu6o#ORiB&Nq z*lS3ZffNKUY7JJlORdvn~bz?L22&X^v zr`Or~Y~~y7juN5BIMwJ4snu9=^vg8KT6RA8xFsGsrO6eshS4VdfGfn?xVSX)O^c5m zo1YC!9PKvIONME2?BwN5!xfSv6RBJ_Aqe1Q);f%y9!F2K(vU#FS(4Jnlcv*RA0maY zXF_B_a67`{qqI|BDV}&U`dP>z4raJZ&MLN7!vC0uq7wT@6{r>P z56`$V!ptutk)#EqLDw#iUEr}hq}L+bbKidxLGAui&U->r<}t>6{=IjF#K&Ggl(|>= zO2jA(IRp5<$XD;khDtciJ|drTD*PmnZD7b^ZRYdn>8c>lM$ywWDS-f=F(;*`a6%u` zu04y+(|meKm&L{m;kb!2OmH`&t<$9xqbF6yx}p0aapxhwPu#9MlrgjDD5T{x_@8Xx zzP{<_x)TvC5hMV@Rt2I6iI}6u0~J0p@Twa+-SoMG)6kU?nxhQuL4e!(trnQRcpdp{kkFRc~WeebI)^J1WxuvwlcF4po}vzLIV#!rv}VH}ST_ zfUr@vPE+uQgTR;hCbK{=8N5R6*Cz@B{RV7XBX+UnBxNY5nGi(0@C$Nh|{L=f%TQ0m*3zq zVh8JI7dsByLJeyhkdq8xYB3&^Z60|;7Py&};Div$kohs#B%`HZW1e)AYc0QF+{5Y1_ewErw1|cj^3mv*(kU_VO`&a8@JJpMYHBG zPQdwrX&7Xh#3Fn^)6J~NHmM8;@oZK=CPA2+}VvUA)`h-qqw{p-)?jNZ5x|pEpcJ zJSWdzISw)8ta#P>K|HWftU%ihcRW5p*)0GSe10Avkat{Wz_Vfsr2g&0KBJ|2pf*v( zE`F?!+OLxK@jFofcWc;pF45MBwzCm$hjz9IIgjX?h+3JbYcy1K?$*2fywzu=XNMZO zW|sF3aJ2tLhn>;a9X|3cJ$*lS`1``x?7B*FvWk=y&+%Rr3D4@RN`Y$a~D) zdqd{-pzs=P$J)%Hpp%WliH3hHS9eQF+!@ndj5NDm&_99e|KYdumDsRf)xlw}*>=*h z=5N#}7Ge}docb)Hf6J5E&4?Yh6c48ZdYuS*1K~d!@S?$SH(>X{gM9}+Fp3R^jIqv? zyk-xr)G4d|xhaDf^CZU%+b+sKV96UXFZ?kp7R_=}X7{UzMJI_ogKb2*QpatLh-raO z`MQ{fo!j1#*ie6Nl9hi2){{H6_?GdaCB{t7{b(l>M{@X35fUQpz>~NPy=EeUkO(<3 zfu5odH`&zB7N+2+9ae~wdTS1$qUvbh!#BD<>pj-kK@U*MEFaqwKlps;*~{OQZ?YNr z(&PMiMpkr_!5J~nOA^{91?o6B4m_!G_~JnMGe8*F19^G=|LHIT(m#ZsQc@@%hA@Gi=zELNE?haJ|~S)yqQM)V?K@3T5BMM5Qljs$md$H%yCE0HBVpdWb_F_r# zUbC9~neF4;?0Q1KQyNoz|3AZN28`s0_s&=${!oBaie7DPyNpWqO@1dcl4=3)oYZa1 zP#1X4X;BM^Xx}w+=)-bB!r9}>s=rd^<2RBcYH7yB#1A#7etol z7?*<~a3mvi3}JX;ND;{}I^iL$dJVRZ_d$4WIP3#0E?*({?SA5HEm{ugnyg73kNZAR zUFpu)6T=ppu`rm82K)~itu%qVg&W4pn9bRaQ1W!W%zh~H^=d~*I3{-4cPBU>^Ubbf zB56wZ&46X$HKQ>Sd?U?iDwK8~;t<$A8{Nm%$KD`>wCi(0AmNP zGlT#R1V&hFvC}=J-MsSs8=qjcXhS4fWX;tSmkPW^ zfnu%vMM;JB^#aqEb-+g81~eA8v<+%B#h1 z1awUV^foD1DIG}DWgaR$M$&Z=kN5eNvU7?uaNaLS2G<}l{g}9l%u8OxS0suxaI<7S zCk1ntfyn~{34{*#uSj5i8^sVmevwh~(N7EpQ!X)J_|%mDS>V<`;@33f7Z&tl;0VSe zWW^DFlhw6*y1v`DIv{ovR|exU@z&qM%SV4bQD?44?$r7t?EQ^mfPTMom*WNF!i3xN z)MpY1oE%3W8w$7CmCe@`w{LNqFNH_+fisl8($A`!=+g@yYJAF2uEs9;)XvaNeGP%{ zAkLOcg{}1IyRj7{HNwYX+OLB?w9Fee?lkDP-R6IMmLJg0{UGU$5gmXbkYHQs8n1LPzlDwc?x*5_C1LE;A4?{3Rb}o>h_#x zxYoL59UO50JKG$>IJef9a8FXP9;-YblekE?p*I_v2Dqnt&1HVnCASRa#iYwf1XcG`>p zeYpMzq7EtxxVP!#ZEzpc>I2tUqGJ~9W)Lt-brm-~Hqf8esajp)e|MzJoiislQ6Bj+ zP1iGW+`yYT0(NJr4FmTQpo`;r3kTmZyYn2k{o){qLl7M#=&oLq1Oz~u{Fb}%E%?}C z*>XKPA3CWG|rfU8O4-{ zrrRh^7kQCX7faJHC*!4-SD6|wbwkcspwt}F|5aoHdfb5k`B8E8l$kr*90f z;0X2tlgmam-9$?&*3vGO5d~hr#c;}WC-;o1pDMD79%&ErH3jrzp08f!Yc9V$bXI$; zJ!JD~Rel@=8N2hGncUNu>RaMjVh1K; zksD!O1%orYBI5wZjL$B|@G(es=cL*Y!t@)^=O`!%79#h_o|Lf098i{c_eQ8lP;Z1+ zL9^{B;>w~74k$`Ez@SFGNg#5q%QYT)3+D|84pt)Qoh-y_Sszf?xkl%v9k=79V(02? z-=>GaDD1X9R*Ior(rdEv$4h9GjjR@spB?(_FbB~aoGq5RHp`=u@{)+fSEY|B8#&Jl zB;XRJm#ln=Vu6H`jFyOJK%y{PGXe($;IhMMWEqVMRI`Qn+0D1(@Q)TqEjhXCdZ|)a>Wi9 zih=9DA||`DrON4KUgS$<;klQNHCZ=xXc4HGs85=9@jvKwW5 zJ^8vTWdu#6oWx$WQz>u48sm~=cw^Gi_gfq=%2!KztXS0a&lBsHw=>h@BqD^G5 zwc_#oh=t3fj1+>>vP|JZtfP$90b@hoRg`r2DCBK)KLNPZS@<})#aB&XqFRC;262oh z!Ab=iFUntkY?i;)1+0OJ#U&Q9SCy}pd0ENhBloPjS2W`ZbS^Q0yhbt3RT+!PFiCPG z2rSOp2jwme07hcLwb!K0E>{omo!K4`Wk;wk!Br-#Y(rHbyaT5LYIR^N3 zK){rA5mgi+nmCpPk}T_e|2(fM`jrdUJ}R?!9SK0?L(zH#zB_IyBFVr=1@!47R~%0* zq_~D-^Ic7XsWq9P$|YMObGXcxZbP;-;9J_nbK|y~PDCGjTMkIg8Lj2xC(Rw24HuC} zKfws8{Tu5A7ZHgAX*D4ESTG80iK@V|Abt0ze9`6VcvOcqYL ztxIOKm1MpZo@;Qyl=ay8R!?I{ z^1c=t&C{n?`Li1?KcTbKv~KaX;1hCO7vWlHqU10VNyy;A+cu;h8nt&N(BSs?BJ&rJQ(|*F_WC1}Y$W}l`gV59?72iykh zYwOKkji5mc9cFnbw!l%;*5yAowBnL2I(5_F{n)h*+lvr=)%n0p+8$PY=GrYdUegJ+ zesJ_3cZ2MBB$Zbh=ZQmlN1N}B<&&a5sQkdL_Io}ra5Qu zqdni1vAucQqg8Zp-)X!4v%gHQTWe|4u0M;n zk}rZ5O{T1iahc56@IeCj_d2ie*OY)M2ygb#!BUjl0b}$+#Q={YQrADoFv{Gv*BDk0q zQ`RbVp-$AF?P(GrCbi(0b}1eMO)1CXFxm(f31bx;^G7yRaTQz-OXVOp$;{0dQZ`z7 z*d=!3n{i|nqda_))dt;2f#B+bpYVYVUtWMzfv!F}N;>YZD|OzxIuDUJ)Ityy#4ZJ~ z7L-Dtt{l`Acn9BcUEX_3v+0gKyJfHTT*NG>r3*!9G$e@$m5t+dirS8PMZOk2xZitc z!GjN^zNu#~5GI<`HxGB*s_TINV$-&EYO#gGZXKujf_=OG?OVy~+`N;!KsW5=>eWTx zu3CGE+xw$@ZSDNleP-F1Z@0B%1?|R!V{UQI!ZuZrtwY^h9a}1HwVut@be8YD9eOQ4 z_6A(kgs<{_Wf^L#N?C-8n#z{oG^OfJ1dJR-_#ytUXsu^wWtvyz*%^cZ1trE*e7j`Q zHnsBB#K$7$-22xEHPCP&6(mrD{MqaSEf`$0YEgf^wu_KtRt*=ch5ZEQMXSm08~cOH zv>79KYFi9NM=9~7NZ7#!M>_YR2e0?vq0hp{m)}1>dH#ThCBzHJgXni$<-89_xbM95 z{i!V`WY=3+yyKcZWZ((`(;vCa_oE;szeE7}&~Zn?qoXvX$XU>|;8*CW`7~0_d^bi}HPUdnqQWBz4%I;qkb9Zk{JEByBbu z^+FQZZc4b8G-mgbm>b&|ru4x@dLo8j-6+suLS6*TD`C=&N2t4JSt?dG+fS70R}5>eU;{9finjyK2TV1 zN8T$JLC9)3i2g2IA~m-}y3C)#Sf52nbszmWHD8TtYxcmu zCD?U6!*#{u>LMlmMGd zQ_;Ku@Ou^}kTJ$(yhx^BHA<+ji+qWAYfDj5qnSm&@#7)1xrQL$JaBKDwBX%G_MY^t zdo<)`Qk9X3J>%yFkG&&cpF>c?B729?HUewJ)&Zt!{P7)Q`a=^*J6FE>;jUv;2@mZ{ z|FcTBC+E#^e2I3N@c9W&`&yMr4#!PGU@WS6v&>=96f3$dq1#=^xE4T3tnwI-7JQPS1Qnk7 zlLc9z!efqs6qqM47zX=GS0AVl)AEJEeqx@HUI-=DLz3HxFH+=W97xm&- zh`#Yvp3m|;)KTFU#59u?gf`|7(wM~|G&89)HlbkDGW;$l!kdiC&zjsd(imD`yT%z) zf>`>@15F<@;$?U1L6~tQ2ut1F`$7wI{1@Pa@Iu{i%-Eerqn1pIqX=VmHJ4>>pIy>Vm?)P!A zDCwWyUvA)=$?Voty%?H38X-eCoJde65Sw|J#zHFs12Y`xU8m(v4K042uFDIzL|QO8uaPm-`|=1WvxTJ}Bk{gI&v1cW=Wtv-uh7u$9?db~>h}93HW@B5?e}n-Z??{@%IO%yqJlBxs0S8q zdtGy`k9wy!RpQ?MLOw~ER^8$3uF?9~b~5C*A`5d-XWhk>8~e{Z-pGv3S^~kFqBN%} zNF?Hy%o7O8xvqE^I`ja7P+^XQQ0g2DignHVj^<@C{cZ!Z+F&2Ma+_M%VNM$@%EVd< z?U`?!$aHD*&rg__(v&+d{Cf@FMrE>0Qg9B*Rf!tJnSsbmd4nb;F^wIKO1tMpF+Q4w zNwStZYhwyP+x1zQf^M8TYlffc1!YIQJ%d%xbG*a<98P)H4R;+lv~CHzN!wQ2&5fq3 z8J5{Xw^O)eh1<2`)I-&FJ+Bcg%-$1dBpu zv7f~BE2G96IeN4X32#?|s-+wq;hv7mLVG-@0@3*zuE^A(`}TN82Kw4?Pp0dA7vng% zlW{=*)bi$S+@0xemWf^FN6x-lAApv!sN-p@_} zo}Wa|vn_-#kIN{}qKkY3X5x~P3eSR#zPyTJ{(sHYKlC5i2>xjv*SPXgH9+B#{IvE`v#RpuMriyiBD$77N(x7eBYeB_#rMd;}EYfLEeO+QOQ zGz1BFok7~3TGT~P0`8Qrmp|0juf?qC!1ic)D4I+KI~WQ8T|M^hJsS`-Osb|pu6*|K z4nvgr9m9UJA>5=5Ayf@-T17W;J)d^b6$hhJ9EAQlc$@wG#5})HMLxITz>wRRmJeEe z%)NHOdzS^XNjLFUI`oNjk-E%gDf_qXY1cy+Y@7j)mmOd}R7&@ftWHk*SMYcg1Qo(v z6UAEF(Izex8lTa(An~`7E3$D zVsKdmn|Y%Yfo&E!Gg&0w+-ovP?z8m^X#G0$qF=)unpTm#*}}Xh!?0^U{qm+D+Fzwf z_IB>qgeaiql)+pSpH@k=7 z?k!R8C&omV#2rd5q~^=c2hrc_l-i)NY@??SSbn8v(C?@6duat}pLCr<7otPf?e7YnHAo>h+8Y8*)x7La{o#*k~0f zMM^U%#t0aVrh)dcnw2}sIN{t&PWDZQ^WATdQ#pD2w-^URiMSbdu@CRTE?CkqCHn70 zQX5x2VA}~6d&XShrb$J285HeHxxX}+f?AR5jq~h-@z}*?8%+zXSxnwkQJj{!99BK( z+v5EP(Tg?g)71qeMgc0iTV%xs=q9YzJ!o(2xsMrynOABH2uP;91-B8SPb2C~{OV%N z?laQszIpsjcxB22rMevMDrW{!6+E?n9#s)~RLQ0*bH^UGMI^QI!SeW~x2d$(=v4RS zCS>Z{lOL5Tx7nwv-I(4iG^Jyw6Cl>^&71*ry;G2A(bA<`wr$&X)h^q%ZF84x?6Pg! zwr$%syUvXp9ew`jepwOgIWuO?95eG9X*MpLpzG0;4CsK=@IhFv`73>|^By z&&G3ta;jT8X(Alx$Umn8Ur?fI^&H}NY$QdmJOxX$u}+h8OH!Ao_slPi;DHKsuo+k0 z{>$A*-*Fb&2|J#TmG(-$-<@2^IRuim6g&@)?|(-ym|J~lOV)5VN60zG{2 z6%M*9?ue>J3M;RKm7>4jRb3;*nu&z*u#>ZNwNL*)9)NP17YnHEyN@qmJZd?=AhO?+NRn)ucthIRor zt?XZpauv$>=FDHP*E*&BK~-I6L$OWsCF@dh!;xP(4cH=ihZid~JuAeH1-`fPDxb%o z;wpunKj<@Uz=f>-lh866GPhl6HS-r?mMt_1LS-U<-0%NXyq;&Q`{RG~STPxESs(y9 zp{42TNYOfS++(R2P@O2lKV&}p@f8s|IsTG`Z*Lji|0q|R7!oC=p4=sl?TyLBxip#4 z%~+;MQcEO6b4p>zrKPuq0^phoxbDxok_{```MO+MbyrWT?6}n@+I#IcjpIk(6FsC%=20!O^YO=7|QAcN!ZQ&N9Zg^R#9V{n3myh4~ zA`*7E!fkF@y6>-eg8N7CK-mzVx4+76ugf}H2@Ha2jVVRR8uHhd(tdP4O(IK!=J`PIdnV48p& z)6+G}a{MYN+&S#qd|=zFhh}yvHWEII>MKVOJ8`HIIpM{qKqx@S`}m=rJT`z1%j2WR zFelSV;BtRA)ez2)nE8sS2j8}Pq>X>A1{)>a3`1>KBeqTf=7`3YpZz}B$(6NNnODWSGI3H zp(y?sVgG|uQ5W5Jx@oIc+%PK}W6YW`3=yYO0uk3Egy;_|K7AMi-6IxTq&HD`a|uz@ z#y>{H?ifS!A+L0`Ckx)S*;v0L_0X^1Q1f-8X*U)&y}i$yHd`frQ}r)867njie-Qo| zY(Ad@+c%{_;YZU=$YuTcx)z^duLE?_?x`5IHyEQkB7!eNcbByw&qU`R@##5J&3%gJ zRa@<&DW1*fmtcr;<%V_u6sTaGM?8#)ohFy7UxgNOzAH;q!xhrCIuL>6Wk%37)W735 zb7XjsH|-8*d)UZbGM@AcJ}sJMon;#Hq;U>*B`7E`w~p=Mae0^Jdib3B6RNr^g>`-+ zAIFM_=kKJcW*MIq*t_PN8q9*tkL=ZaF1M8&+bAhrcvIT|g>8*yQ)=lCL$ejD$Bn*v zbZeQ6qXkx}^>a&exxOISJFl6#psw{SZB6AVWQz{}k#Ex>=%HleRkw}og`?Wqg;#FS z%J-@CBjea9q8&S$`|A49j^4@bqZ(gY!|SH|89nHd4zg?%5{vHbe}x_hRm~O&DqKl=YA=87WqY-*XUq*Fbw}~D4f$K^ zF}@7Y4jN&r-t>AV-ik~Wq|_KQes&Zf6j%KCgC_U|JlV-DIGTIDYg7?{@bf6nkDba0YK9WC z0@XdgBgZD>j*vK^coghPdKs$C-kci1jx_N)rN4>0EQy3tC(aN*{2dJ{N^E@bY`Ar5 zaIr(jv3!Y_%P*~9>>;U~_*s$7^J#Ot(e;+Oq+$DeBbsi+W>&O|rjT8SW$wot^Go0m z0M{D+_(i4J~>959xU!4|8vx^=uy5)4d!Ht;NVQjZ`+EmP3 zeCmUPBe1NbxQY4Cf?(@4z&l}c4)S{!PcV7T-aGL|s=8a6&|^H1)J^{JxqIO=N?0&t zJ^p#t!%_;R0U(AtJ9r>qH-Paz7d})*t`?1o8p^p#7gD zlwSghg@N_|Puriu{<7O(dw%&u%?wz+Kjx5Ww#`C5nYHTL@Uz8Deq0tp=4ce&&^DGp zCMDnA`+o6?EhJIQHzCKiKjDv|g8;g)Gc_5-^V#`A6mi$LU?o5_YI=ZIp zjUN|R)3z(YLm)C4NMgJYc0yAS&yh+D)rUV`$LTd%@^RX1V9E9!Ik!{OTt;v+9|E!hXch#md*Z2yH~fP-yzpcu-&wRoujwuS$)UAVnnb^ zb%glPC5H#IDhX!4<^DzX>r5jMICX%48@EM&^S(C(-734w6?`Gp+8uB~s;xKh1gl+B z{cy3mfxZD#y~`Eo2Db9~z6Wxf1MrR9>kITM2kdwi^&{-rp{qtZ*E&2VIa3Be9w;) z!;zsT$UgNeTHWeP^&dZR`tHD#74ic{PR>+v%r@*e);-2>0-V>QLu|pn8jw9Z_!72o z;jIUh8mJ1>u>p>@%1SEzx14uh5hT$<63$!X=!b=J36n?@vHvn#{pFPssH98qyY^8L)4b2&fK?k}l4fZrtznok&NvHFSn)*%vT zSo}$lM|n#p^W2qB<02{A%+hvR>TnZK`l(N^JQ8!_jiYn6b+RPxK=tmzOy@#!WwIAk zJlP0l&^bFBcrY~G*Ny*F+4(JS-v-A`jWOwooTuTAdp{$XC-Xy%QZONb!`#4!{RkW- zhYRy5Xb9|djGNOC&1QGnU{M%haR1rDKwC(+*-U|BA=?p{H(ZiQOoA8p8#i{S3&>2C zat#@~WJf#9xg};uJ0kuZ+`lc(Q$he?jMe-%EHQJYidxQL49^fGv{`k^7H;9Tzd!Fx z@y{gOv@t;XUgCjgwzP+!g#D`+AbI-vb_IVgzR-C&_fR!&Xk%}H`PaX|Y*4Qr9OZi9?6i1?{TOc1oAGX992o2cT$no=sG;l> za6^)nvxg9DwJ7VRY2TlH6^rdZl-9~O#Kb=}BqPN)dhYV1hlot(rZ z*x|zr^{aJyVcRU!6VFZkUjp%NV_`#L562NhA^=RCcp#uTK|U1u?ORT>JcV6cabe}tYYi2Ub}UUG^FP{9)A(I3B}`W1?; zr~nAOC|iXcdSX88W%rOBy|345MLAb!Aq!c3JP0H|ZBA%b4-;2~2}NdPBq|6I5r74# zzxM!R#6UIiJ9Kn(VUQRc^OG9%3bEOj34+ad9DPp?{YF=*=at>_)8;pb4eGMufM zK5aN1^rrs8V)~518G<#Yntgct?D-_oE%Q}4b8Dvlikq(yBUoe31~)m4pVUXE5=}bG z6bO>p&?nf0h8CEeDgip@88;I${7@=;4)epXGvuE<9m=F)E?^?REqYQlL>+@o3J;P^ zp9KzyZX}z!24q62%;NiLvHn|-B!h-2JHb3cgy0O8O$!aKW$L=z^%EEskrn@^u!h0b zRMb9Xkto+@*gsr@UopJo353WKzRXqQuuRgtPo%FPY?KT@OeTU=B*kUK3C~ZTyvpi` zS1?pUovc*m*ypH3ivc`;_Ju^{RtBI&$^sVhU1~?=bb8Agg0py!5O%L?qxX5t(7|qA z%wJ<;gtK9w1rC=Ijwn@Pg6qB>B21I~bt}4H4JWMy6I6if>Of$gE*4Qx6`+FyWU!wF zRFT_TMz78?GU@3CeE9{$J|_K1sCwz(qh8V(G`5_(PLZ(tr2!7lZ-X{7Q_O5BwoqA$iXjC;MIIhd4 zFb1b8QbK3UV^y)8ewd7IIiB?01Uw9LF%9x}y!wHZMHy_C3LwKFp~@V@rA@P%#_mE} zehUrsG!_c_-zYnxR|Lh>t+7M_enM)s-^BBJ6v}jd_!lIJ^{%FDP~j+$iYnX~>gMa~ z0k?LEpZcItulinmBw8zx0pUMt)I-j8@XrDZqUl+QUcj@zZ~{e{4mi&J_gOJlF;r8l zYx2ER8b)vR`E@Web71)x!m5v21{t}cj3nQvOu-XXxq|-Pu*_rPIezKM+w?c9ws#1% zEc8>pUeMxxH{ZyW8}XPX8tF$cWMw|$QR@J4hCFo?->ML(BiI721c5LMReh(q@!8WV zT4;^}ga|=KHJ^t%tGUczP{6~k2CeR$@j9Ho!aiYvr4VymBwF~%m4pp4Qr+AY?E z6341L!SrpO#251JVP2?DhmUJiQ|!Yyxa&~!iSmPP4vw)>qKwi{L;%|LkTMZC znd7D}Nb7oPdb}ry!}kUfl-NGxH#}p~S6HsX+XK8uDkJME&ilRgYW;iytXfnPh#16U zxy;tmZ{_tSe+073ktv&n=q% z5+cIDwxjYqvR?&{R8OHnn^yz81vqUEB%CcmZXpiHlxm&>wEr36<&Z z3Q}x71c1rh$(dsqwgW96fGQcFS45^yWA2s5BSg#Y%b(b(IHpiS)jKZfDK9~wsi(f} z3MKou=gUkuv=l|bEoBHvckw8E6i@qBS(<{125CwKO9x*WP`-I(5!IaL3tY>GETMYu zRv3z`{+$MfEBDJ6TXxasHg2mIlESTe&J}_e_5V3BsllMA$bjo_n)~5uk6|hb3`CMw zrIR!39}l*~2h$pZ>Nf=xqOZ;(-Xw?b@nA;-7|quo9`wopLCWu{K`SS3weXEOrtAQZbUZ^WZ7(7oCB+>J!+N-D1Viy$D0m zkWP0-M@rW(B>m$A*R4#;K9%rS<9R7nd~%4~NMX3HPOH1}9j4V8sQtw(i(=d+`gnqA zf!W{Jk-Y_v$Na)pfA_j&Yi`hE?)dM31`5_&N2S+eEtueuQ&`Lao1?cmEP4w4m6LTf zudUNHYaioWl1aa6OT{?^OQCA8wlclW>8E=MJm=Sm&=x=@eu1;yc6YaD*ATg;sp=p@j4BbumZS^CI+v)zgsu$*R}BvJ`%)@DV}YVcoo&ZdT&LJSDmDws<@~;? zRvW2tG=yx<3BB>H;%>L5*`Wz+cyI}K^RBI^iz#kpp!>A z7biO0GbssJsD?y7UC+^Wx2U)tOUVKkDj|x%eS^%|pq3F9 zK+=O>k}cG_%4vSwPS|x!Rt;{j=B%#@V2A10o?r3z(86Vi+!hRHm{t8)26VX8p>vBm zn0!MN>!LZ?n%GQCmvfzOqI>>DBN@@UT&@bJdeuH%Zx4tuwK9qF;R_d{3C>Z6w5zgk zL#w_P+WpBGck$x&{gl>TMD-5E+99D1->j3)KWwtpOToJfzn+bW;o@c~0b>Dnk&6|_ zBRn$`6Pp>Fn+vY{tJG|nwH?R}Dfg7k9rC9(Ay*IKX0mNNfPw2)&5ZVWC1O98M@d9> zIKUY30C8)YPR7s@7kg0LkiqB^!FD1bq27!08#&6QO5P^p-Lv&YJ4elpjYTVkOy7RO zmQIqcsI4-#gXCTQ@>`4K>fiA@N4++%4Gz#r-LH3u_0(-6Rz0^_Q_<0!ZW2~ z@TzyJHe8*oGj?cOygICD&f&1yg?qD0fYvqT@<>8UtRv33Ee37qxSspy&g$Aj zZ&%UMj**$epTaMnow~PD)0}i&j<@0Mky!k(!xX*kA3>i?ZQbtgk)ThOw9c%~r%G?} zpnf4k9%GaZ&f81WE+eXd%3E=hY~zgg!H~Kf^=~@SA!s3&;h5%&+}@%gCl{RU?kl-N z4;uTZ_Z0Gb|Mk)qGIa~i&!OQyU%>yCI6y5oBK3ahz!5+I0EGXi#Nq5{Vq#!yVDD_= z=tQrlXYuPUsHgXfeYH`LvI1g6=sr=CUKps>Us0lu^rV15(>xV8%}b2i!evdBnJ1yX z?y(B&bVPrlb2*iLv$aB7Q5ij4^@DlwR91_FtBog*Cc!uciM)l!ndYiwJ-X@ zqb00BzKI@8k1L1jhs(h#+Yr(T#Qf5w76jmb6b0|;x;bhs#^(l-Xy62H%G}hQ`Mq5R<;V-qFaXb?w zktC(WgpoD(;En!eJ>xb9EqE-=iqh$z0!Q8w3K;4B2?J$+M4K~-moj?U0l6T?fg)H3 z=-)YjSv$c_+zSr!&4mYdfY{tecyf2kId^xNsidZL)A+q<#6&nJkLQ|J#?tl#Q*ncMaROCWO5vdhmGY>0*JeYgoz)Y zg~nnC=@e&B6n@*WVvsy^_IMO2D7*BWpqFk8+q3j3a89ve-SlV+E3F1Z%P_?km3c$g zS2rx7CcV(AD762{D}wVMBiwk!i(?Rep;FSvA<4GTri4Qh(Mr1 zlt%6rtGwGD=VnwnFH1Tz1NgD<6eg?X70TT{%}``x)0B*rDC0!pEc2uO1G}RCB)3~X zQb89T-3$9=i$CY3q1!mAruJ6N5IC>Oq+r%xv0LH&hPadH&GR>7hunzbWM3IU zf{cjbN;lNb)Jvk&`Eo;|2dxcE*2KX+%wZ@NI|rdDWHXUv)!?`@xv}8bQUVZP+Tg0I z5$w-^_#U!@i#|IT6;r7f3{Pd%d~C=Z|*q|vfbFF z{_ZlLWtrpjE=#vub^wJ-i%9#ftDCF)ubm~g3)7^8_B{% zLcOi$59oV8dPmeS?Q1OhH~Am*+X^WUg2|-FA=+NbCxq-2hhDVwCg;NgtP@v@FyJIl zuVv>Y0VQr}U7i&uT&;W-8`==Q%r{a#)a9(b;IED^wGpPb2mG)x3(`KejiL|7Uk%6j z|DBM27G){IzX^H!n~*sF&xCX`aR1H5CKZ{;-)!uA zZ+zxZApTD>YDoTgi8U8AJqWziE*Ugs$G&V&ic8wa0E;NNbvLM^5M(T?!|$(QE_u*| z+`v$I#K~f136z*vP|+klng=^ZEdd^^mTra{$##e+e2zXcEil zlRcHp!bd2SXzAOErgvkybfQkaMLj;GxU|R;J3EGMY0e!`lJ?QkG zHdqZH^bGVjhZDC+@d09Xqzg!=&<8LeZ~EeUcf`tj%8^-PCE=9t+HnSGsGh-2UVyYT ztfA-cdFs;(T|ES%H;JFf;63;I!$!FOwt{AX)AbV`O5YtWi*>!*11t;rxZSc?+fPt< z&48tll@La=cR}#bv3d{lDzxyva_h@Ag5cbYW5Lk_Hbzm7qC!o`E_XJ{P@gj72L;$&7|R-?mBJEHV^VmCvdi>=O3wu7_FBH{--# z9J37*zNcMm^q!qXU#v#>57P2CHM~fa+|cPfa{sefnQT}|D}e$4K*0T9ij|>@g|+ej zC{|gj8@3yw2;L`ZFeo7LFlqdD^guYjwWFE;04O{oB|lE}P<+$0t>T)Fv_s^VTG3GG z9*&KE7fI@He3|ykYUIqAs?M_?)<)wvn`Xkw4#p<6VcqZ=wYr z5sAe)(t-W6-SfpstuwcXf_P8U;U@2{j3^+HxtohF4T>g*fDgZ#N3%D_`beFOfSX9S z@YF|OB+yIj`3r&>{PjD%tiY*0TN!N&Rv>JYjvcpJ0qH~aOhxF2+HM?7{tVBd-y2@K zmjVN5p=1dCfC3iW3WDJES0^;bp1V;d3}Vlyf=Y!ihkDDRc4#oDZDXo)oa)Xm9!Jza z)9{nTZA16Q4&2s4#g6UImYvI1v<~a0%zx;oA((e4_!rl6MEf;m3i$GhfNhCzBsyHR zL`;E&v!IEca}6{bOHf`vGnN zejH4Mm^Ap3jpvhtXUkUWZEPtPK$HuLT3*_b%yEj5ns{oaSjgfIGB7Z=IRvodYAq?x z=-FjMRL=l$5#Yy#x?Rh5`YDx`OA$P@6y!8uaA8;|+8!4&I8X%H^oFZTU0}BB4#-6q zOxL8+%UouojDgY{=s{Ky4W;-Z#~7WtaX|A7TOL2C{yep2beSxWyF*Ah3&z>cz49V3 z?MwrtxmQif^i1u%V=RnMC%Sfm9>p!ZIPD-kL4LB<<9tYn!#RG_#&^$ov42xJ&XtF3VJYeBt2n6* zr%QbZJlpNSB21pL(NTBl}nf~yLoJ!dq=F{A>Y8!no^={L;7ke81IjxZv{ zmL#7{OMfayGwYHM>6=-k$4@m|mS8z%dpX}#UJL!ZWQXNc8G0;uc0xmJs|NbaVYawj ztByVACrudJ2ZVE=yQAhmBx7lGup!|eyuY30rtOu_7Hsem8?8)^|WBhp@SeAQ8PW1mTY%y{&WFK7w_ z0h}`^%TSXI^B2(Vck_2gfY3*#hNF?`Q$rNDR6TOOJECSWUD`RLkQ+#11q=MpQESDH z)RfPZ!T#xQIncKdGj6c;`-v&xy}HvXg*n&d4%gyN8MOEGk8!-~y9SmX*Sy>?OSf{5 zy?Vmun*-@iuK$)Jez(1`fnvN``t~#5Evx%Q*MpPhx?a^t|5cS~4qi9;lx_&79cw3# z8;_0XgguuS8TSqG59oj22iOQhHco#}<28=|VN3p>_Thh@$c6?^|M3>t!nU%*ZoBL7 z0j(h5C`(Fuh4U6Jt!|wlt@7uBFf8EhJ;8d6NO0GnO(JEFxK97sP9yxCcBK@Nea|cP z<5;HSq@T_>FsWj@ytZ9SEk<*5leTRxXN%ggQehL_T)RPTj=L#JE>Ev5P6*Zv>rli} zcu<5-h7g>9_o+d|pUhgb8|v*&mxWfb7@#irLeeR%s#K+ytV6tY?}zE39}+1Nv7rNW zf63p+&THvJ@MLfwtX>IthHBn>(o(P*w(29fLWXYEij3&vQvkLvzV<~6Hs~J+_^Q3o zAr2B7g;Geq4OWBx@w0=qKg%Y(6$REWSZzkT0ff;i_Z)}@1ObSB#ZC_B3BC~`06{|{ z0Eo*c56kvD+fBgGcY2moAt+O{JZS4a)N=p6@aqHLnO=oJ_#*O?VU`qT&#q0ehcM*H zI~w>H#!!KcwK4cH?TO)P+lE;&Rf4l4zdF7fL7`H7Nn=pqbe5O>ILu=)386}GmR^?e z;r?=riuz=W)c>17v}|7*%n8}Bo~}}re&s-jvq^o&f$-z96?V6KpwB=dSbMa6cU1(WP*r?^%AurN!S2My020pT89Cg4;j~y@8*Rr7)P&xbB_cEs>bF5n$ z-87F88@wd+fa|Vti(#cgH|vYoW46OJsYTbfMToxZZ3$Tn#eu`=tpRG)`|od5TrT6m zv?1+8FfrS}3d(A7x(${(ZAHGdpNeiWAjQo(20;Mrt(+f_mF|S&3`%cBl^+9|-$*0` z$zct|OXk+5DdeVjG+erxAZxGZm>R(WEBkH{wJUr)e{X$x5`Vznv@OA@|F)G9cN08H zH}Xo7Kn{=bz}bwXYC*=H5$_MA&nF8`{m8N``LpmaIR`6GwLbLdU69NepzK`C6GSI} zhZ>zxgc;(ZCO=Kh>g`M{t5;rmS8*xeeF=;?h26 zd4QX+d15?qy@bCCNzC_Xxs9lS5;j;1V2BDPOBoTHQkibIqJ_H5KzS(+&k*Iz6^DSLgYw$AdmZ@QM;|gN#XYp47 zJ8{NG00c9reW2oLrv98XwkaD~1Kzkq054A-PfEn=hC#k5B zAJF103O{WHgA4SoHD?}^gQWEU3b+3TiA`vu=$I75hn&z20Yj>jV1EU-w$*0&B{g{h z%zBbWs%o7mvb$g`^fx5XRzi=}J8vp>Igv`nj2Psyt`2ifrgQoI=Y!Zk^~}X9<^IgM zUDyW8P+4fo>;PVYi8aE(LwkDb(HM|dZGe3eUC51T(OA;&2X`8U6}i;K7>`E2eZf(=t_4d{d`sF#k5TuxE=)D%{< z|ES$|GKID=O-r?a!)5@nR!NwUSxdyEGBx7vdQ*s0gS=HU+;_RJEe~{~;pCA!AG#>; z-JlP4grv-<2x+`5F!T}L`!bs~%f_gHB#n{g=w{iPHZ(kJxTggks!c!iexBe_Ww*3_ zu{re>c=<2J;B?~A?~(6bxH-9IlTzz@Pj`941Fl}H0}Z+$p@ z?5qIFIU_9NO;dVx*z#k?>1(d>ju*$E+=6@P8wBs?x%^K3pJW@F&YvH$a?Iag&$=nR zlpox*H3#2z>G(}iP%FlGq=0^IH1q$J1(>PS%iZa-Vr#D;xk+M_-=Vzv1bs!=zIN^1 zOv4R+YPp!`VYcJ3&7sDLy?z)n5o1Tw6l-Z^_1rZP{P|cb^I$!dbPXiu+w z;7l}BNvBC82978mmjSk0n1W`Aa^DVmRd#pkrozZD!A8IJi3n+e7ApX+W?rkE;W>=g zB15)7a0^%#%F7oc_&wh1jD|z3-Qlc$o0fP@N!e3(QUxmmqrZjk2Y5GGEQ|)E5D0Fx z9^T6Xt5|&KCRB%L+L?HN(RSni4fOEuGYadop-gc&X}*#4Wrf-B`kj9%y{;Dt?)A~h zNwr8@G**8re# z=NQ1Y7X{-IJNJd;fklcCSVa?}eC`O!C{gpA+d7@;#0J$!G{rHG#>{1{;;Cyz_;Xv; ztQ22LGKTkaal?xXL^_gw31|-Q&UAp_W-of(aKhWP#kUZ4*v8V+Gr(+!buYrwK9DT` z%<`=Y+>zn5He(U>t(sAs%M}Rhgo5y0WlRxo{9Da4+=Rx~xD(ImXQzn$NP>nqsnnZ& ztRNqoo*y4^c=^E0jBtPfknPMy?4%<-K5Whs0F}NZkmXGkc0RQ*<)r#fm6vkSwB5_$ zi;b~IW+l-_w#jzA3TGs0JFKUAp>T>~!@(v{P|B}}s6XVD`YeSo>Y2j8(J0Uh#W)LL zX6qDGYCo>n6RI|)Jngs5-`OVPab^ddg8TQ(YEAz59oC3_+HCmS3 z^(bW`+-h^&B##k+?rSVvsM#S?S@FmSp#B>U{LYoQZv{~8G+Fe0F>NUvLYX zwK9&6nH9PI0@TF_lVSps8HXRZ2SVd`&qJGcs^IKH4|wrmir7*4*sSG#vyrWJ_Pb0w zF)Ac7VE}Q{!1oVGbBx*oSbD2GK5NRLNnAdQ)3`$R!r$Gs$n2g=2~1*cFJ1BNNLXTF zmn8Vzg5bq85Hi5X3wv4BKF}qijllYC&pf@v=7T1dp zMWBxIvLIU@LB?*S``mJoXb>&%4+{$Y_#g#XDm9NIqhE0 zqby`a*NSbg8OjTc2~T}OY%Xs9L3?tFLM?ZCjD;Q3K*>RO$~!%Uot-sRj<_>2C(EXv zRLPji5IPAKVXDGLp=1|T;cmetG(+LS#p1xT16R~RNFtj+0Wz6K!RvR84f%{?`AiiR z9{5&H`D;Zjv7%Jbo$RoMnicC}c4D0423hGtoo(WH;2rR&(FRXb+ZE}q82kNdUH$Ot9hpcp zpxl>ALu?ryqh2!%z5tE+X~Qnoy-04~d!5>{pSJEPbU2pSLUfnsA4(+p(tdi)-xZ0! zxqvS$IBkP3AbPe-1N>(;%gt@d3pwy}ekCiw+)wCYgo6NFJ70uj=B?`d+1%O~pBm!^ zFDV3jJO6rb9t)cPOxg2HgHZ_HX0)ByXHp90FGPF<<62!E=I2j$Re~}!HfYi(a8pmc zc&-m6gJ9|2TM(<|y%lI?w}288vmG>mP^CdBD`60wa)v;2!aL6ExM z!FOeE_>C|>&*)+=dG|kth~+^_>VCih0Jez!FOvEHZjp`cZ2t3rgQfF3N_@N36QF`1 ztc#jAG2y3A8Ao=eYAq45jLTLS(kJfRuae9YFS&2<$44w8Q9L3stJ4|?Yvs`U?%G}N zzP?)3yiT31vuw=J|I|`v31?4B6&02|^4TlE@tq&DY8-@>bgDd)25MX)8k#Ll+P#Z2 zo4`FeKvNYJbc_R_0cFQD{m^9K&PIUl&QoTh1JYxBG`#@c@gcvu;vFN8_t9bCrUD7K z=#M)jSeQg@iAwzOZ5h96W8p2UfEvM-FfAX_XBF9mL}a|>s@7zf=J_&{{)od^+V&|L zi*UGFP?duySDeUz#oSd*c_-zlSLR*$b}9U^a6av?s-AL?TY4 zldP6YKNpuk;8ytNB-A7Z*HPzp5)%d%eM(^Er}95b%0Avb;H)0DP-?*gy~+ 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 KEr0Gv$Va93z literal 0 HcmV?d00001 diff --git a/bindings/python/bindings-test/share/python-wheels/idna-2.10-py2.py3-none-any.whl b/bindings/python/bindings-test/share/python-wheels/idna-2.10-py2.py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..0ba1ddac371b09197ec3c1f4f109d49a5d99fd27 GIT binary patch literal 63344 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
`Y8|bJQ+E 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 AApigX literal 0 HcmV?d00001 diff --git a/bindings/python/bindings-test/share/python-wheels/msgpack-1.0.0-py2.py3-none-any.whl b/bindings/python/bindings-test/share/python-wheels/msgpack-1.0.0-py2.py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..e761172fd87d6d25024a8474f21e50798c71bd6c GIT binary patch literal 75866 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