update to python3.7
This commit is contained in:
parent
da2d24a7f4
commit
80c4a755da
2912 changed files with 206832 additions and 100407 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
lib/libreadline.7.0.dylib
Normal file
BIN
lib/libreadline.7.0.dylib
Normal file
Binary file not shown.
1
lib/libreadline.7.dylib
Symbolic link
1
lib/libreadline.7.dylib
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
libreadline.7.0.dylib
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
13
lib/pkgconfig/python-3.7.pc
Normal file
13
lib/pkgconfig/python-3.7.pc
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
# See: man pkg-config
|
||||
prefix=/oml/openmedialibrary_platform_darwin/dist
|
||||
exec_prefix=${prefix}
|
||||
libdir=${exec_prefix}/lib
|
||||
includedir=${prefix}/include
|
||||
|
||||
Name: Python
|
||||
Description: Python library
|
||||
Requires:
|
||||
Version: 3.7
|
||||
Libs.private: -ldl -framework CoreFoundation
|
||||
Libs: -L${libdir} -lpython3.7m
|
||||
Cflags: -I${includedir}/python3.7m
|
||||
|
|
@ -1,677 +0,0 @@
|
|||
# system configuration generated and used by the sysconfig module
|
||||
build_time_vars = {'ABIFLAGS': 'm',
|
||||
'AC_APPLE_UNIVERSAL_BUILD': 0,
|
||||
'AIX_GENUINE_CPLUSPLUS': 0,
|
||||
'AR': 'ar',
|
||||
'ARFLAGS': 'rc',
|
||||
'ASDLGEN': 'python ./Parser/asdl_c.py',
|
||||
'ASDLGEN_FILES': './Parser/asdl.py ./Parser/asdl_c.py',
|
||||
'AST_ASDL': './Parser/Python.asdl',
|
||||
'AST_C': 'Python/Python-ast.c',
|
||||
'AST_C_DIR': 'Python',
|
||||
'AST_H': 'Include/Python-ast.h',
|
||||
'AST_H_DIR': 'Include',
|
||||
'BASECFLAGS': '-Wno-unused-result -Wsign-compare -Wunreachable-code '
|
||||
'-fno-common -dynamic',
|
||||
'BASECPPFLAGS': '',
|
||||
'BASEMODLIBS': '',
|
||||
'BINDIR': '/Users/build/.local/Cellar/python3/3.5.0/Frameworks/Python.framework/Versions/3.5/bin',
|
||||
'BINLIBDEST': '/Users/build/.local/Cellar/python3/3.5.0/Frameworks/Python.framework/Versions/3.5/lib/python3.5',
|
||||
'BLDLIBRARY': '',
|
||||
'BLDSHARED': 'clang -bundle -undefined dynamic_lookup',
|
||||
'BUILDEXE': '.exe',
|
||||
'BUILDPYTHON': 'python.exe',
|
||||
'BUILD_GNU_TYPE': 'x86_64-apple-darwin15.2.0',
|
||||
'BYTESTR_DEPS': '\\',
|
||||
'CC': 'clang',
|
||||
'CCSHARED': '',
|
||||
'CFLAGS': '-Wno-unused-result -Wsign-compare -Wunreachable-code -fno-common '
|
||||
'-dynamic -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes',
|
||||
'CFLAGSFORSHARED': '',
|
||||
'CFLAGS_NODIST': '',
|
||||
'CONFIGFILES': 'configure configure.ac acconfig.h pyconfig.h.in '
|
||||
'Makefile.pre.in',
|
||||
'CONFIGURE_CFLAGS': '',
|
||||
'CONFIGURE_CFLAGS_NODIST': '-Werror=declaration-after-statement',
|
||||
'CONFIGURE_CPPFLAGS': '',
|
||||
'CONFIGURE_LDFLAGS': '',
|
||||
'CONFIG_ARGS': "'--prefix=/Users/build/.local/Cellar/python3/3.5.0' "
|
||||
"'--enable-ipv6' "
|
||||
"'--datarootdir=/Users/build/.local/Cellar/python3/3.5.0/share' "
|
||||
"'--datadir=/Users/build/.local/Cellar/python3/3.5.0/share' "
|
||||
"'--enable-framework=/Users/build/.local/Cellar/python3/3.5.0/Frameworks' "
|
||||
"'--without-ensurepip' '--without-gcc' "
|
||||
"'MACOSX_DEPLOYMENT_TARGET=10.11' 'CC=clang' "
|
||||
"'PKG_CONFIG_PATH=/Users/build/.local/opt/xz/lib/pkgconfig:/Users/build/.local/opt/sqlite/lib/pkgconfig:/Users/build/.local/opt/openssl/lib/pkgconfig' "
|
||||
"'PKG_CONFIG_LIBDIR=/usr/lib/pkgconfig:/Users/build/.local/Library/ENV/pkgconfig/10.11'",
|
||||
'CONFINCLUDEDIR': '/Users/build/.local/Cellar/python3/3.5.0/Frameworks/Python.framework/Versions/3.5/include',
|
||||
'CONFINCLUDEPY': '/Users/build/.local/Cellar/python3/3.5.0/Frameworks/Python.framework/Versions/3.5/include/python3.5m',
|
||||
'COREPYTHONPATH': ':plat-darwin',
|
||||
'COVERAGE_INFO': '/private/tmp/python320151125-76692-lzmenz/Python-3.5.0/coverage.info',
|
||||
'COVERAGE_REPORT': '/private/tmp/python320151125-76692-lzmenz/Python-3.5.0/lcov-report',
|
||||
'COVERAGE_REPORT_OPTIONS': '--no-branch-coverage --title "CPython lcov '
|
||||
'report"',
|
||||
'CPPFLAGS': '-I. -IInclude -I./Include',
|
||||
'CXX': 'clang++',
|
||||
'DESTDIRS': '/Users/build/.local/Cellar/python3/3.5.0/Frameworks/Python.framework/Versions/3.5 '
|
||||
'/Users/build/.local/Cellar/python3/3.5.0/Frameworks/Python.framework/Versions/3.5/lib '
|
||||
'/Users/build/.local/Cellar/python3/3.5.0/Frameworks/Python.framework/Versions/3.5/lib/python3.5 '
|
||||
'/Users/build/.local/Cellar/python3/3.5.0/Frameworks/Python.framework/Versions/3.5/lib/python3.5/lib-dynload',
|
||||
'DESTLIB': '/Users/build/.local/Cellar/python3/3.5.0/Frameworks/Python.framework/Versions/3.5/lib/python3.5',
|
||||
'DESTPATH': '',
|
||||
'DESTSHARED': '/Users/build/.local/Cellar/python3/3.5.0/Frameworks/Python.framework/Versions/3.5/lib/python3.5/lib-dynload',
|
||||
'DIRMODE': 755,
|
||||
'DIST': 'README ChangeLog configure configure.ac acconfig.h pyconfig.h.in '
|
||||
'Makefile.pre.in Include Lib Misc Ext-dummy',
|
||||
'DISTDIRS': 'Include Lib Misc Ext-dummy',
|
||||
'DISTFILES': 'README ChangeLog configure configure.ac acconfig.h '
|
||||
'pyconfig.h.in Makefile.pre.in',
|
||||
'DLINCLDIR': '.',
|
||||
'DLLLIBRARY': '',
|
||||
'DOUBLE_IS_ARM_MIXED_ENDIAN_IEEE754': 0,
|
||||
'DOUBLE_IS_BIG_ENDIAN_IEEE754': 0,
|
||||
'DOUBLE_IS_LITTLE_ENDIAN_IEEE754': 1,
|
||||
'DYNLOADFILE': 'dynload_shlib.o',
|
||||
'ENABLE_IPV6': 1,
|
||||
'ENSUREPIP': 'no',
|
||||
'EXE': '',
|
||||
'EXEMODE': 755,
|
||||
'EXTRAMACHDEPPATH': '',
|
||||
'EXTRATESTOPTS': '',
|
||||
'EXTRA_CFLAGS': '',
|
||||
'EXT_SUFFIX': '.cpython-35m-darwin.so',
|
||||
'FILEMODE': 644,
|
||||
'FLOCK_NEEDS_LIBBSD': 0,
|
||||
'GETPGRP_HAVE_ARG': 0,
|
||||
'GETTIMEOFDAY_NO_TZ': 0,
|
||||
'GNULD': 'no',
|
||||
'GRAMMAR_C': 'Python/graminit.c',
|
||||
'GRAMMAR_H': 'Include/graminit.h',
|
||||
'GRAMMAR_INPUT': './Grammar/Grammar',
|
||||
'HAVE_ACCEPT4': 0,
|
||||
'HAVE_ACOSH': 1,
|
||||
'HAVE_ADDRINFO': 1,
|
||||
'HAVE_ALARM': 1,
|
||||
'HAVE_ALIGNED_REQUIRED': 0,
|
||||
'HAVE_ALLOCA_H': 1,
|
||||
'HAVE_ALTZONE': 0,
|
||||
'HAVE_ASINH': 1,
|
||||
'HAVE_ASM_TYPES_H': 0,
|
||||
'HAVE_ATANH': 1,
|
||||
'HAVE_BIND_TEXTDOMAIN_CODESET': 0,
|
||||
'HAVE_BLUETOOTH_BLUETOOTH_H': 0,
|
||||
'HAVE_BLUETOOTH_H': 0,
|
||||
'HAVE_BROKEN_MBSTOWCS': 0,
|
||||
'HAVE_BROKEN_NICE': 0,
|
||||
'HAVE_BROKEN_PIPE_BUF': 0,
|
||||
'HAVE_BROKEN_POLL': 0,
|
||||
'HAVE_BROKEN_POSIX_SEMAPHORES': 0,
|
||||
'HAVE_BROKEN_PTHREAD_SIGMASK': 0,
|
||||
'HAVE_BROKEN_SEM_GETVALUE': 1,
|
||||
'HAVE_BROKEN_UNSETENV': 0,
|
||||
'HAVE_BUILTIN_ATOMIC': 1,
|
||||
'HAVE_C99_BOOL': 1,
|
||||
'HAVE_CHFLAGS': 1,
|
||||
'HAVE_CHOWN': 1,
|
||||
'HAVE_CHROOT': 1,
|
||||
'HAVE_CLOCK': 1,
|
||||
'HAVE_CLOCK_GETRES': 0,
|
||||
'HAVE_CLOCK_GETTIME': 0,
|
||||
'HAVE_COMPUTED_GOTOS': 1,
|
||||
'HAVE_CONFSTR': 1,
|
||||
'HAVE_CONIO_H': 0,
|
||||
'HAVE_COPYSIGN': 1,
|
||||
'HAVE_CTERMID': 1,
|
||||
'HAVE_CTERMID_R': 1,
|
||||
'HAVE_CURSES_H': 1,
|
||||
'HAVE_CURSES_IS_TERM_RESIZED': 1,
|
||||
'HAVE_CURSES_RESIZETERM': 1,
|
||||
'HAVE_CURSES_RESIZE_TERM': 1,
|
||||
'HAVE_DECL_ISFINITE': 1,
|
||||
'HAVE_DECL_ISINF': 1,
|
||||
'HAVE_DECL_ISNAN': 1,
|
||||
'HAVE_DECL_TZNAME': 0,
|
||||
'HAVE_DEVICE_MACROS': 1,
|
||||
'HAVE_DEV_PTC': 0,
|
||||
'HAVE_DEV_PTMX': 1,
|
||||
'HAVE_DIRECT_H': 0,
|
||||
'HAVE_DIRENT_D_TYPE': 1,
|
||||
'HAVE_DIRENT_H': 1,
|
||||
'HAVE_DIRFD': 1,
|
||||
'HAVE_DLFCN_H': 1,
|
||||
'HAVE_DLOPEN': 1,
|
||||
'HAVE_DUP2': 1,
|
||||
'HAVE_DUP3': 0,
|
||||
'HAVE_DYNAMIC_LOADING': 1,
|
||||
'HAVE_ENDIAN_H': 0,
|
||||
'HAVE_EPOLL': 0,
|
||||
'HAVE_EPOLL_CREATE1': 0,
|
||||
'HAVE_ERF': 1,
|
||||
'HAVE_ERFC': 1,
|
||||
'HAVE_ERRNO_H': 1,
|
||||
'HAVE_EXECV': 1,
|
||||
'HAVE_EXPM1': 1,
|
||||
'HAVE_FACCESSAT': 1,
|
||||
'HAVE_FCHDIR': 1,
|
||||
'HAVE_FCHMOD': 1,
|
||||
'HAVE_FCHMODAT': 1,
|
||||
'HAVE_FCHOWN': 1,
|
||||
'HAVE_FCHOWNAT': 1,
|
||||
'HAVE_FCNTL_H': 1,
|
||||
'HAVE_FDATASYNC': 0,
|
||||
'HAVE_FDOPENDIR': 1,
|
||||
'HAVE_FEXECVE': 0,
|
||||
'HAVE_FINITE': 1,
|
||||
'HAVE_FLOCK': 1,
|
||||
'HAVE_FORK': 1,
|
||||
'HAVE_FORKPTY': 1,
|
||||
'HAVE_FPATHCONF': 1,
|
||||
'HAVE_FSEEK64': 0,
|
||||
'HAVE_FSEEKO': 1,
|
||||
'HAVE_FSTATAT': 1,
|
||||
'HAVE_FSTATVFS': 1,
|
||||
'HAVE_FSYNC': 1,
|
||||
'HAVE_FTELL64': 0,
|
||||
'HAVE_FTELLO': 1,
|
||||
'HAVE_FTIME': 1,
|
||||
'HAVE_FTRUNCATE': 1,
|
||||
'HAVE_FUTIMENS': 0,
|
||||
'HAVE_FUTIMES': 1,
|
||||
'HAVE_FUTIMESAT': 0,
|
||||
'HAVE_GAI_STRERROR': 1,
|
||||
'HAVE_GAMMA': 1,
|
||||
'HAVE_GCC_ASM_FOR_MC68881': 0,
|
||||
'HAVE_GCC_ASM_FOR_X64': 1,
|
||||
'HAVE_GCC_ASM_FOR_X87': 1,
|
||||
'HAVE_GCC_UINT128_T': 1,
|
||||
'HAVE_GETADDRINFO': 1,
|
||||
'HAVE_GETC_UNLOCKED': 1,
|
||||
'HAVE_GETENTROPY': 0,
|
||||
'HAVE_GETGROUPLIST': 1,
|
||||
'HAVE_GETGROUPS': 1,
|
||||
'HAVE_GETHOSTBYNAME': 1,
|
||||
'HAVE_GETHOSTBYNAME_R': 0,
|
||||
'HAVE_GETHOSTBYNAME_R_3_ARG': 0,
|
||||
'HAVE_GETHOSTBYNAME_R_5_ARG': 0,
|
||||
'HAVE_GETHOSTBYNAME_R_6_ARG': 0,
|
||||
'HAVE_GETITIMER': 1,
|
||||
'HAVE_GETLOADAVG': 1,
|
||||
'HAVE_GETLOGIN': 1,
|
||||
'HAVE_GETNAMEINFO': 1,
|
||||
'HAVE_GETPAGESIZE': 1,
|
||||
'HAVE_GETPEERNAME': 1,
|
||||
'HAVE_GETPGID': 1,
|
||||
'HAVE_GETPGRP': 1,
|
||||
'HAVE_GETPID': 1,
|
||||
'HAVE_GETPRIORITY': 1,
|
||||
'HAVE_GETPWENT': 1,
|
||||
'HAVE_GETRANDOM_SYSCALL': 0,
|
||||
'HAVE_GETRESGID': 0,
|
||||
'HAVE_GETRESUID': 0,
|
||||
'HAVE_GETSID': 1,
|
||||
'HAVE_GETSPENT': 0,
|
||||
'HAVE_GETSPNAM': 0,
|
||||
'HAVE_GETTIMEOFDAY': 1,
|
||||
'HAVE_GETWD': 1,
|
||||
'HAVE_GLIBC_MEMMOVE_BUG': 0,
|
||||
'HAVE_GRP_H': 1,
|
||||
'HAVE_HSTRERROR': 1,
|
||||
'HAVE_HTOLE64': 0,
|
||||
'HAVE_HYPOT': 1,
|
||||
'HAVE_IEEEFP_H': 0,
|
||||
'HAVE_IF_NAMEINDEX': 1,
|
||||
'HAVE_INET_ATON': 1,
|
||||
'HAVE_INET_PTON': 1,
|
||||
'HAVE_INITGROUPS': 1,
|
||||
'HAVE_INT32_T': 1,
|
||||
'HAVE_INT64_T': 1,
|
||||
'HAVE_INTTYPES_H': 1,
|
||||
'HAVE_IO_H': 0,
|
||||
'HAVE_IPA_PURE_CONST_BUG': 0,
|
||||
'HAVE_KILL': 1,
|
||||
'HAVE_KILLPG': 1,
|
||||
'HAVE_KQUEUE': 1,
|
||||
'HAVE_LANGINFO_H': 1,
|
||||
'HAVE_LARGEFILE_SUPPORT': 0,
|
||||
'HAVE_LCHFLAGS': 1,
|
||||
'HAVE_LCHMOD': 1,
|
||||
'HAVE_LCHOWN': 1,
|
||||
'HAVE_LGAMMA': 1,
|
||||
'HAVE_LIBDL': 1,
|
||||
'HAVE_LIBDLD': 0,
|
||||
'HAVE_LIBIEEE': 0,
|
||||
'HAVE_LIBINTL_H': 0,
|
||||
'HAVE_LIBREADLINE': 1,
|
||||
'HAVE_LIBRESOLV': 0,
|
||||
'HAVE_LIBSENDFILE': 0,
|
||||
'HAVE_LIBUTIL_H': 0,
|
||||
'HAVE_LINK': 1,
|
||||
'HAVE_LINKAT': 1,
|
||||
'HAVE_LINUX_CAN_BCM_H': 0,
|
||||
'HAVE_LINUX_CAN_H': 0,
|
||||
'HAVE_LINUX_CAN_RAW_FD_FRAMES': 0,
|
||||
'HAVE_LINUX_CAN_RAW_H': 0,
|
||||
'HAVE_LINUX_NETLINK_H': 0,
|
||||
'HAVE_LINUX_TIPC_H': 0,
|
||||
'HAVE_LOCKF': 1,
|
||||
'HAVE_LOG1P': 1,
|
||||
'HAVE_LOG2': 1,
|
||||
'HAVE_LONG_DOUBLE': 1,
|
||||
'HAVE_LONG_LONG': 1,
|
||||
'HAVE_LSTAT': 1,
|
||||
'HAVE_LUTIMES': 1,
|
||||
'HAVE_MAKEDEV': 1,
|
||||
'HAVE_MBRTOWC': 1,
|
||||
'HAVE_MEMMOVE': 1,
|
||||
'HAVE_MEMORY_H': 1,
|
||||
'HAVE_MEMRCHR': 0,
|
||||
'HAVE_MKDIRAT': 1,
|
||||
'HAVE_MKFIFO': 1,
|
||||
'HAVE_MKFIFOAT': 0,
|
||||
'HAVE_MKNOD': 1,
|
||||
'HAVE_MKNODAT': 0,
|
||||
'HAVE_MKTIME': 1,
|
||||
'HAVE_MMAP': 1,
|
||||
'HAVE_MREMAP': 0,
|
||||
'HAVE_NCURSES_H': 1,
|
||||
'HAVE_NDIR_H': 0,
|
||||
'HAVE_NETPACKET_PACKET_H': 0,
|
||||
'HAVE_NET_IF_H': 1,
|
||||
'HAVE_NICE': 1,
|
||||
'HAVE_OPENAT': 1,
|
||||
'HAVE_OPENPTY': 1,
|
||||
'HAVE_OSX105_SDK': 1,
|
||||
'HAVE_PATHCONF': 1,
|
||||
'HAVE_PAUSE': 1,
|
||||
'HAVE_PIPE2': 0,
|
||||
'HAVE_PLOCK': 0,
|
||||
'HAVE_POLL': 1,
|
||||
'HAVE_POLL_H': 1,
|
||||
'HAVE_POSIX_FADVISE': 0,
|
||||
'HAVE_POSIX_FALLOCATE': 0,
|
||||
'HAVE_PREAD': 1,
|
||||
'HAVE_PRLIMIT': 0,
|
||||
'HAVE_PROCESS_H': 0,
|
||||
'HAVE_PROTOTYPES': 1,
|
||||
'HAVE_PTHREAD_ATFORK': 1,
|
||||
'HAVE_PTHREAD_DESTRUCTOR': 0,
|
||||
'HAVE_PTHREAD_H': 1,
|
||||
'HAVE_PTHREAD_INIT': 0,
|
||||
'HAVE_PTHREAD_KILL': 1,
|
||||
'HAVE_PTHREAD_SIGMASK': 1,
|
||||
'HAVE_PTY_H': 0,
|
||||
'HAVE_PUTENV': 1,
|
||||
'HAVE_PWRITE': 1,
|
||||
'HAVE_RAND_EGD': 1,
|
||||
'HAVE_READLINK': 1,
|
||||
'HAVE_READLINKAT': 1,
|
||||
'HAVE_READV': 1,
|
||||
'HAVE_REALPATH': 1,
|
||||
'HAVE_RENAMEAT': 1,
|
||||
'HAVE_RL_APPEND_HISTORY': 1,
|
||||
'HAVE_RL_CALLBACK': 1,
|
||||
'HAVE_RL_CATCH_SIGNAL': 1,
|
||||
'HAVE_RL_COMPLETION_APPEND_CHARACTER': 1,
|
||||
'HAVE_RL_COMPLETION_DISPLAY_MATCHES_HOOK': 1,
|
||||
'HAVE_RL_COMPLETION_MATCHES': 1,
|
||||
'HAVE_RL_COMPLETION_SUPPRESS_APPEND': 1,
|
||||
'HAVE_RL_PRE_INPUT_HOOK': 1,
|
||||
'HAVE_ROUND': 1,
|
||||
'HAVE_SCHED_GET_PRIORITY_MAX': 1,
|
||||
'HAVE_SCHED_H': 1,
|
||||
'HAVE_SCHED_RR_GET_INTERVAL': 0,
|
||||
'HAVE_SCHED_SETAFFINITY': 0,
|
||||
'HAVE_SCHED_SETPARAM': 0,
|
||||
'HAVE_SCHED_SETSCHEDULER': 0,
|
||||
'HAVE_SELECT': 1,
|
||||
'HAVE_SEM_GETVALUE': 1,
|
||||
'HAVE_SEM_OPEN': 1,
|
||||
'HAVE_SEM_TIMEDWAIT': 0,
|
||||
'HAVE_SEM_UNLINK': 1,
|
||||
'HAVE_SENDFILE': 1,
|
||||
'HAVE_SETEGID': 1,
|
||||
'HAVE_SETEUID': 1,
|
||||
'HAVE_SETGID': 1,
|
||||
'HAVE_SETGROUPS': 1,
|
||||
'HAVE_SETHOSTNAME': 1,
|
||||
'HAVE_SETITIMER': 1,
|
||||
'HAVE_SETLOCALE': 1,
|
||||
'HAVE_SETPGID': 1,
|
||||
'HAVE_SETPGRP': 1,
|
||||
'HAVE_SETPRIORITY': 1,
|
||||
'HAVE_SETREGID': 1,
|
||||
'HAVE_SETRESGID': 0,
|
||||
'HAVE_SETRESUID': 0,
|
||||
'HAVE_SETREUID': 1,
|
||||
'HAVE_SETSID': 1,
|
||||
'HAVE_SETUID': 1,
|
||||
'HAVE_SETVBUF': 1,
|
||||
'HAVE_SHADOW_H': 0,
|
||||
'HAVE_SIGACTION': 1,
|
||||
'HAVE_SIGALTSTACK': 1,
|
||||
'HAVE_SIGINTERRUPT': 1,
|
||||
'HAVE_SIGNAL_H': 1,
|
||||
'HAVE_SIGPENDING': 1,
|
||||
'HAVE_SIGRELSE': 1,
|
||||
'HAVE_SIGTIMEDWAIT': 0,
|
||||
'HAVE_SIGWAIT': 1,
|
||||
'HAVE_SIGWAITINFO': 0,
|
||||
'HAVE_SNPRINTF': 1,
|
||||
'HAVE_SOCKADDR_SA_LEN': 1,
|
||||
'HAVE_SOCKADDR_STORAGE': 1,
|
||||
'HAVE_SOCKETPAIR': 1,
|
||||
'HAVE_SPAWN_H': 1,
|
||||
'HAVE_SSIZE_T': 1,
|
||||
'HAVE_STATVFS': 1,
|
||||
'HAVE_STAT_TV_NSEC': 0,
|
||||
'HAVE_STAT_TV_NSEC2': 1,
|
||||
'HAVE_STDARG_PROTOTYPES': 1,
|
||||
'HAVE_STDINT_H': 1,
|
||||
'HAVE_STDLIB_H': 1,
|
||||
'HAVE_STD_ATOMIC': 0,
|
||||
'HAVE_STRDUP': 1,
|
||||
'HAVE_STRFTIME': 1,
|
||||
'HAVE_STRINGS_H': 1,
|
||||
'HAVE_STRING_H': 1,
|
||||
'HAVE_STRLCPY': 1,
|
||||
'HAVE_STROPTS_H': 0,
|
||||
'HAVE_STRUCT_STAT_ST_BIRTHTIME': 1,
|
||||
'HAVE_STRUCT_STAT_ST_BLKSIZE': 1,
|
||||
'HAVE_STRUCT_STAT_ST_BLOCKS': 1,
|
||||
'HAVE_STRUCT_STAT_ST_FLAGS': 1,
|
||||
'HAVE_STRUCT_STAT_ST_GEN': 1,
|
||||
'HAVE_STRUCT_STAT_ST_RDEV': 1,
|
||||
'HAVE_STRUCT_TM_TM_ZONE': 1,
|
||||
'HAVE_ST_BLOCKS': 1,
|
||||
'HAVE_SYMLINK': 1,
|
||||
'HAVE_SYMLINKAT': 1,
|
||||
'HAVE_SYNC': 1,
|
||||
'HAVE_SYSCONF': 1,
|
||||
'HAVE_SYSEXITS_H': 1,
|
||||
'HAVE_SYS_AUDIOIO_H': 0,
|
||||
'HAVE_SYS_BSDTTY_H': 0,
|
||||
'HAVE_SYS_DEVPOLL_H': 0,
|
||||
'HAVE_SYS_DIR_H': 0,
|
||||
'HAVE_SYS_ENDIAN_H': 0,
|
||||
'HAVE_SYS_EPOLL_H': 0,
|
||||
'HAVE_SYS_EVENT_H': 1,
|
||||
'HAVE_SYS_FILE_H': 1,
|
||||
'HAVE_SYS_IOCTL_H': 1,
|
||||
'HAVE_SYS_KERN_CONTROL_H': 1,
|
||||
'HAVE_SYS_LOADAVG_H': 0,
|
||||
'HAVE_SYS_LOCK_H': 1,
|
||||
'HAVE_SYS_MKDEV_H': 0,
|
||||
'HAVE_SYS_MODEM_H': 0,
|
||||
'HAVE_SYS_NDIR_H': 0,
|
||||
'HAVE_SYS_PARAM_H': 1,
|
||||
'HAVE_SYS_POLL_H': 1,
|
||||
'HAVE_SYS_RESOURCE_H': 1,
|
||||
'HAVE_SYS_SELECT_H': 1,
|
||||
'HAVE_SYS_SENDFILE_H': 0,
|
||||
'HAVE_SYS_SOCKET_H': 1,
|
||||
'HAVE_SYS_STATVFS_H': 1,
|
||||
'HAVE_SYS_STAT_H': 1,
|
||||
'HAVE_SYS_SYSCALL_H': 1,
|
||||
'HAVE_SYS_SYS_DOMAIN_H': 1,
|
||||
'HAVE_SYS_TERMIO_H': 0,
|
||||
'HAVE_SYS_TIMES_H': 1,
|
||||
'HAVE_SYS_TIME_H': 1,
|
||||
'HAVE_SYS_TYPES_H': 1,
|
||||
'HAVE_SYS_UIO_H': 1,
|
||||
'HAVE_SYS_UN_H': 1,
|
||||
'HAVE_SYS_UTSNAME_H': 1,
|
||||
'HAVE_SYS_WAIT_H': 1,
|
||||
'HAVE_SYS_XATTR_H': 1,
|
||||
'HAVE_TCGETPGRP': 1,
|
||||
'HAVE_TCSETPGRP': 1,
|
||||
'HAVE_TEMPNAM': 1,
|
||||
'HAVE_TERMIOS_H': 1,
|
||||
'HAVE_TERM_H': 1,
|
||||
'HAVE_TGAMMA': 1,
|
||||
'HAVE_TIMEGM': 1,
|
||||
'HAVE_TIMES': 1,
|
||||
'HAVE_TMPFILE': 1,
|
||||
'HAVE_TMPNAM': 1,
|
||||
'HAVE_TMPNAM_R': 0,
|
||||
'HAVE_TM_ZONE': 1,
|
||||
'HAVE_TRUNCATE': 1,
|
||||
'HAVE_TZNAME': 0,
|
||||
'HAVE_UCS4_TCL': 0,
|
||||
'HAVE_UINT32_T': 1,
|
||||
'HAVE_UINT64_T': 1,
|
||||
'HAVE_UINTPTR_T': 1,
|
||||
'HAVE_UNAME': 1,
|
||||
'HAVE_UNISTD_H': 1,
|
||||
'HAVE_UNLINKAT': 1,
|
||||
'HAVE_UNSETENV': 1,
|
||||
'HAVE_USABLE_WCHAR_T': 0,
|
||||
'HAVE_UTIL_H': 1,
|
||||
'HAVE_UTIMENSAT': 0,
|
||||
'HAVE_UTIMES': 1,
|
||||
'HAVE_UTIME_H': 1,
|
||||
'HAVE_WAIT3': 1,
|
||||
'HAVE_WAIT4': 1,
|
||||
'HAVE_WAITID': 1,
|
||||
'HAVE_WAITPID': 1,
|
||||
'HAVE_WCHAR_H': 1,
|
||||
'HAVE_WCSCOLL': 1,
|
||||
'HAVE_WCSFTIME': 1,
|
||||
'HAVE_WCSXFRM': 1,
|
||||
'HAVE_WMEMCMP': 1,
|
||||
'HAVE_WORKING_TZSET': 1,
|
||||
'HAVE_WRITEV': 1,
|
||||
'HAVE_ZLIB_COPY': 1,
|
||||
'HAVE__GETPTY': 0,
|
||||
'HGBRANCH': '',
|
||||
'HGTAG': '',
|
||||
'HGVERSION': '',
|
||||
'HOST_GNU_TYPE': 'x86_64-apple-darwin15.2.0',
|
||||
'INCLDIRSTOMAKE': '/Users/build/.local/Cellar/python3/3.5.0/Frameworks/Python.framework/Versions/3.5/include '
|
||||
'/Users/build/.local/Cellar/python3/3.5.0/Frameworks/Python.framework/Versions/3.5/include '
|
||||
'/Users/build/.local/Cellar/python3/3.5.0/Frameworks/Python.framework/Versions/3.5/include/python3.5m '
|
||||
'/Users/build/.local/Cellar/python3/3.5.0/Frameworks/Python.framework/Versions/3.5/include/python3.5m',
|
||||
'INCLUDEDIR': '/Users/build/.local/Cellar/python3/3.5.0/Frameworks/Python.framework/Versions/3.5/include',
|
||||
'INCLUDEPY': '/Users/build/.local/Cellar/python3/3.5.0/Frameworks/Python.framework/Versions/3.5/include/python3.5m',
|
||||
'INSTALL': '/usr/bin/install -c',
|
||||
'INSTALL_DATA': '/usr/bin/install -c -m 644',
|
||||
'INSTALL_PROGRAM': '/usr/bin/install -c',
|
||||
'INSTALL_SCRIPT': '/usr/bin/install -c',
|
||||
'INSTALL_SHARED': '/usr/bin/install -c -m 555',
|
||||
'INSTSONAME': 'Python.framework/Versions/3.5/Python',
|
||||
'IO_H': 'Modules/_io/_iomodule.h',
|
||||
'IO_OBJS': '\\',
|
||||
'LDCXXSHARED': 'clang++ -bundle -undefined dynamic_lookup',
|
||||
'LDFLAGS': '',
|
||||
'LDLAST': '',
|
||||
'LDLIBRARY': 'Python.framework/Versions/3.5/Python',
|
||||
'LDLIBRARYDIR': '',
|
||||
'LDSHARED': 'clang -bundle -undefined dynamic_lookup',
|
||||
'LDVERSION': '3.5m',
|
||||
'LIBC': '',
|
||||
'LIBDEST': '/Users/build/.local/Cellar/python3/3.5.0/Frameworks/Python.framework/Versions/3.5/lib/python3.5',
|
||||
'LIBDIR': '/Users/build/.local/Cellar/python3/3.5.0/Frameworks/Python.framework/Versions/3.5/lib',
|
||||
'LIBFFI_INCLUDEDIR': '',
|
||||
'LIBM': '',
|
||||
'LIBOBJDIR': 'Python/',
|
||||
'LIBOBJS': '',
|
||||
'LIBPC': '/Users/build/.local/Cellar/python3/3.5.0/Frameworks/Python.framework/Versions/3.5/lib/pkgconfig',
|
||||
'LIBPL': '/Users/build/.local/Cellar/python3/3.5.0/Frameworks/Python.framework/Versions/3.5/lib/python3.5/config-3.5m',
|
||||
'LIBRARY': 'libpython3.5m.a',
|
||||
'LIBRARY_OBJS': '\\',
|
||||
'LIBRARY_OBJS_OMIT_FROZEN': '\\',
|
||||
'LIBS': '-ldl -framework CoreFoundation',
|
||||
'LIBSUBDIRS': 'tkinter tkinter/test tkinter/test/test_tkinter \\',
|
||||
'LINKCC': 'clang',
|
||||
'LINKFORSHARED': '-Wl,-stack_size,1000000 -framework CoreFoundation '
|
||||
'Python.framework/Versions/3.5/Python',
|
||||
'LIPO_32BIT_FLAGS': '',
|
||||
'LN': 'ln',
|
||||
'LOCALMODLIBS': '',
|
||||
'LOG1P_DROPS_ZERO_SIGN': 0,
|
||||
'MACHDEP': 'darwin',
|
||||
'MACHDEPPATH': ':plat-darwin',
|
||||
'MACHDEPS': 'plat-darwin',
|
||||
'MACHDEP_OBJS': '',
|
||||
'MACHDESTLIB': '/Users/build/.local/Cellar/python3/3.5.0/Frameworks/Python.framework/Versions/3.5/lib/python3.5',
|
||||
'MACOSX_DEPLOYMENT_TARGET': '10.11',
|
||||
'MAINCC': 'clang',
|
||||
'MAJOR_IN_MKDEV': 0,
|
||||
'MAJOR_IN_SYSMACROS': 0,
|
||||
'MAKESETUP': './Modules/makesetup',
|
||||
'MANDIR': '/Users/build/.local/Cellar/python3/3.5.0/share/man',
|
||||
'MKDIR_P': './install-sh -c -d',
|
||||
'MODLIBS': '',
|
||||
'MODOBJS': 'Modules/_threadmodule.o Modules/signalmodule.o '
|
||||
'Modules/posixmodule.o Modules/errnomodule.o '
|
||||
'Modules/pwdmodule.o Modules/_sre.o Modules/_codecsmodule.o '
|
||||
'Modules/_weakref.o Modules/_functoolsmodule.o '
|
||||
'Modules/_operator.o Modules/_collectionsmodule.o '
|
||||
'Modules/itertoolsmodule.o Modules/atexitmodule.o '
|
||||
'Modules/_stat.o Modules/timemodule.o Modules/_localemodule.o '
|
||||
'Modules/_iomodule.o Modules/iobase.o Modules/fileio.o '
|
||||
'Modules/bytesio.o Modules/bufferedio.o Modules/textio.o '
|
||||
'Modules/stringio.o Modules/zipimport.o Modules/faulthandler.o '
|
||||
'Modules/_tracemalloc.o Modules/hashtable.o '
|
||||
'Modules/symtablemodule.o Modules/xxsubtype.o',
|
||||
'MODULE_OBJS': '\\',
|
||||
'MULTIARCH': '',
|
||||
'MVWDELCH_IS_EXPRESSION': 1,
|
||||
'NO_AS_NEEDED': '',
|
||||
'OBJECT_OBJS': '\\',
|
||||
'OPCODETARGETGEN': '\\',
|
||||
'OPCODETARGETGEN_FILES': '\\',
|
||||
'OPCODETARGETS_H': '\\',
|
||||
'OPCODE_H': './Include/opcode.h',
|
||||
'OPCODE_H_DIR': './Include',
|
||||
'OPCODE_H_GEN': 'python ./Tools/scripts/generate_opcode_h.py ./Lib/opcode.py '
|
||||
'./Include/opcode.h',
|
||||
'OPCODE_H_SCRIPT': './Tools/scripts/generate_opcode_h.py',
|
||||
'OPT': '-DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes',
|
||||
'OTHER_LIBTOOL_OPT': '',
|
||||
'PACKAGE_BUGREPORT': 0,
|
||||
'PACKAGE_NAME': 0,
|
||||
'PACKAGE_STRING': 0,
|
||||
'PACKAGE_TARNAME': 0,
|
||||
'PACKAGE_URL': 0,
|
||||
'PACKAGE_VERSION': 0,
|
||||
'PARSER_HEADERS': '\\',
|
||||
'PARSER_OBJS': '\\ Parser/myreadline.o Parser/parsetok.o Parser/tokenizer.o',
|
||||
'PGEN': 'Parser/pgen',
|
||||
'PGENOBJS': '\\ \\',
|
||||
'PGENSRCS': '\\ \\',
|
||||
'PGOBJS': '\\',
|
||||
'PGSRCS': '\\',
|
||||
'PLATDIR': 'plat-darwin',
|
||||
'POBJS': '\\',
|
||||
'POSIX_SEMAPHORES_NOT_ENABLED': 0,
|
||||
'PROFILE_TASK': './Tools/pybench/pybench.py -n 2 --with-gc --with-syscheck',
|
||||
'PSRCS': '\\',
|
||||
'PTHREAD_SYSTEM_SCHED_SUPPORTED': 1,
|
||||
'PURIFY': '',
|
||||
'PY3LIBRARY': '',
|
||||
'PYLONG_BITS_IN_DIGIT': 0,
|
||||
'PYTHON': 'python',
|
||||
'PYTHONFRAMEWORK': 'Python',
|
||||
'PYTHONFRAMEWORKDIR': 'Python.framework',
|
||||
'PYTHONFRAMEWORKINSTALLDIR': '/Users/build/.local/Cellar/python3/3.5.0/Frameworks/Python.framework',
|
||||
'PYTHONFRAMEWORKPREFIX': '/Users/build/.local/Cellar/python3/3.5.0/Frameworks',
|
||||
'PYTHONPATH': ':plat-darwin',
|
||||
'PYTHON_FOR_BUILD': './python.exe -E',
|
||||
'PYTHON_HEADERS': '\\',
|
||||
'PYTHON_OBJS': '\\',
|
||||
'PY_CFLAGS': '-Wno-unused-result -Wsign-compare -Wunreachable-code '
|
||||
'-fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall '
|
||||
'-Wstrict-prototypes',
|
||||
'PY_CFLAGS_NODIST': '-Werror=declaration-after-statement',
|
||||
'PY_CORE_CFLAGS': '-Wno-unused-result -Wsign-compare -Wunreachable-code '
|
||||
'-fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall '
|
||||
'-Wstrict-prototypes -Werror=declaration-after-statement '
|
||||
'-I. -IInclude -I./Include -DPy_BUILD_CORE',
|
||||
'PY_CPPFLAGS': '-I. -IInclude -I./Include',
|
||||
'PY_FORMAT_LONG_LONG': '"ll"',
|
||||
'PY_FORMAT_SIZE_T': '"z"',
|
||||
'PY_LDFLAGS': '',
|
||||
'Py_DEBUG': 0,
|
||||
'Py_ENABLE_SHARED': 0,
|
||||
'Py_HASH_ALGORITHM': 0,
|
||||
'QUICKTESTOPTS': '-x test_subprocess test_io test_lib2to3 \\',
|
||||
'RANLIB': 'ranlib',
|
||||
'READELF': ':',
|
||||
'RESSRCDIR': 'Mac/Resources/framework',
|
||||
'RETSIGTYPE': 'void',
|
||||
'RUNSHARED': 'DYLD_FRAMEWORK_PATH=/private/tmp/python320151125-76692-lzmenz/Python-3.5.0',
|
||||
'SCRIPTDIR': '/Users/build/.local/Cellar/python3/3.5.0/Frameworks/Python.framework/Versions/3.5/lib',
|
||||
'SETPGRP_HAVE_ARG': 0,
|
||||
'SGI_ABI': '',
|
||||
'SHELL': '/bin/sh',
|
||||
'SHLIBS': '-ldl -framework CoreFoundation',
|
||||
'SHLIB_SUFFIX': '.so',
|
||||
'SIGNAL_OBJS': '',
|
||||
'SIGNED_RIGHT_SHIFT_ZERO_FILLS': 0,
|
||||
'SITEPATH': '',
|
||||
'SIZEOF_DOUBLE': 8,
|
||||
'SIZEOF_FLOAT': 4,
|
||||
'SIZEOF_FPOS_T': 8,
|
||||
'SIZEOF_INT': 4,
|
||||
'SIZEOF_LONG': 8,
|
||||
'SIZEOF_LONG_DOUBLE': 16,
|
||||
'SIZEOF_LONG_LONG': 8,
|
||||
'SIZEOF_OFF_T': 8,
|
||||
'SIZEOF_PID_T': 4,
|
||||
'SIZEOF_PTHREAD_T': 8,
|
||||
'SIZEOF_SHORT': 2,
|
||||
'SIZEOF_SIZE_T': 8,
|
||||
'SIZEOF_TIME_T': 8,
|
||||
'SIZEOF_UINTPTR_T': 8,
|
||||
'SIZEOF_VOID_P': 8,
|
||||
'SIZEOF_WCHAR_T': 4,
|
||||
'SIZEOF__BOOL': 1,
|
||||
'SOABI': 'cpython-35m-darwin',
|
||||
'SRCDIRS': 'Parser Grammar Objects Python Modules Mac Programs',
|
||||
'SRC_GDB_HOOKS': './Tools/gdb/libpython.py',
|
||||
'STDC_HEADERS': 1,
|
||||
'STRICT_SYSV_CURSES': "/* Don't use ncurses extensions */",
|
||||
'STRIPFLAG': '-s',
|
||||
'SUBDIRS': '',
|
||||
'SUBDIRSTOO': 'Include Lib Misc',
|
||||
'SYSLIBS': '',
|
||||
'SYS_SELECT_WITH_SYS_TIME': 1,
|
||||
'TANH_PRESERVES_ZERO_SIGN': 1,
|
||||
'TCLTK_INCLUDES': '',
|
||||
'TCLTK_LIBS': '',
|
||||
'TESTOPTS': '',
|
||||
'TESTPATH': '',
|
||||
'TESTPYTHON': 'DYLD_FRAMEWORK_PATH=/private/tmp/python320151125-76692-lzmenz/Python-3.5.0 '
|
||||
'./python.exe',
|
||||
'TESTPYTHONOPTS': '',
|
||||
'TESTRUNNER': 'DYLD_FRAMEWORK_PATH=/private/tmp/python320151125-76692-lzmenz/Python-3.5.0 '
|
||||
'./python.exe ./Tools/scripts/run_tests.py',
|
||||
'TESTTIMEOUT': 3600,
|
||||
'THREADOBJ': 'Python/thread.o',
|
||||
'TIMEMODULE_LIB': 0,
|
||||
'TIME_WITH_SYS_TIME': 1,
|
||||
'TM_IN_SYS_TIME': 0,
|
||||
'UNICODE_DEPS': '\\',
|
||||
'UNIVERSALSDK': '',
|
||||
'USE_COMPUTED_GOTOS': 0,
|
||||
'USE_INLINE': 1,
|
||||
'VA_LIST_IS_ARRAY': 1,
|
||||
'VERSION': '3.5',
|
||||
'WANT_SIGFPE_HANDLER': 0,
|
||||
'WINDOW_HAS_FLAGS': 0,
|
||||
'WITH_DOC_STRINGS': 1,
|
||||
'WITH_DYLD': 1,
|
||||
'WITH_LIBINTL': 0,
|
||||
'WITH_NEXT_FRAMEWORK': 1,
|
||||
'WITH_PYMALLOC': 1,
|
||||
'WITH_THREAD': 1,
|
||||
'WITH_TSC': 0,
|
||||
'WITH_VALGRIND': 0,
|
||||
'X87_DOUBLE_ROUNDING': 0,
|
||||
'XMLLIBSUBDIRS': 'xml xml/dom xml/etree xml/parsers xml/sax',
|
||||
'abs_builddir': '/private/tmp/python320151125-76692-lzmenz/Python-3.5.0',
|
||||
'abs_srcdir': '/private/tmp/python320151125-76692-lzmenz/Python-3.5.0',
|
||||
'datarootdir': '/Users/build/.local/Cellar/python3/3.5.0/share',
|
||||
'exec_prefix': '/Users/build/.local/Cellar/python3/3.5.0/Frameworks/Python.framework/Versions/3.5',
|
||||
'prefix': '/Users/build/.local/Cellar/python3/3.5.0/Frameworks/Python.framework/Versions/3.5',
|
||||
'srcdir': '.'}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,17 +0,0 @@
|
|||
"""Compatibility helpers for the different Python versions."""
|
||||
|
||||
import sys
|
||||
|
||||
PY34 = sys.version_info >= (3, 4)
|
||||
PY35 = sys.version_info >= (3, 5)
|
||||
|
||||
|
||||
def flatten_list_bytes(list_of_data):
|
||||
"""Concatenate a sequence of bytes-like objects."""
|
||||
if not PY34:
|
||||
# On Python 3.3 and older, bytes.join() doesn't handle
|
||||
# memoryview.
|
||||
list_of_data = (
|
||||
bytes(data) if isinstance(data, memoryview) else data
|
||||
for data in list_of_data)
|
||||
return b''.join(list_of_data)
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
"""Constants."""
|
||||
|
||||
# After the connection is lost, log warnings after this many write()s.
|
||||
LOG_THRESHOLD_FOR_CONNLOST_WRITES = 5
|
||||
|
||||
# Seconds to wait before retrying accept().
|
||||
ACCEPT_RETRY_DELAY = 1
|
||||
|
|
@ -1,299 +0,0 @@
|
|||
__all__ = ['coroutine',
|
||||
'iscoroutinefunction', 'iscoroutine']
|
||||
|
||||
import functools
|
||||
import inspect
|
||||
import opcode
|
||||
import os
|
||||
import sys
|
||||
import traceback
|
||||
import types
|
||||
|
||||
from . import compat
|
||||
from . import events
|
||||
from . import futures
|
||||
from .log import logger
|
||||
|
||||
|
||||
# Opcode of "yield from" instruction
|
||||
_YIELD_FROM = opcode.opmap['YIELD_FROM']
|
||||
|
||||
# If you set _DEBUG to true, @coroutine will wrap the resulting
|
||||
# generator objects in a CoroWrapper instance (defined below). That
|
||||
# instance will log a message when the generator is never iterated
|
||||
# over, which may happen when you forget to use "yield from" with a
|
||||
# coroutine call. Note that the value of the _DEBUG flag is taken
|
||||
# when the decorator is used, so to be of any use it must be set
|
||||
# before you define your coroutines. A downside of using this feature
|
||||
# is that tracebacks show entries for the CoroWrapper.__next__ method
|
||||
# when _DEBUG is true.
|
||||
_DEBUG = (not sys.flags.ignore_environment
|
||||
and bool(os.environ.get('PYTHONASYNCIODEBUG')))
|
||||
|
||||
|
||||
try:
|
||||
_types_coroutine = types.coroutine
|
||||
except AttributeError:
|
||||
_types_coroutine = None
|
||||
|
||||
try:
|
||||
_inspect_iscoroutinefunction = inspect.iscoroutinefunction
|
||||
except AttributeError:
|
||||
_inspect_iscoroutinefunction = lambda func: False
|
||||
|
||||
try:
|
||||
from collections.abc import Coroutine as _CoroutineABC, \
|
||||
Awaitable as _AwaitableABC
|
||||
except ImportError:
|
||||
_CoroutineABC = _AwaitableABC = None
|
||||
|
||||
|
||||
# Check for CPython issue #21209
|
||||
def has_yield_from_bug():
|
||||
class MyGen:
|
||||
def __init__(self):
|
||||
self.send_args = None
|
||||
def __iter__(self):
|
||||
return self
|
||||
def __next__(self):
|
||||
return 42
|
||||
def send(self, *what):
|
||||
self.send_args = what
|
||||
return None
|
||||
def yield_from_gen(gen):
|
||||
yield from gen
|
||||
value = (1, 2, 3)
|
||||
gen = MyGen()
|
||||
coro = yield_from_gen(gen)
|
||||
next(coro)
|
||||
coro.send(value)
|
||||
return gen.send_args != (value,)
|
||||
_YIELD_FROM_BUG = has_yield_from_bug()
|
||||
del has_yield_from_bug
|
||||
|
||||
|
||||
def debug_wrapper(gen):
|
||||
# This function is called from 'sys.set_coroutine_wrapper'.
|
||||
# We only wrap here coroutines defined via 'async def' syntax.
|
||||
# Generator-based coroutines are wrapped in @coroutine
|
||||
# decorator.
|
||||
return CoroWrapper(gen, None)
|
||||
|
||||
|
||||
class CoroWrapper:
|
||||
# Wrapper for coroutine object in _DEBUG mode.
|
||||
|
||||
def __init__(self, gen, func=None):
|
||||
assert inspect.isgenerator(gen) or inspect.iscoroutine(gen), gen
|
||||
self.gen = gen
|
||||
self.func = func # Used to unwrap @coroutine decorator
|
||||
self._source_traceback = traceback.extract_stack(sys._getframe(1))
|
||||
self.__name__ = getattr(gen, '__name__', None)
|
||||
self.__qualname__ = getattr(gen, '__qualname__', None)
|
||||
|
||||
def __repr__(self):
|
||||
coro_repr = _format_coroutine(self)
|
||||
if self._source_traceback:
|
||||
frame = self._source_traceback[-1]
|
||||
coro_repr += ', created at %s:%s' % (frame[0], frame[1])
|
||||
return '<%s %s>' % (self.__class__.__name__, coro_repr)
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
return self.gen.send(None)
|
||||
|
||||
if _YIELD_FROM_BUG:
|
||||
# For for CPython issue #21209: using "yield from" and a custom
|
||||
# generator, generator.send(tuple) unpacks the tuple instead of passing
|
||||
# the tuple unchanged. Check if the caller is a generator using "yield
|
||||
# from" to decide if the parameter should be unpacked or not.
|
||||
def send(self, *value):
|
||||
frame = sys._getframe()
|
||||
caller = frame.f_back
|
||||
assert caller.f_lasti >= 0
|
||||
if caller.f_code.co_code[caller.f_lasti] != _YIELD_FROM:
|
||||
value = value[0]
|
||||
return self.gen.send(value)
|
||||
else:
|
||||
def send(self, value):
|
||||
return self.gen.send(value)
|
||||
|
||||
def throw(self, exc):
|
||||
return self.gen.throw(exc)
|
||||
|
||||
def close(self):
|
||||
return self.gen.close()
|
||||
|
||||
@property
|
||||
def gi_frame(self):
|
||||
return self.gen.gi_frame
|
||||
|
||||
@property
|
||||
def gi_running(self):
|
||||
return self.gen.gi_running
|
||||
|
||||
@property
|
||||
def gi_code(self):
|
||||
return self.gen.gi_code
|
||||
|
||||
if compat.PY35:
|
||||
|
||||
__await__ = __iter__ # make compatible with 'await' expression
|
||||
|
||||
@property
|
||||
def gi_yieldfrom(self):
|
||||
return self.gen.gi_yieldfrom
|
||||
|
||||
@property
|
||||
def cr_await(self):
|
||||
return self.gen.cr_await
|
||||
|
||||
@property
|
||||
def cr_running(self):
|
||||
return self.gen.cr_running
|
||||
|
||||
@property
|
||||
def cr_code(self):
|
||||
return self.gen.cr_code
|
||||
|
||||
@property
|
||||
def cr_frame(self):
|
||||
return self.gen.cr_frame
|
||||
|
||||
def __del__(self):
|
||||
# Be careful accessing self.gen.frame -- self.gen might not exist.
|
||||
gen = getattr(self, 'gen', None)
|
||||
frame = getattr(gen, 'gi_frame', None)
|
||||
if frame is None:
|
||||
frame = getattr(gen, 'cr_frame', None)
|
||||
if frame is not None and frame.f_lasti == -1:
|
||||
msg = '%r was never yielded from' % self
|
||||
tb = getattr(self, '_source_traceback', ())
|
||||
if tb:
|
||||
tb = ''.join(traceback.format_list(tb))
|
||||
msg += ('\nCoroutine object created at '
|
||||
'(most recent call last):\n')
|
||||
msg += tb.rstrip()
|
||||
logger.error(msg)
|
||||
|
||||
|
||||
def coroutine(func):
|
||||
"""Decorator to mark coroutines.
|
||||
|
||||
If the coroutine is not yielded from before it is destroyed,
|
||||
an error message is logged.
|
||||
"""
|
||||
if _inspect_iscoroutinefunction(func):
|
||||
# In Python 3.5 that's all we need to do for coroutines
|
||||
# defiend with "async def".
|
||||
# Wrapping in CoroWrapper will happen via
|
||||
# 'sys.set_coroutine_wrapper' function.
|
||||
return func
|
||||
|
||||
if inspect.isgeneratorfunction(func):
|
||||
coro = func
|
||||
else:
|
||||
@functools.wraps(func)
|
||||
def coro(*args, **kw):
|
||||
res = func(*args, **kw)
|
||||
if isinstance(res, futures.Future) or inspect.isgenerator(res):
|
||||
res = yield from res
|
||||
elif _AwaitableABC is not None:
|
||||
# If 'func' returns an Awaitable (new in 3.5) we
|
||||
# want to run it.
|
||||
try:
|
||||
await_meth = res.__await__
|
||||
except AttributeError:
|
||||
pass
|
||||
else:
|
||||
if isinstance(res, _AwaitableABC):
|
||||
res = yield from await_meth()
|
||||
return res
|
||||
|
||||
if not _DEBUG:
|
||||
if _types_coroutine is None:
|
||||
wrapper = coro
|
||||
else:
|
||||
wrapper = _types_coroutine(coro)
|
||||
else:
|
||||
@functools.wraps(func)
|
||||
def wrapper(*args, **kwds):
|
||||
w = CoroWrapper(coro(*args, **kwds), func=func)
|
||||
if w._source_traceback:
|
||||
del w._source_traceback[-1]
|
||||
# Python < 3.5 does not implement __qualname__
|
||||
# on generator objects, so we set it manually.
|
||||
# We use getattr as some callables (such as
|
||||
# functools.partial may lack __qualname__).
|
||||
w.__name__ = getattr(func, '__name__', None)
|
||||
w.__qualname__ = getattr(func, '__qualname__', None)
|
||||
return w
|
||||
|
||||
wrapper._is_coroutine = True # For iscoroutinefunction().
|
||||
return wrapper
|
||||
|
||||
|
||||
def iscoroutinefunction(func):
|
||||
"""Return True if func is a decorated coroutine function."""
|
||||
return (getattr(func, '_is_coroutine', False) or
|
||||
_inspect_iscoroutinefunction(func))
|
||||
|
||||
|
||||
_COROUTINE_TYPES = (types.GeneratorType, CoroWrapper)
|
||||
if _CoroutineABC is not None:
|
||||
_COROUTINE_TYPES += (_CoroutineABC,)
|
||||
|
||||
|
||||
def iscoroutine(obj):
|
||||
"""Return True if obj is a coroutine object."""
|
||||
return isinstance(obj, _COROUTINE_TYPES)
|
||||
|
||||
|
||||
def _format_coroutine(coro):
|
||||
assert iscoroutine(coro)
|
||||
|
||||
coro_name = None
|
||||
if isinstance(coro, CoroWrapper):
|
||||
func = coro.func
|
||||
coro_name = coro.__qualname__
|
||||
if coro_name is not None:
|
||||
coro_name = '{}()'.format(coro_name)
|
||||
else:
|
||||
func = coro
|
||||
|
||||
if coro_name is None:
|
||||
coro_name = events._format_callback(func, ())
|
||||
|
||||
try:
|
||||
coro_code = coro.gi_code
|
||||
except AttributeError:
|
||||
coro_code = coro.cr_code
|
||||
|
||||
try:
|
||||
coro_frame = coro.gi_frame
|
||||
except AttributeError:
|
||||
coro_frame = coro.cr_frame
|
||||
|
||||
filename = coro_code.co_filename
|
||||
if (isinstance(coro, CoroWrapper)
|
||||
and not inspect.isgeneratorfunction(coro.func)
|
||||
and coro.func is not None):
|
||||
filename, lineno = events._get_function_source(coro.func)
|
||||
if coro_frame is None:
|
||||
coro_repr = ('%s done, defined at %s:%s'
|
||||
% (coro_name, filename, lineno))
|
||||
else:
|
||||
coro_repr = ('%s running, defined at %s:%s'
|
||||
% (coro_name, filename, lineno))
|
||||
elif coro_frame is not None:
|
||||
lineno = coro_frame.f_lineno
|
||||
coro_repr = ('%s running at %s:%s'
|
||||
% (coro_name, filename, lineno))
|
||||
else:
|
||||
lineno = coro_code.co_firstlineno
|
||||
coro_repr = ('%s done, defined at %s:%s'
|
||||
% (coro_name, filename, lineno))
|
||||
|
||||
return coro_repr
|
||||
|
|
@ -1,411 +0,0 @@
|
|||
"""A Future class similar to the one in PEP 3148."""
|
||||
|
||||
__all__ = ['CancelledError', 'TimeoutError',
|
||||
'InvalidStateError',
|
||||
'Future', 'wrap_future',
|
||||
]
|
||||
|
||||
import concurrent.futures._base
|
||||
import logging
|
||||
import reprlib
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
from . import compat
|
||||
from . import events
|
||||
|
||||
# States for Future.
|
||||
_PENDING = 'PENDING'
|
||||
_CANCELLED = 'CANCELLED'
|
||||
_FINISHED = 'FINISHED'
|
||||
|
||||
Error = concurrent.futures._base.Error
|
||||
CancelledError = concurrent.futures.CancelledError
|
||||
TimeoutError = concurrent.futures.TimeoutError
|
||||
|
||||
STACK_DEBUG = logging.DEBUG - 1 # heavy-duty debugging
|
||||
|
||||
|
||||
class InvalidStateError(Error):
|
||||
"""The operation is not allowed in this state."""
|
||||
|
||||
|
||||
class _TracebackLogger:
|
||||
"""Helper to log a traceback upon destruction if not cleared.
|
||||
|
||||
This solves a nasty problem with Futures and Tasks that have an
|
||||
exception set: if nobody asks for the exception, the exception is
|
||||
never logged. This violates the Zen of Python: 'Errors should
|
||||
never pass silently. Unless explicitly silenced.'
|
||||
|
||||
However, we don't want to log the exception as soon as
|
||||
set_exception() is called: if the calling code is written
|
||||
properly, it will get the exception and handle it properly. But
|
||||
we *do* want to log it if result() or exception() was never called
|
||||
-- otherwise developers waste a lot of time wondering why their
|
||||
buggy code fails silently.
|
||||
|
||||
An earlier attempt added a __del__() method to the Future class
|
||||
itself, but this backfired because the presence of __del__()
|
||||
prevents garbage collection from breaking cycles. A way out of
|
||||
this catch-22 is to avoid having a __del__() method on the Future
|
||||
class itself, but instead to have a reference to a helper object
|
||||
with a __del__() method that logs the traceback, where we ensure
|
||||
that the helper object doesn't participate in cycles, and only the
|
||||
Future has a reference to it.
|
||||
|
||||
The helper object is added when set_exception() is called. When
|
||||
the Future is collected, and the helper is present, the helper
|
||||
object is also collected, and its __del__() method will log the
|
||||
traceback. When the Future's result() or exception() method is
|
||||
called (and a helper object is present), it removes the helper
|
||||
object, after calling its clear() method to prevent it from
|
||||
logging.
|
||||
|
||||
One downside is that we do a fair amount of work to extract the
|
||||
traceback from the exception, even when it is never logged. It
|
||||
would seem cheaper to just store the exception object, but that
|
||||
references the traceback, which references stack frames, which may
|
||||
reference the Future, which references the _TracebackLogger, and
|
||||
then the _TracebackLogger would be included in a cycle, which is
|
||||
what we're trying to avoid! As an optimization, we don't
|
||||
immediately format the exception; we only do the work when
|
||||
activate() is called, which call is delayed until after all the
|
||||
Future's callbacks have run. Since usually a Future has at least
|
||||
one callback (typically set by 'yield from') and usually that
|
||||
callback extracts the callback, thereby removing the need to
|
||||
format the exception.
|
||||
|
||||
PS. I don't claim credit for this solution. I first heard of it
|
||||
in a discussion about closing files when they are collected.
|
||||
"""
|
||||
|
||||
__slots__ = ('loop', 'source_traceback', 'exc', 'tb')
|
||||
|
||||
def __init__(self, future, exc):
|
||||
self.loop = future._loop
|
||||
self.source_traceback = future._source_traceback
|
||||
self.exc = exc
|
||||
self.tb = None
|
||||
|
||||
def activate(self):
|
||||
exc = self.exc
|
||||
if exc is not None:
|
||||
self.exc = None
|
||||
self.tb = traceback.format_exception(exc.__class__, exc,
|
||||
exc.__traceback__)
|
||||
|
||||
def clear(self):
|
||||
self.exc = None
|
||||
self.tb = None
|
||||
|
||||
def __del__(self):
|
||||
if self.tb:
|
||||
msg = 'Future/Task exception was never retrieved\n'
|
||||
if self.source_traceback:
|
||||
src = ''.join(traceback.format_list(self.source_traceback))
|
||||
msg += 'Future/Task created at (most recent call last):\n'
|
||||
msg += '%s\n' % src.rstrip()
|
||||
msg += ''.join(self.tb).rstrip()
|
||||
self.loop.call_exception_handler({'message': msg})
|
||||
|
||||
|
||||
class Future:
|
||||
"""This class is *almost* compatible with concurrent.futures.Future.
|
||||
|
||||
Differences:
|
||||
|
||||
- result() and exception() do not take a timeout argument and
|
||||
raise an exception when the future isn't done yet.
|
||||
|
||||
- Callbacks registered with add_done_callback() are always called
|
||||
via the event loop's call_soon_threadsafe().
|
||||
|
||||
- This class is not compatible with the wait() and as_completed()
|
||||
methods in the concurrent.futures package.
|
||||
|
||||
(In Python 3.4 or later we may be able to unify the implementations.)
|
||||
"""
|
||||
|
||||
# Class variables serving as defaults for instance variables.
|
||||
_state = _PENDING
|
||||
_result = None
|
||||
_exception = None
|
||||
_loop = None
|
||||
_source_traceback = None
|
||||
|
||||
_blocking = False # proper use of future (yield vs yield from)
|
||||
|
||||
_log_traceback = False # Used for Python 3.4 and later
|
||||
_tb_logger = None # Used for Python 3.3 only
|
||||
|
||||
def __init__(self, *, loop=None):
|
||||
"""Initialize the future.
|
||||
|
||||
The optional event_loop argument allows to explicitly set the event
|
||||
loop object used by the future. If it's not provided, the future uses
|
||||
the default event loop.
|
||||
"""
|
||||
if loop is None:
|
||||
self._loop = events.get_event_loop()
|
||||
else:
|
||||
self._loop = loop
|
||||
self._callbacks = []
|
||||
if self._loop.get_debug():
|
||||
self._source_traceback = traceback.extract_stack(sys._getframe(1))
|
||||
|
||||
def _format_callbacks(self):
|
||||
cb = self._callbacks
|
||||
size = len(cb)
|
||||
if not size:
|
||||
cb = ''
|
||||
|
||||
def format_cb(callback):
|
||||
return events._format_callback_source(callback, ())
|
||||
|
||||
if size == 1:
|
||||
cb = format_cb(cb[0])
|
||||
elif size == 2:
|
||||
cb = '{}, {}'.format(format_cb(cb[0]), format_cb(cb[1]))
|
||||
elif size > 2:
|
||||
cb = '{}, <{} more>, {}'.format(format_cb(cb[0]),
|
||||
size-2,
|
||||
format_cb(cb[-1]))
|
||||
return 'cb=[%s]' % cb
|
||||
|
||||
def _repr_info(self):
|
||||
info = [self._state.lower()]
|
||||
if self._state == _FINISHED:
|
||||
if self._exception is not None:
|
||||
info.append('exception={!r}'.format(self._exception))
|
||||
else:
|
||||
# use reprlib to limit the length of the output, especially
|
||||
# for very long strings
|
||||
result = reprlib.repr(self._result)
|
||||
info.append('result={}'.format(result))
|
||||
if self._callbacks:
|
||||
info.append(self._format_callbacks())
|
||||
if self._source_traceback:
|
||||
frame = self._source_traceback[-1]
|
||||
info.append('created at %s:%s' % (frame[0], frame[1]))
|
||||
return info
|
||||
|
||||
def __repr__(self):
|
||||
info = self._repr_info()
|
||||
return '<%s %s>' % (self.__class__.__name__, ' '.join(info))
|
||||
|
||||
# On Python 3.3 and older, objects with a destructor part of a reference
|
||||
# cycle are never destroyed. It's not more the case on Python 3.4 thanks
|
||||
# to the PEP 442.
|
||||
if compat.PY34:
|
||||
def __del__(self):
|
||||
if not self._log_traceback:
|
||||
# set_exception() was not called, or result() or exception()
|
||||
# has consumed the exception
|
||||
return
|
||||
exc = self._exception
|
||||
context = {
|
||||
'message': ('%s exception was never retrieved'
|
||||
% self.__class__.__name__),
|
||||
'exception': exc,
|
||||
'future': self,
|
||||
}
|
||||
if self._source_traceback:
|
||||
context['source_traceback'] = self._source_traceback
|
||||
self._loop.call_exception_handler(context)
|
||||
|
||||
def cancel(self):
|
||||
"""Cancel the future and schedule callbacks.
|
||||
|
||||
If the future is already done or cancelled, return False. Otherwise,
|
||||
change the future's state to cancelled, schedule the callbacks and
|
||||
return True.
|
||||
"""
|
||||
if self._state != _PENDING:
|
||||
return False
|
||||
self._state = _CANCELLED
|
||||
self._schedule_callbacks()
|
||||
return True
|
||||
|
||||
def _schedule_callbacks(self):
|
||||
"""Internal: Ask the event loop to call all callbacks.
|
||||
|
||||
The callbacks are scheduled to be called as soon as possible. Also
|
||||
clears the callback list.
|
||||
"""
|
||||
callbacks = self._callbacks[:]
|
||||
if not callbacks:
|
||||
return
|
||||
|
||||
self._callbacks[:] = []
|
||||
for callback in callbacks:
|
||||
self._loop.call_soon(callback, self)
|
||||
|
||||
def cancelled(self):
|
||||
"""Return True if the future was cancelled."""
|
||||
return self._state == _CANCELLED
|
||||
|
||||
# Don't implement running(); see http://bugs.python.org/issue18699
|
||||
|
||||
def done(self):
|
||||
"""Return True if the future is done.
|
||||
|
||||
Done means either that a result / exception are available, or that the
|
||||
future was cancelled.
|
||||
"""
|
||||
return self._state != _PENDING
|
||||
|
||||
def result(self):
|
||||
"""Return the result this future represents.
|
||||
|
||||
If the future has been cancelled, raises CancelledError. If the
|
||||
future's result isn't yet available, raises InvalidStateError. If
|
||||
the future is done and has an exception set, this exception is raised.
|
||||
"""
|
||||
if self._state == _CANCELLED:
|
||||
raise CancelledError
|
||||
if self._state != _FINISHED:
|
||||
raise InvalidStateError('Result is not ready.')
|
||||
self._log_traceback = False
|
||||
if self._tb_logger is not None:
|
||||
self._tb_logger.clear()
|
||||
self._tb_logger = None
|
||||
if self._exception is not None:
|
||||
raise self._exception
|
||||
return self._result
|
||||
|
||||
def exception(self):
|
||||
"""Return the exception that was set on this future.
|
||||
|
||||
The exception (or None if no exception was set) is returned only if
|
||||
the future is done. If the future has been cancelled, raises
|
||||
CancelledError. If the future isn't done yet, raises
|
||||
InvalidStateError.
|
||||
"""
|
||||
if self._state == _CANCELLED:
|
||||
raise CancelledError
|
||||
if self._state != _FINISHED:
|
||||
raise InvalidStateError('Exception is not set.')
|
||||
self._log_traceback = False
|
||||
if self._tb_logger is not None:
|
||||
self._tb_logger.clear()
|
||||
self._tb_logger = None
|
||||
return self._exception
|
||||
|
||||
def add_done_callback(self, fn):
|
||||
"""Add a callback to be run when the future becomes done.
|
||||
|
||||
The callback is called with a single argument - the future object. If
|
||||
the future is already done when this is called, the callback is
|
||||
scheduled with call_soon.
|
||||
"""
|
||||
if self._state != _PENDING:
|
||||
self._loop.call_soon(fn, self)
|
||||
else:
|
||||
self._callbacks.append(fn)
|
||||
|
||||
# New method not in PEP 3148.
|
||||
|
||||
def remove_done_callback(self, fn):
|
||||
"""Remove all instances of a callback from the "call when done" list.
|
||||
|
||||
Returns the number of callbacks removed.
|
||||
"""
|
||||
filtered_callbacks = [f for f in self._callbacks if f != fn]
|
||||
removed_count = len(self._callbacks) - len(filtered_callbacks)
|
||||
if removed_count:
|
||||
self._callbacks[:] = filtered_callbacks
|
||||
return removed_count
|
||||
|
||||
# So-called internal methods (note: no set_running_or_notify_cancel()).
|
||||
|
||||
def _set_result_unless_cancelled(self, result):
|
||||
"""Helper setting the result only if the future was not cancelled."""
|
||||
if self.cancelled():
|
||||
return
|
||||
self.set_result(result)
|
||||
|
||||
def set_result(self, result):
|
||||
"""Mark the future done and set its result.
|
||||
|
||||
If the future is already done when this method is called, raises
|
||||
InvalidStateError.
|
||||
"""
|
||||
if self._state != _PENDING:
|
||||
raise InvalidStateError('{}: {!r}'.format(self._state, self))
|
||||
self._result = result
|
||||
self._state = _FINISHED
|
||||
self._schedule_callbacks()
|
||||
|
||||
def set_exception(self, exception):
|
||||
"""Mark the future done and set an exception.
|
||||
|
||||
If the future is already done when this method is called, raises
|
||||
InvalidStateError.
|
||||
"""
|
||||
if self._state != _PENDING:
|
||||
raise InvalidStateError('{}: {!r}'.format(self._state, self))
|
||||
if isinstance(exception, type):
|
||||
exception = exception()
|
||||
self._exception = exception
|
||||
self._state = _FINISHED
|
||||
self._schedule_callbacks()
|
||||
if compat.PY34:
|
||||
self._log_traceback = True
|
||||
else:
|
||||
self._tb_logger = _TracebackLogger(self, exception)
|
||||
# Arrange for the logger to be activated after all callbacks
|
||||
# have had a chance to call result() or exception().
|
||||
self._loop.call_soon(self._tb_logger.activate)
|
||||
|
||||
# Truly internal methods.
|
||||
|
||||
def _copy_state(self, other):
|
||||
"""Internal helper to copy state from another Future.
|
||||
|
||||
The other Future may be a concurrent.futures.Future.
|
||||
"""
|
||||
assert other.done()
|
||||
if self.cancelled():
|
||||
return
|
||||
assert not self.done()
|
||||
if other.cancelled():
|
||||
self.cancel()
|
||||
else:
|
||||
exception = other.exception()
|
||||
if exception is not None:
|
||||
self.set_exception(exception)
|
||||
else:
|
||||
result = other.result()
|
||||
self.set_result(result)
|
||||
|
||||
def __iter__(self):
|
||||
if not self.done():
|
||||
self._blocking = True
|
||||
yield self # This tells Task to wait for completion.
|
||||
assert self.done(), "yield from wasn't used with future"
|
||||
return self.result() # May raise too.
|
||||
|
||||
if compat.PY35:
|
||||
__await__ = __iter__ # make compatible with 'await' expression
|
||||
|
||||
|
||||
def wrap_future(fut, *, loop=None):
|
||||
"""Wrap concurrent.futures.Future object."""
|
||||
if isinstance(fut, Future):
|
||||
return fut
|
||||
assert isinstance(fut, concurrent.futures.Future), \
|
||||
'concurrent.futures.Future is expected, got {!r}'.format(fut)
|
||||
if loop is None:
|
||||
loop = events.get_event_loop()
|
||||
new_future = Future(loop=loop)
|
||||
|
||||
def _check_cancel_other(f):
|
||||
if f.cancelled():
|
||||
fut.cancel()
|
||||
|
||||
new_future.add_done_callback(_check_cancel_other)
|
||||
fut.add_done_callback(
|
||||
lambda future: loop.call_soon_threadsafe(
|
||||
new_future._copy_state, future))
|
||||
return new_future
|
||||
|
|
@ -1,324 +0,0 @@
|
|||
"""Queues"""
|
||||
|
||||
__all__ = ['Queue', 'PriorityQueue', 'LifoQueue', 'QueueFull', 'QueueEmpty']
|
||||
|
||||
import collections
|
||||
import heapq
|
||||
|
||||
from . import compat
|
||||
from . import events
|
||||
from . import futures
|
||||
from . import locks
|
||||
from .coroutines import coroutine
|
||||
|
||||
|
||||
class QueueEmpty(Exception):
|
||||
"""Exception raised when Queue.get_nowait() is called on a Queue object
|
||||
which is empty.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class QueueFull(Exception):
|
||||
"""Exception raised when the Queue.put_nowait() method is called on a Queue
|
||||
object which is full.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class Queue:
|
||||
"""A queue, useful for coordinating producer and consumer coroutines.
|
||||
|
||||
If maxsize is less than or equal to zero, the queue size is infinite. If it
|
||||
is an integer greater than 0, then "yield from put()" will block when the
|
||||
queue reaches maxsize, until an item is removed by get().
|
||||
|
||||
Unlike the standard library Queue, you can reliably know this Queue's size
|
||||
with qsize(), since your single-threaded asyncio application won't be
|
||||
interrupted between calling qsize() and doing an operation on the Queue.
|
||||
"""
|
||||
|
||||
def __init__(self, maxsize=0, *, loop=None):
|
||||
if loop is None:
|
||||
self._loop = events.get_event_loop()
|
||||
else:
|
||||
self._loop = loop
|
||||
self._maxsize = maxsize
|
||||
|
||||
# Futures.
|
||||
self._getters = collections.deque()
|
||||
# Futures
|
||||
self._putters = collections.deque()
|
||||
self._unfinished_tasks = 0
|
||||
self._finished = locks.Event(loop=self._loop)
|
||||
self._finished.set()
|
||||
self._init(maxsize)
|
||||
|
||||
# These three are overridable in subclasses.
|
||||
|
||||
def _init(self, maxsize):
|
||||
self._queue = collections.deque()
|
||||
|
||||
def _get(self):
|
||||
return self._queue.popleft()
|
||||
|
||||
def _put(self, item):
|
||||
self._queue.append(item)
|
||||
|
||||
# End of the overridable methods.
|
||||
|
||||
def __put_internal(self, item):
|
||||
self._put(item)
|
||||
self._unfinished_tasks += 1
|
||||
self._finished.clear()
|
||||
|
||||
def __repr__(self):
|
||||
return '<{} at {:#x} {}>'.format(
|
||||
type(self).__name__, id(self), self._format())
|
||||
|
||||
def __str__(self):
|
||||
return '<{} {}>'.format(type(self).__name__, self._format())
|
||||
|
||||
def _format(self):
|
||||
result = 'maxsize={!r}'.format(self._maxsize)
|
||||
if getattr(self, '_queue', None):
|
||||
result += ' _queue={!r}'.format(list(self._queue))
|
||||
if self._getters:
|
||||
result += ' _getters[{}]'.format(len(self._getters))
|
||||
if self._putters:
|
||||
result += ' _putters[{}]'.format(len(self._putters))
|
||||
if self._unfinished_tasks:
|
||||
result += ' tasks={}'.format(self._unfinished_tasks)
|
||||
return result
|
||||
|
||||
def _consume_done_getters(self):
|
||||
# Delete waiters at the head of the get() queue who've timed out.
|
||||
while self._getters and self._getters[0].done():
|
||||
self._getters.popleft()
|
||||
|
||||
def _consume_done_putters(self):
|
||||
# Delete waiters at the head of the put() queue who've timed out.
|
||||
while self._putters and self._putters[0].done():
|
||||
self._putters.popleft()
|
||||
|
||||
def qsize(self):
|
||||
"""Number of items in the queue."""
|
||||
return len(self._queue)
|
||||
|
||||
@property
|
||||
def maxsize(self):
|
||||
"""Number of items allowed in the queue."""
|
||||
return self._maxsize
|
||||
|
||||
def empty(self):
|
||||
"""Return True if the queue is empty, False otherwise."""
|
||||
return not self._queue
|
||||
|
||||
def full(self):
|
||||
"""Return True if there are maxsize items in the queue.
|
||||
|
||||
Note: if the Queue was initialized with maxsize=0 (the default),
|
||||
then full() is never True.
|
||||
"""
|
||||
if self._maxsize <= 0:
|
||||
return False
|
||||
else:
|
||||
return self.qsize() >= self._maxsize
|
||||
|
||||
@coroutine
|
||||
def put(self, item):
|
||||
"""Put an item into the queue.
|
||||
|
||||
Put an item into the queue. If the queue is full, wait until a free
|
||||
slot is available before adding item.
|
||||
|
||||
This method is a coroutine.
|
||||
"""
|
||||
self._consume_done_getters()
|
||||
if self._getters:
|
||||
assert not self._queue, (
|
||||
'queue non-empty, why are getters waiting?')
|
||||
|
||||
getter = self._getters.popleft()
|
||||
self.__put_internal(item)
|
||||
|
||||
# getter cannot be cancelled, we just removed done getters
|
||||
getter.set_result(self._get())
|
||||
|
||||
elif self._maxsize > 0 and self._maxsize <= self.qsize():
|
||||
waiter = futures.Future(loop=self._loop)
|
||||
|
||||
self._putters.append(waiter)
|
||||
yield from waiter
|
||||
self._put(item)
|
||||
|
||||
else:
|
||||
self.__put_internal(item)
|
||||
|
||||
def put_nowait(self, item):
|
||||
"""Put an item into the queue without blocking.
|
||||
|
||||
If no free slot is immediately available, raise QueueFull.
|
||||
"""
|
||||
self._consume_done_getters()
|
||||
if self._getters:
|
||||
assert not self._queue, (
|
||||
'queue non-empty, why are getters waiting?')
|
||||
|
||||
getter = self._getters.popleft()
|
||||
self.__put_internal(item)
|
||||
|
||||
# getter cannot be cancelled, we just removed done getters
|
||||
getter.set_result(self._get())
|
||||
|
||||
elif self._maxsize > 0 and self._maxsize <= self.qsize():
|
||||
raise QueueFull
|
||||
else:
|
||||
self.__put_internal(item)
|
||||
|
||||
@coroutine
|
||||
def get(self):
|
||||
"""Remove and return an item from the queue.
|
||||
|
||||
If queue is empty, wait until an item is available.
|
||||
|
||||
This method is a coroutine.
|
||||
"""
|
||||
self._consume_done_putters()
|
||||
if self._putters:
|
||||
assert self.full(), 'queue not full, why are putters waiting?'
|
||||
putter = self._putters.popleft()
|
||||
|
||||
# When a getter runs and frees up a slot so this putter can
|
||||
# run, we need to defer the put for a tick to ensure that
|
||||
# getters and putters alternate perfectly. See
|
||||
# ChannelTest.test_wait.
|
||||
self._loop.call_soon(putter._set_result_unless_cancelled, None)
|
||||
|
||||
return self._get()
|
||||
|
||||
elif self.qsize():
|
||||
return self._get()
|
||||
else:
|
||||
waiter = futures.Future(loop=self._loop)
|
||||
self._getters.append(waiter)
|
||||
try:
|
||||
return (yield from waiter)
|
||||
except futures.CancelledError:
|
||||
# if we get CancelledError, it means someone cancelled this
|
||||
# get() coroutine. But there is a chance that the waiter
|
||||
# already is ready and contains an item that has just been
|
||||
# removed from the queue. In this case, we need to put the item
|
||||
# back into the front of the queue. This get() must either
|
||||
# succeed without fault or, if it gets cancelled, it must be as
|
||||
# if it never happened.
|
||||
if waiter.done():
|
||||
self._put_it_back(waiter.result())
|
||||
raise
|
||||
|
||||
def _put_it_back(self, item):
|
||||
"""
|
||||
This is called when we have a waiter to get() an item and this waiter
|
||||
gets cancelled. In this case, we put the item back: wake up another
|
||||
waiter or put it in the _queue.
|
||||
"""
|
||||
self._consume_done_getters()
|
||||
if self._getters:
|
||||
assert not self._queue, (
|
||||
'queue non-empty, why are getters waiting?')
|
||||
|
||||
getter = self._getters.popleft()
|
||||
self.__put_internal(item)
|
||||
|
||||
# getter cannot be cancelled, we just removed done getters
|
||||
getter.set_result(item)
|
||||
else:
|
||||
self._queue.appendleft(item)
|
||||
|
||||
def get_nowait(self):
|
||||
"""Remove and return an item from the queue.
|
||||
|
||||
Return an item if one is immediately available, else raise QueueEmpty.
|
||||
"""
|
||||
self._consume_done_putters()
|
||||
if self._putters:
|
||||
assert self.full(), 'queue not full, why are putters waiting?'
|
||||
putter = self._putters.popleft()
|
||||
# Wake putter on next tick.
|
||||
|
||||
# getter cannot be cancelled, we just removed done putters
|
||||
putter.set_result(None)
|
||||
|
||||
return self._get()
|
||||
|
||||
elif self.qsize():
|
||||
return self._get()
|
||||
else:
|
||||
raise QueueEmpty
|
||||
|
||||
def task_done(self):
|
||||
"""Indicate that a formerly enqueued task is complete.
|
||||
|
||||
Used by queue consumers. For each get() used to fetch a task,
|
||||
a subsequent call to task_done() tells the queue that the processing
|
||||
on the task is complete.
|
||||
|
||||
If a join() is currently blocking, it will resume when all items have
|
||||
been processed (meaning that a task_done() call was received for every
|
||||
item that had been put() into the queue).
|
||||
|
||||
Raises ValueError if called more times than there were items placed in
|
||||
the queue.
|
||||
"""
|
||||
if self._unfinished_tasks <= 0:
|
||||
raise ValueError('task_done() called too many times')
|
||||
self._unfinished_tasks -= 1
|
||||
if self._unfinished_tasks == 0:
|
||||
self._finished.set()
|
||||
|
||||
@coroutine
|
||||
def join(self):
|
||||
"""Block until all items in the queue have been gotten and processed.
|
||||
|
||||
The count of unfinished tasks goes up whenever an item is added to the
|
||||
queue. The count goes down whenever a consumer calls task_done() to
|
||||
indicate that the item was retrieved and all work on it is complete.
|
||||
When the count of unfinished tasks drops to zero, join() unblocks.
|
||||
"""
|
||||
if self._unfinished_tasks > 0:
|
||||
yield from self._finished.wait()
|
||||
|
||||
|
||||
class PriorityQueue(Queue):
|
||||
"""A subclass of Queue; retrieves entries in priority order (lowest first).
|
||||
|
||||
Entries are typically tuples of the form: (priority number, data).
|
||||
"""
|
||||
|
||||
def _init(self, maxsize):
|
||||
self._queue = []
|
||||
|
||||
def _put(self, item, heappush=heapq.heappush):
|
||||
heappush(self._queue, item)
|
||||
|
||||
def _get(self, heappop=heapq.heappop):
|
||||
return heappop(self._queue)
|
||||
|
||||
|
||||
class LifoQueue(Queue):
|
||||
"""A subclass of Queue that retrieves most recently added entries first."""
|
||||
|
||||
def _init(self, maxsize):
|
||||
self._queue = []
|
||||
|
||||
def _put(self, item):
|
||||
self._queue.append(item)
|
||||
|
||||
def _get(self):
|
||||
return self._queue.pop()
|
||||
|
||||
|
||||
if not compat.PY35:
|
||||
JoinableQueue = Queue
|
||||
"""Deprecated alias for Queue."""
|
||||
__all__.append('JoinableQueue')
|
||||
|
|
@ -1,519 +0,0 @@
|
|||
"""Stream-related things."""
|
||||
|
||||
__all__ = ['StreamReader', 'StreamWriter', 'StreamReaderProtocol',
|
||||
'open_connection', 'start_server',
|
||||
'IncompleteReadError',
|
||||
]
|
||||
|
||||
import socket
|
||||
|
||||
if hasattr(socket, 'AF_UNIX'):
|
||||
__all__.extend(['open_unix_connection', 'start_unix_server'])
|
||||
|
||||
from . import coroutines
|
||||
from . import compat
|
||||
from . import events
|
||||
from . import futures
|
||||
from . import protocols
|
||||
from .coroutines import coroutine
|
||||
from .log import logger
|
||||
|
||||
|
||||
_DEFAULT_LIMIT = 2**16
|
||||
|
||||
|
||||
class IncompleteReadError(EOFError):
|
||||
"""
|
||||
Incomplete read error. Attributes:
|
||||
|
||||
- partial: read bytes string before the end of stream was reached
|
||||
- expected: total number of expected bytes
|
||||
"""
|
||||
def __init__(self, partial, expected):
|
||||
EOFError.__init__(self, "%s bytes read on a total of %s expected bytes"
|
||||
% (len(partial), expected))
|
||||
self.partial = partial
|
||||
self.expected = expected
|
||||
|
||||
|
||||
@coroutine
|
||||
def open_connection(host=None, port=None, *,
|
||||
loop=None, limit=_DEFAULT_LIMIT, **kwds):
|
||||
"""A wrapper for create_connection() returning a (reader, writer) pair.
|
||||
|
||||
The reader returned is a StreamReader instance; the writer is a
|
||||
StreamWriter instance.
|
||||
|
||||
The arguments are all the usual arguments to create_connection()
|
||||
except protocol_factory; most common are positional host and port,
|
||||
with various optional keyword arguments following.
|
||||
|
||||
Additional optional keyword arguments are loop (to set the event loop
|
||||
instance to use) and limit (to set the buffer limit passed to the
|
||||
StreamReader).
|
||||
|
||||
(If you want to customize the StreamReader and/or
|
||||
StreamReaderProtocol classes, just copy the code -- there's
|
||||
really nothing special here except some convenience.)
|
||||
"""
|
||||
if loop is None:
|
||||
loop = events.get_event_loop()
|
||||
reader = StreamReader(limit=limit, loop=loop)
|
||||
protocol = StreamReaderProtocol(reader, loop=loop)
|
||||
transport, _ = yield from loop.create_connection(
|
||||
lambda: protocol, host, port, **kwds)
|
||||
writer = StreamWriter(transport, protocol, reader, loop)
|
||||
return reader, writer
|
||||
|
||||
|
||||
@coroutine
|
||||
def start_server(client_connected_cb, host=None, port=None, *,
|
||||
loop=None, limit=_DEFAULT_LIMIT, **kwds):
|
||||
"""Start a socket server, call back for each client connected.
|
||||
|
||||
The first parameter, `client_connected_cb`, takes two parameters:
|
||||
client_reader, client_writer. client_reader is a StreamReader
|
||||
object, while client_writer is a StreamWriter object. This
|
||||
parameter can either be a plain callback function or a coroutine;
|
||||
if it is a coroutine, it will be automatically converted into a
|
||||
Task.
|
||||
|
||||
The rest of the arguments are all the usual arguments to
|
||||
loop.create_server() except protocol_factory; most common are
|
||||
positional host and port, with various optional keyword arguments
|
||||
following. The return value is the same as loop.create_server().
|
||||
|
||||
Additional optional keyword arguments are loop (to set the event loop
|
||||
instance to use) and limit (to set the buffer limit passed to the
|
||||
StreamReader).
|
||||
|
||||
The return value is the same as loop.create_server(), i.e. a
|
||||
Server object which can be used to stop the service.
|
||||
"""
|
||||
if loop is None:
|
||||
loop = events.get_event_loop()
|
||||
|
||||
def factory():
|
||||
reader = StreamReader(limit=limit, loop=loop)
|
||||
protocol = StreamReaderProtocol(reader, client_connected_cb,
|
||||
loop=loop)
|
||||
return protocol
|
||||
|
||||
return (yield from loop.create_server(factory, host, port, **kwds))
|
||||
|
||||
|
||||
if hasattr(socket, 'AF_UNIX'):
|
||||
# UNIX Domain Sockets are supported on this platform
|
||||
|
||||
@coroutine
|
||||
def open_unix_connection(path=None, *,
|
||||
loop=None, limit=_DEFAULT_LIMIT, **kwds):
|
||||
"""Similar to `open_connection` but works with UNIX Domain Sockets."""
|
||||
if loop is None:
|
||||
loop = events.get_event_loop()
|
||||
reader = StreamReader(limit=limit, loop=loop)
|
||||
protocol = StreamReaderProtocol(reader, loop=loop)
|
||||
transport, _ = yield from loop.create_unix_connection(
|
||||
lambda: protocol, path, **kwds)
|
||||
writer = StreamWriter(transport, protocol, reader, loop)
|
||||
return reader, writer
|
||||
|
||||
|
||||
@coroutine
|
||||
def start_unix_server(client_connected_cb, path=None, *,
|
||||
loop=None, limit=_DEFAULT_LIMIT, **kwds):
|
||||
"""Similar to `start_server` but works with UNIX Domain Sockets."""
|
||||
if loop is None:
|
||||
loop = events.get_event_loop()
|
||||
|
||||
def factory():
|
||||
reader = StreamReader(limit=limit, loop=loop)
|
||||
protocol = StreamReaderProtocol(reader, client_connected_cb,
|
||||
loop=loop)
|
||||
return protocol
|
||||
|
||||
return (yield from loop.create_unix_server(factory, path, **kwds))
|
||||
|
||||
|
||||
class FlowControlMixin(protocols.Protocol):
|
||||
"""Reusable flow control logic for StreamWriter.drain().
|
||||
|
||||
This implements the protocol methods pause_writing(),
|
||||
resume_reading() and connection_lost(). If the subclass overrides
|
||||
these it must call the super methods.
|
||||
|
||||
StreamWriter.drain() must wait for _drain_helper() coroutine.
|
||||
"""
|
||||
|
||||
def __init__(self, loop=None):
|
||||
if loop is None:
|
||||
self._loop = events.get_event_loop()
|
||||
else:
|
||||
self._loop = loop
|
||||
self._paused = False
|
||||
self._drain_waiter = None
|
||||
self._connection_lost = False
|
||||
|
||||
def pause_writing(self):
|
||||
assert not self._paused
|
||||
self._paused = True
|
||||
if self._loop.get_debug():
|
||||
logger.debug("%r pauses writing", self)
|
||||
|
||||
def resume_writing(self):
|
||||
assert self._paused
|
||||
self._paused = False
|
||||
if self._loop.get_debug():
|
||||
logger.debug("%r resumes writing", self)
|
||||
|
||||
waiter = self._drain_waiter
|
||||
if waiter is not None:
|
||||
self._drain_waiter = None
|
||||
if not waiter.done():
|
||||
waiter.set_result(None)
|
||||
|
||||
def connection_lost(self, exc):
|
||||
self._connection_lost = True
|
||||
# Wake up the writer if currently paused.
|
||||
if not self._paused:
|
||||
return
|
||||
waiter = self._drain_waiter
|
||||
if waiter is None:
|
||||
return
|
||||
self._drain_waiter = None
|
||||
if waiter.done():
|
||||
return
|
||||
if exc is None:
|
||||
waiter.set_result(None)
|
||||
else:
|
||||
waiter.set_exception(exc)
|
||||
|
||||
@coroutine
|
||||
def _drain_helper(self):
|
||||
if self._connection_lost:
|
||||
raise ConnectionResetError('Connection lost')
|
||||
if not self._paused:
|
||||
return
|
||||
waiter = self._drain_waiter
|
||||
assert waiter is None or waiter.cancelled()
|
||||
waiter = futures.Future(loop=self._loop)
|
||||
self._drain_waiter = waiter
|
||||
yield from waiter
|
||||
|
||||
|
||||
class StreamReaderProtocol(FlowControlMixin, protocols.Protocol):
|
||||
"""Helper class to adapt between Protocol and StreamReader.
|
||||
|
||||
(This is a helper class instead of making StreamReader itself a
|
||||
Protocol subclass, because the StreamReader has other potential
|
||||
uses, and to prevent the user of the StreamReader to accidentally
|
||||
call inappropriate methods of the protocol.)
|
||||
"""
|
||||
|
||||
def __init__(self, stream_reader, client_connected_cb=None, loop=None):
|
||||
super().__init__(loop=loop)
|
||||
self._stream_reader = stream_reader
|
||||
self._stream_writer = None
|
||||
self._client_connected_cb = client_connected_cb
|
||||
|
||||
def connection_made(self, transport):
|
||||
self._stream_reader.set_transport(transport)
|
||||
if self._client_connected_cb is not None:
|
||||
self._stream_writer = StreamWriter(transport, self,
|
||||
self._stream_reader,
|
||||
self._loop)
|
||||
res = self._client_connected_cb(self._stream_reader,
|
||||
self._stream_writer)
|
||||
if coroutines.iscoroutine(res):
|
||||
self._loop.create_task(res)
|
||||
|
||||
def connection_lost(self, exc):
|
||||
if exc is None:
|
||||
self._stream_reader.feed_eof()
|
||||
else:
|
||||
self._stream_reader.set_exception(exc)
|
||||
super().connection_lost(exc)
|
||||
|
||||
def data_received(self, data):
|
||||
self._stream_reader.feed_data(data)
|
||||
|
||||
def eof_received(self):
|
||||
self._stream_reader.feed_eof()
|
||||
return True
|
||||
|
||||
|
||||
class StreamWriter:
|
||||
"""Wraps a Transport.
|
||||
|
||||
This exposes write(), writelines(), [can_]write_eof(),
|
||||
get_extra_info() and close(). It adds drain() which returns an
|
||||
optional Future on which you can wait for flow control. It also
|
||||
adds a transport property which references the Transport
|
||||
directly.
|
||||
"""
|
||||
|
||||
def __init__(self, transport, protocol, reader, loop):
|
||||
self._transport = transport
|
||||
self._protocol = protocol
|
||||
# drain() expects that the reader has a exception() method
|
||||
assert reader is None or isinstance(reader, StreamReader)
|
||||
self._reader = reader
|
||||
self._loop = loop
|
||||
|
||||
def __repr__(self):
|
||||
info = [self.__class__.__name__, 'transport=%r' % self._transport]
|
||||
if self._reader is not None:
|
||||
info.append('reader=%r' % self._reader)
|
||||
return '<%s>' % ' '.join(info)
|
||||
|
||||
@property
|
||||
def transport(self):
|
||||
return self._transport
|
||||
|
||||
def write(self, data):
|
||||
self._transport.write(data)
|
||||
|
||||
def writelines(self, data):
|
||||
self._transport.writelines(data)
|
||||
|
||||
def write_eof(self):
|
||||
return self._transport.write_eof()
|
||||
|
||||
def can_write_eof(self):
|
||||
return self._transport.can_write_eof()
|
||||
|
||||
def close(self):
|
||||
return self._transport.close()
|
||||
|
||||
def get_extra_info(self, name, default=None):
|
||||
return self._transport.get_extra_info(name, default)
|
||||
|
||||
@coroutine
|
||||
def drain(self):
|
||||
"""Flush the write buffer.
|
||||
|
||||
The intended use is to write
|
||||
|
||||
w.write(data)
|
||||
yield from w.drain()
|
||||
"""
|
||||
if self._reader is not None:
|
||||
exc = self._reader.exception()
|
||||
if exc is not None:
|
||||
raise exc
|
||||
yield from self._protocol._drain_helper()
|
||||
|
||||
|
||||
class StreamReader:
|
||||
|
||||
def __init__(self, limit=_DEFAULT_LIMIT, loop=None):
|
||||
# The line length limit is a security feature;
|
||||
# it also doubles as half the buffer limit.
|
||||
self._limit = limit
|
||||
if loop is None:
|
||||
self._loop = events.get_event_loop()
|
||||
else:
|
||||
self._loop = loop
|
||||
self._buffer = bytearray()
|
||||
self._eof = False # Whether we're done.
|
||||
self._waiter = None # A future used by _wait_for_data()
|
||||
self._exception = None
|
||||
self._transport = None
|
||||
self._paused = False
|
||||
|
||||
def __repr__(self):
|
||||
info = ['StreamReader']
|
||||
if self._buffer:
|
||||
info.append('%d bytes' % len(info))
|
||||
if self._eof:
|
||||
info.append('eof')
|
||||
if self._limit != _DEFAULT_LIMIT:
|
||||
info.append('l=%d' % self._limit)
|
||||
if self._waiter:
|
||||
info.append('w=%r' % self._waiter)
|
||||
if self._exception:
|
||||
info.append('e=%r' % self._exception)
|
||||
if self._transport:
|
||||
info.append('t=%r' % self._transport)
|
||||
if self._paused:
|
||||
info.append('paused')
|
||||
return '<%s>' % ' '.join(info)
|
||||
|
||||
def exception(self):
|
||||
return self._exception
|
||||
|
||||
def set_exception(self, exc):
|
||||
self._exception = exc
|
||||
|
||||
waiter = self._waiter
|
||||
if waiter is not None:
|
||||
self._waiter = None
|
||||
if not waiter.cancelled():
|
||||
waiter.set_exception(exc)
|
||||
|
||||
def _wakeup_waiter(self):
|
||||
"""Wakeup read() or readline() function waiting for data or EOF."""
|
||||
waiter = self._waiter
|
||||
if waiter is not None:
|
||||
self._waiter = None
|
||||
if not waiter.cancelled():
|
||||
waiter.set_result(None)
|
||||
|
||||
def set_transport(self, transport):
|
||||
assert self._transport is None, 'Transport already set'
|
||||
self._transport = transport
|
||||
|
||||
def _maybe_resume_transport(self):
|
||||
if self._paused and len(self._buffer) <= self._limit:
|
||||
self._paused = False
|
||||
self._transport.resume_reading()
|
||||
|
||||
def feed_eof(self):
|
||||
self._eof = True
|
||||
self._wakeup_waiter()
|
||||
|
||||
def at_eof(self):
|
||||
"""Return True if the buffer is empty and 'feed_eof' was called."""
|
||||
return self._eof and not self._buffer
|
||||
|
||||
def feed_data(self, data):
|
||||
assert not self._eof, 'feed_data after feed_eof'
|
||||
|
||||
if not data:
|
||||
return
|
||||
|
||||
self._buffer.extend(data)
|
||||
self._wakeup_waiter()
|
||||
|
||||
if (self._transport is not None and
|
||||
not self._paused and
|
||||
len(self._buffer) > 2*self._limit):
|
||||
try:
|
||||
self._transport.pause_reading()
|
||||
except NotImplementedError:
|
||||
# The transport can't be paused.
|
||||
# We'll just have to buffer all data.
|
||||
# Forget the transport so we don't keep trying.
|
||||
self._transport = None
|
||||
else:
|
||||
self._paused = True
|
||||
|
||||
@coroutine
|
||||
def _wait_for_data(self, func_name):
|
||||
"""Wait until feed_data() or feed_eof() is called."""
|
||||
# StreamReader uses a future to link the protocol feed_data() method
|
||||
# to a read coroutine. Running two read coroutines at the same time
|
||||
# would have an unexpected behaviour. It would not possible to know
|
||||
# which coroutine would get the next data.
|
||||
if self._waiter is not None:
|
||||
raise RuntimeError('%s() called while another coroutine is '
|
||||
'already waiting for incoming data' % func_name)
|
||||
|
||||
self._waiter = futures.Future(loop=self._loop)
|
||||
try:
|
||||
yield from self._waiter
|
||||
finally:
|
||||
self._waiter = None
|
||||
|
||||
@coroutine
|
||||
def readline(self):
|
||||
if self._exception is not None:
|
||||
raise self._exception
|
||||
|
||||
line = bytearray()
|
||||
not_enough = True
|
||||
|
||||
while not_enough:
|
||||
while self._buffer and not_enough:
|
||||
ichar = self._buffer.find(b'\n')
|
||||
if ichar < 0:
|
||||
line.extend(self._buffer)
|
||||
self._buffer.clear()
|
||||
else:
|
||||
ichar += 1
|
||||
line.extend(self._buffer[:ichar])
|
||||
del self._buffer[:ichar]
|
||||
not_enough = False
|
||||
|
||||
if len(line) > self._limit:
|
||||
self._maybe_resume_transport()
|
||||
raise ValueError('Line is too long')
|
||||
|
||||
if self._eof:
|
||||
break
|
||||
|
||||
if not_enough:
|
||||
yield from self._wait_for_data('readline')
|
||||
|
||||
self._maybe_resume_transport()
|
||||
return bytes(line)
|
||||
|
||||
@coroutine
|
||||
def read(self, n=-1):
|
||||
if self._exception is not None:
|
||||
raise self._exception
|
||||
|
||||
if not n:
|
||||
return b''
|
||||
|
||||
if n < 0:
|
||||
# This used to just loop creating a new waiter hoping to
|
||||
# collect everything in self._buffer, but that would
|
||||
# deadlock if the subprocess sends more than self.limit
|
||||
# bytes. So just call self.read(self._limit) until EOF.
|
||||
blocks = []
|
||||
while True:
|
||||
block = yield from self.read(self._limit)
|
||||
if not block:
|
||||
break
|
||||
blocks.append(block)
|
||||
return b''.join(blocks)
|
||||
else:
|
||||
if not self._buffer and not self._eof:
|
||||
yield from self._wait_for_data('read')
|
||||
|
||||
if n < 0 or len(self._buffer) <= n:
|
||||
data = bytes(self._buffer)
|
||||
self._buffer.clear()
|
||||
else:
|
||||
# n > 0 and len(self._buffer) > n
|
||||
data = bytes(self._buffer[:n])
|
||||
del self._buffer[:n]
|
||||
|
||||
self._maybe_resume_transport()
|
||||
return data
|
||||
|
||||
@coroutine
|
||||
def readexactly(self, n):
|
||||
if self._exception is not None:
|
||||
raise self._exception
|
||||
|
||||
# There used to be "optimized" code here. It created its own
|
||||
# Future and waited until self._buffer had at least the n
|
||||
# bytes, then called read(n). Unfortunately, this could pause
|
||||
# the transport if the argument was larger than the pause
|
||||
# limit (which is twice self._limit). So now we just read()
|
||||
# into a local buffer.
|
||||
|
||||
blocks = []
|
||||
while n > 0:
|
||||
block = yield from self.read(n)
|
||||
if not block:
|
||||
partial = b''.join(blocks)
|
||||
raise IncompleteReadError(partial, len(partial) + n)
|
||||
blocks.append(block)
|
||||
n -= len(block)
|
||||
|
||||
return b''.join(blocks)
|
||||
|
||||
if compat.PY35:
|
||||
@coroutine
|
||||
def __aiter__(self):
|
||||
return self
|
||||
|
||||
@coroutine
|
||||
def __anext__(self):
|
||||
val = yield from self.readline()
|
||||
if val == b'':
|
||||
raise StopAsyncIteration
|
||||
return val
|
||||
|
|
@ -1,682 +0,0 @@
|
|||
"""Support for tasks, coroutines and the scheduler."""
|
||||
|
||||
__all__ = ['Task',
|
||||
'FIRST_COMPLETED', 'FIRST_EXCEPTION', 'ALL_COMPLETED',
|
||||
'wait', 'wait_for', 'as_completed', 'sleep', 'async',
|
||||
'gather', 'shield', 'ensure_future',
|
||||
]
|
||||
|
||||
import concurrent.futures
|
||||
import functools
|
||||
import inspect
|
||||
import linecache
|
||||
import traceback
|
||||
import warnings
|
||||
import weakref
|
||||
|
||||
from . import compat
|
||||
from . import coroutines
|
||||
from . import events
|
||||
from . import futures
|
||||
from .coroutines import coroutine
|
||||
|
||||
|
||||
class Task(futures.Future):
|
||||
"""A coroutine wrapped in a Future."""
|
||||
|
||||
# An important invariant maintained while a Task not done:
|
||||
#
|
||||
# - Either _fut_waiter is None, and _step() is scheduled;
|
||||
# - or _fut_waiter is some Future, and _step() is *not* scheduled.
|
||||
#
|
||||
# The only transition from the latter to the former is through
|
||||
# _wakeup(). When _fut_waiter is not None, one of its callbacks
|
||||
# must be _wakeup().
|
||||
|
||||
# Weak set containing all tasks alive.
|
||||
_all_tasks = weakref.WeakSet()
|
||||
|
||||
# Dictionary containing tasks that are currently active in
|
||||
# all running event loops. {EventLoop: Task}
|
||||
_current_tasks = {}
|
||||
|
||||
# If False, don't log a message if the task is destroyed whereas its
|
||||
# status is still pending
|
||||
_log_destroy_pending = True
|
||||
|
||||
@classmethod
|
||||
def current_task(cls, loop=None):
|
||||
"""Return the currently running task in an event loop or None.
|
||||
|
||||
By default the current task for the current event loop is returned.
|
||||
|
||||
None is returned when called not in the context of a Task.
|
||||
"""
|
||||
if loop is None:
|
||||
loop = events.get_event_loop()
|
||||
return cls._current_tasks.get(loop)
|
||||
|
||||
@classmethod
|
||||
def all_tasks(cls, loop=None):
|
||||
"""Return a set of all tasks for an event loop.
|
||||
|
||||
By default all tasks for the current event loop are returned.
|
||||
"""
|
||||
if loop is None:
|
||||
loop = events.get_event_loop()
|
||||
return {t for t in cls._all_tasks if t._loop is loop}
|
||||
|
||||
def __init__(self, coro, *, loop=None):
|
||||
assert coroutines.iscoroutine(coro), repr(coro)
|
||||
super().__init__(loop=loop)
|
||||
if self._source_traceback:
|
||||
del self._source_traceback[-1]
|
||||
self._coro = coro
|
||||
self._fut_waiter = None
|
||||
self._must_cancel = False
|
||||
self._loop.call_soon(self._step)
|
||||
self.__class__._all_tasks.add(self)
|
||||
|
||||
# On Python 3.3 or older, objects with a destructor that are part of a
|
||||
# reference cycle are never destroyed. That's not the case any more on
|
||||
# Python 3.4 thanks to the PEP 442.
|
||||
if compat.PY34:
|
||||
def __del__(self):
|
||||
if self._state == futures._PENDING and self._log_destroy_pending:
|
||||
context = {
|
||||
'task': self,
|
||||
'message': 'Task was destroyed but it is pending!',
|
||||
}
|
||||
if self._source_traceback:
|
||||
context['source_traceback'] = self._source_traceback
|
||||
self._loop.call_exception_handler(context)
|
||||
futures.Future.__del__(self)
|
||||
|
||||
def _repr_info(self):
|
||||
info = super()._repr_info()
|
||||
|
||||
if self._must_cancel:
|
||||
# replace status
|
||||
info[0] = 'cancelling'
|
||||
|
||||
coro = coroutines._format_coroutine(self._coro)
|
||||
info.insert(1, 'coro=<%s>' % coro)
|
||||
|
||||
if self._fut_waiter is not None:
|
||||
info.insert(2, 'wait_for=%r' % self._fut_waiter)
|
||||
return info
|
||||
|
||||
def get_stack(self, *, limit=None):
|
||||
"""Return the list of stack frames for this task's coroutine.
|
||||
|
||||
If the coroutine is not done, this returns the stack where it is
|
||||
suspended. If the coroutine has completed successfully or was
|
||||
cancelled, this returns an empty list. If the coroutine was
|
||||
terminated by an exception, this returns the list of traceback
|
||||
frames.
|
||||
|
||||
The frames are always ordered from oldest to newest.
|
||||
|
||||
The optional limit gives the maximum number of frames to
|
||||
return; by default all available frames are returned. Its
|
||||
meaning differs depending on whether a stack or a traceback is
|
||||
returned: the newest frames of a stack are returned, but the
|
||||
oldest frames of a traceback are returned. (This matches the
|
||||
behavior of the traceback module.)
|
||||
|
||||
For reasons beyond our control, only one stack frame is
|
||||
returned for a suspended coroutine.
|
||||
"""
|
||||
frames = []
|
||||
try:
|
||||
# 'async def' coroutines
|
||||
f = self._coro.cr_frame
|
||||
except AttributeError:
|
||||
f = self._coro.gi_frame
|
||||
if f is not None:
|
||||
while f is not None:
|
||||
if limit is not None:
|
||||
if limit <= 0:
|
||||
break
|
||||
limit -= 1
|
||||
frames.append(f)
|
||||
f = f.f_back
|
||||
frames.reverse()
|
||||
elif self._exception is not None:
|
||||
tb = self._exception.__traceback__
|
||||
while tb is not None:
|
||||
if limit is not None:
|
||||
if limit <= 0:
|
||||
break
|
||||
limit -= 1
|
||||
frames.append(tb.tb_frame)
|
||||
tb = tb.tb_next
|
||||
return frames
|
||||
|
||||
def print_stack(self, *, limit=None, file=None):
|
||||
"""Print the stack or traceback for this task's coroutine.
|
||||
|
||||
This produces output similar to that of the traceback module,
|
||||
for the frames retrieved by get_stack(). The limit argument
|
||||
is passed to get_stack(). The file argument is an I/O stream
|
||||
to which the output is written; by default output is written
|
||||
to sys.stderr.
|
||||
"""
|
||||
extracted_list = []
|
||||
checked = set()
|
||||
for f in self.get_stack(limit=limit):
|
||||
lineno = f.f_lineno
|
||||
co = f.f_code
|
||||
filename = co.co_filename
|
||||
name = co.co_name
|
||||
if filename not in checked:
|
||||
checked.add(filename)
|
||||
linecache.checkcache(filename)
|
||||
line = linecache.getline(filename, lineno, f.f_globals)
|
||||
extracted_list.append((filename, lineno, name, line))
|
||||
exc = self._exception
|
||||
if not extracted_list:
|
||||
print('No stack for %r' % self, file=file)
|
||||
elif exc is not None:
|
||||
print('Traceback for %r (most recent call last):' % self,
|
||||
file=file)
|
||||
else:
|
||||
print('Stack for %r (most recent call last):' % self,
|
||||
file=file)
|
||||
traceback.print_list(extracted_list, file=file)
|
||||
if exc is not None:
|
||||
for line in traceback.format_exception_only(exc.__class__, exc):
|
||||
print(line, file=file, end='')
|
||||
|
||||
def cancel(self):
|
||||
"""Request that this task cancel itself.
|
||||
|
||||
This arranges for a CancelledError to be thrown into the
|
||||
wrapped coroutine on the next cycle through the event loop.
|
||||
The coroutine then has a chance to clean up or even deny
|
||||
the request using try/except/finally.
|
||||
|
||||
Unlike Future.cancel, this does not guarantee that the
|
||||
task will be cancelled: the exception might be caught and
|
||||
acted upon, delaying cancellation of the task or preventing
|
||||
cancellation completely. The task may also return a value or
|
||||
raise a different exception.
|
||||
|
||||
Immediately after this method is called, Task.cancelled() will
|
||||
not return True (unless the task was already cancelled). A
|
||||
task will be marked as cancelled when the wrapped coroutine
|
||||
terminates with a CancelledError exception (even if cancel()
|
||||
was not called).
|
||||
"""
|
||||
if self.done():
|
||||
return False
|
||||
if self._fut_waiter is not None:
|
||||
if self._fut_waiter.cancel():
|
||||
# Leave self._fut_waiter; it may be a Task that
|
||||
# catches and ignores the cancellation so we may have
|
||||
# to cancel it again later.
|
||||
return True
|
||||
# It must be the case that self._step is already scheduled.
|
||||
self._must_cancel = True
|
||||
return True
|
||||
|
||||
def _step(self, value=None, exc=None):
|
||||
assert not self.done(), \
|
||||
'_step(): already done: {!r}, {!r}, {!r}'.format(self, value, exc)
|
||||
if self._must_cancel:
|
||||
if not isinstance(exc, futures.CancelledError):
|
||||
exc = futures.CancelledError()
|
||||
self._must_cancel = False
|
||||
coro = self._coro
|
||||
self._fut_waiter = None
|
||||
|
||||
self.__class__._current_tasks[self._loop] = self
|
||||
# Call either coro.throw(exc) or coro.send(value).
|
||||
try:
|
||||
if exc is not None:
|
||||
result = coro.throw(exc)
|
||||
else:
|
||||
result = coro.send(value)
|
||||
except StopIteration as exc:
|
||||
self.set_result(exc.value)
|
||||
except futures.CancelledError as exc:
|
||||
super().cancel() # I.e., Future.cancel(self).
|
||||
except Exception as exc:
|
||||
self.set_exception(exc)
|
||||
except BaseException as exc:
|
||||
self.set_exception(exc)
|
||||
raise
|
||||
else:
|
||||
if isinstance(result, futures.Future):
|
||||
# Yielded Future must come from Future.__iter__().
|
||||
if result._blocking:
|
||||
result._blocking = False
|
||||
result.add_done_callback(self._wakeup)
|
||||
self._fut_waiter = result
|
||||
if self._must_cancel:
|
||||
if self._fut_waiter.cancel():
|
||||
self._must_cancel = False
|
||||
else:
|
||||
self._loop.call_soon(
|
||||
self._step, None,
|
||||
RuntimeError(
|
||||
'yield was used instead of yield from '
|
||||
'in task {!r} with {!r}'.format(self, result)))
|
||||
elif result is None:
|
||||
# Bare yield relinquishes control for one event loop iteration.
|
||||
self._loop.call_soon(self._step)
|
||||
elif inspect.isgenerator(result):
|
||||
# Yielding a generator is just wrong.
|
||||
self._loop.call_soon(
|
||||
self._step, None,
|
||||
RuntimeError(
|
||||
'yield was used instead of yield from for '
|
||||
'generator in task {!r} with {}'.format(
|
||||
self, result)))
|
||||
else:
|
||||
# Yielding something else is an error.
|
||||
self._loop.call_soon(
|
||||
self._step, None,
|
||||
RuntimeError(
|
||||
'Task got bad yield: {!r}'.format(result)))
|
||||
finally:
|
||||
self.__class__._current_tasks.pop(self._loop)
|
||||
self = None # Needed to break cycles when an exception occurs.
|
||||
|
||||
def _wakeup(self, future):
|
||||
try:
|
||||
value = future.result()
|
||||
except Exception as exc:
|
||||
# This may also be a cancellation.
|
||||
self._step(None, exc)
|
||||
else:
|
||||
self._step(value, None)
|
||||
self = None # Needed to break cycles when an exception occurs.
|
||||
|
||||
|
||||
# wait() and as_completed() similar to those in PEP 3148.
|
||||
|
||||
FIRST_COMPLETED = concurrent.futures.FIRST_COMPLETED
|
||||
FIRST_EXCEPTION = concurrent.futures.FIRST_EXCEPTION
|
||||
ALL_COMPLETED = concurrent.futures.ALL_COMPLETED
|
||||
|
||||
|
||||
@coroutine
|
||||
def wait(fs, *, loop=None, timeout=None, return_when=ALL_COMPLETED):
|
||||
"""Wait for the Futures and coroutines given by fs to complete.
|
||||
|
||||
The sequence futures must not be empty.
|
||||
|
||||
Coroutines will be wrapped in Tasks.
|
||||
|
||||
Returns two sets of Future: (done, pending).
|
||||
|
||||
Usage:
|
||||
|
||||
done, pending = yield from asyncio.wait(fs)
|
||||
|
||||
Note: This does not raise TimeoutError! Futures that aren't done
|
||||
when the timeout occurs are returned in the second set.
|
||||
"""
|
||||
if isinstance(fs, futures.Future) or coroutines.iscoroutine(fs):
|
||||
raise TypeError("expect a list of futures, not %s" % type(fs).__name__)
|
||||
if not fs:
|
||||
raise ValueError('Set of coroutines/Futures is empty.')
|
||||
if return_when not in (FIRST_COMPLETED, FIRST_EXCEPTION, ALL_COMPLETED):
|
||||
raise ValueError('Invalid return_when value: {}'.format(return_when))
|
||||
|
||||
if loop is None:
|
||||
loop = events.get_event_loop()
|
||||
|
||||
fs = {ensure_future(f, loop=loop) for f in set(fs)}
|
||||
|
||||
return (yield from _wait(fs, timeout, return_when, loop))
|
||||
|
||||
|
||||
def _release_waiter(waiter, *args):
|
||||
if not waiter.done():
|
||||
waiter.set_result(None)
|
||||
|
||||
|
||||
@coroutine
|
||||
def wait_for(fut, timeout, *, loop=None):
|
||||
"""Wait for the single Future or coroutine to complete, with timeout.
|
||||
|
||||
Coroutine will be wrapped in Task.
|
||||
|
||||
Returns result of the Future or coroutine. When a timeout occurs,
|
||||
it cancels the task and raises TimeoutError. To avoid the task
|
||||
cancellation, wrap it in shield().
|
||||
|
||||
If the wait is cancelled, the task is also cancelled.
|
||||
|
||||
This function is a coroutine.
|
||||
"""
|
||||
if loop is None:
|
||||
loop = events.get_event_loop()
|
||||
|
||||
if timeout is None:
|
||||
return (yield from fut)
|
||||
|
||||
waiter = futures.Future(loop=loop)
|
||||
timeout_handle = loop.call_later(timeout, _release_waiter, waiter)
|
||||
cb = functools.partial(_release_waiter, waiter)
|
||||
|
||||
fut = ensure_future(fut, loop=loop)
|
||||
fut.add_done_callback(cb)
|
||||
|
||||
try:
|
||||
# wait until the future completes or the timeout
|
||||
try:
|
||||
yield from waiter
|
||||
except futures.CancelledError:
|
||||
fut.remove_done_callback(cb)
|
||||
fut.cancel()
|
||||
raise
|
||||
|
||||
if fut.done():
|
||||
return fut.result()
|
||||
else:
|
||||
fut.remove_done_callback(cb)
|
||||
fut.cancel()
|
||||
raise futures.TimeoutError()
|
||||
finally:
|
||||
timeout_handle.cancel()
|
||||
|
||||
|
||||
@coroutine
|
||||
def _wait(fs, timeout, return_when, loop):
|
||||
"""Internal helper for wait() and _wait_for().
|
||||
|
||||
The fs argument must be a collection of Futures.
|
||||
"""
|
||||
assert fs, 'Set of Futures is empty.'
|
||||
waiter = futures.Future(loop=loop)
|
||||
timeout_handle = None
|
||||
if timeout is not None:
|
||||
timeout_handle = loop.call_later(timeout, _release_waiter, waiter)
|
||||
counter = len(fs)
|
||||
|
||||
def _on_completion(f):
|
||||
nonlocal counter
|
||||
counter -= 1
|
||||
if (counter <= 0 or
|
||||
return_when == FIRST_COMPLETED or
|
||||
return_when == FIRST_EXCEPTION and (not f.cancelled() and
|
||||
f.exception() is not None)):
|
||||
if timeout_handle is not None:
|
||||
timeout_handle.cancel()
|
||||
if not waiter.done():
|
||||
waiter.set_result(None)
|
||||
|
||||
for f in fs:
|
||||
f.add_done_callback(_on_completion)
|
||||
|
||||
try:
|
||||
yield from waiter
|
||||
finally:
|
||||
if timeout_handle is not None:
|
||||
timeout_handle.cancel()
|
||||
|
||||
done, pending = set(), set()
|
||||
for f in fs:
|
||||
f.remove_done_callback(_on_completion)
|
||||
if f.done():
|
||||
done.add(f)
|
||||
else:
|
||||
pending.add(f)
|
||||
return done, pending
|
||||
|
||||
|
||||
# This is *not* a @coroutine! It is just an iterator (yielding Futures).
|
||||
def as_completed(fs, *, loop=None, timeout=None):
|
||||
"""Return an iterator whose values are coroutines.
|
||||
|
||||
When waiting for the yielded coroutines you'll get the results (or
|
||||
exceptions!) of the original Futures (or coroutines), in the order
|
||||
in which and as soon as they complete.
|
||||
|
||||
This differs from PEP 3148; the proper way to use this is:
|
||||
|
||||
for f in as_completed(fs):
|
||||
result = yield from f # The 'yield from' may raise.
|
||||
# Use result.
|
||||
|
||||
If a timeout is specified, the 'yield from' will raise
|
||||
TimeoutError when the timeout occurs before all Futures are done.
|
||||
|
||||
Note: The futures 'f' are not necessarily members of fs.
|
||||
"""
|
||||
if isinstance(fs, futures.Future) or coroutines.iscoroutine(fs):
|
||||
raise TypeError("expect a list of futures, not %s" % type(fs).__name__)
|
||||
loop = loop if loop is not None else events.get_event_loop()
|
||||
todo = {ensure_future(f, loop=loop) for f in set(fs)}
|
||||
from .queues import Queue # Import here to avoid circular import problem.
|
||||
done = Queue(loop=loop)
|
||||
timeout_handle = None
|
||||
|
||||
def _on_timeout():
|
||||
for f in todo:
|
||||
f.remove_done_callback(_on_completion)
|
||||
done.put_nowait(None) # Queue a dummy value for _wait_for_one().
|
||||
todo.clear() # Can't do todo.remove(f) in the loop.
|
||||
|
||||
def _on_completion(f):
|
||||
if not todo:
|
||||
return # _on_timeout() was here first.
|
||||
todo.remove(f)
|
||||
done.put_nowait(f)
|
||||
if not todo and timeout_handle is not None:
|
||||
timeout_handle.cancel()
|
||||
|
||||
@coroutine
|
||||
def _wait_for_one():
|
||||
f = yield from done.get()
|
||||
if f is None:
|
||||
# Dummy value from _on_timeout().
|
||||
raise futures.TimeoutError
|
||||
return f.result() # May raise f.exception().
|
||||
|
||||
for f in todo:
|
||||
f.add_done_callback(_on_completion)
|
||||
if todo and timeout is not None:
|
||||
timeout_handle = loop.call_later(timeout, _on_timeout)
|
||||
for _ in range(len(todo)):
|
||||
yield _wait_for_one()
|
||||
|
||||
|
||||
@coroutine
|
||||
def sleep(delay, result=None, *, loop=None):
|
||||
"""Coroutine that completes after a given time (in seconds)."""
|
||||
future = futures.Future(loop=loop)
|
||||
h = future._loop.call_later(delay,
|
||||
future._set_result_unless_cancelled, result)
|
||||
try:
|
||||
return (yield from future)
|
||||
finally:
|
||||
h.cancel()
|
||||
|
||||
|
||||
def async(coro_or_future, *, loop=None):
|
||||
"""Wrap a coroutine in a future.
|
||||
|
||||
If the argument is a Future, it is returned directly.
|
||||
|
||||
This function is deprecated in 3.5. Use asyncio.ensure_future() instead.
|
||||
"""
|
||||
|
||||
warnings.warn("asyncio.async() function is deprecated, use ensure_future()",
|
||||
DeprecationWarning)
|
||||
|
||||
return ensure_future(coro_or_future, loop=loop)
|
||||
|
||||
|
||||
def ensure_future(coro_or_future, *, loop=None):
|
||||
"""Wrap a coroutine in a future.
|
||||
|
||||
If the argument is a Future, it is returned directly.
|
||||
"""
|
||||
if isinstance(coro_or_future, futures.Future):
|
||||
if loop is not None and loop is not coro_or_future._loop:
|
||||
raise ValueError('loop argument must agree with Future')
|
||||
return coro_or_future
|
||||
elif coroutines.iscoroutine(coro_or_future):
|
||||
if loop is None:
|
||||
loop = events.get_event_loop()
|
||||
task = loop.create_task(coro_or_future)
|
||||
if task._source_traceback:
|
||||
del task._source_traceback[-1]
|
||||
return task
|
||||
else:
|
||||
raise TypeError('A Future or coroutine is required')
|
||||
|
||||
|
||||
class _GatheringFuture(futures.Future):
|
||||
"""Helper for gather().
|
||||
|
||||
This overrides cancel() to cancel all the children and act more
|
||||
like Task.cancel(), which doesn't immediately mark itself as
|
||||
cancelled.
|
||||
"""
|
||||
|
||||
def __init__(self, children, *, loop=None):
|
||||
super().__init__(loop=loop)
|
||||
self._children = children
|
||||
|
||||
def cancel(self):
|
||||
if self.done():
|
||||
return False
|
||||
for child in self._children:
|
||||
child.cancel()
|
||||
return True
|
||||
|
||||
|
||||
def gather(*coros_or_futures, loop=None, return_exceptions=False):
|
||||
"""Return a future aggregating results from the given coroutines
|
||||
or futures.
|
||||
|
||||
All futures must share the same event loop. If all the tasks are
|
||||
done successfully, the returned future's result is the list of
|
||||
results (in the order of the original sequence, not necessarily
|
||||
the order of results arrival). If *return_exceptions* is True,
|
||||
exceptions in the tasks are treated the same as successful
|
||||
results, and gathered in the result list; otherwise, the first
|
||||
raised exception will be immediately propagated to the returned
|
||||
future.
|
||||
|
||||
Cancellation: if the outer Future is cancelled, all children (that
|
||||
have not completed yet) are also cancelled. If any child is
|
||||
cancelled, this is treated as if it raised CancelledError --
|
||||
the outer Future is *not* cancelled in this case. (This is to
|
||||
prevent the cancellation of one child to cause other children to
|
||||
be cancelled.)
|
||||
"""
|
||||
if not coros_or_futures:
|
||||
outer = futures.Future(loop=loop)
|
||||
outer.set_result([])
|
||||
return outer
|
||||
|
||||
arg_to_fut = {}
|
||||
for arg in set(coros_or_futures):
|
||||
if not isinstance(arg, futures.Future):
|
||||
fut = ensure_future(arg, loop=loop)
|
||||
if loop is None:
|
||||
loop = fut._loop
|
||||
# The caller cannot control this future, the "destroy pending task"
|
||||
# warning should not be emitted.
|
||||
fut._log_destroy_pending = False
|
||||
else:
|
||||
fut = arg
|
||||
if loop is None:
|
||||
loop = fut._loop
|
||||
elif fut._loop is not loop:
|
||||
raise ValueError("futures are tied to different event loops")
|
||||
arg_to_fut[arg] = fut
|
||||
|
||||
children = [arg_to_fut[arg] for arg in coros_or_futures]
|
||||
nchildren = len(children)
|
||||
outer = _GatheringFuture(children, loop=loop)
|
||||
nfinished = 0
|
||||
results = [None] * nchildren
|
||||
|
||||
def _done_callback(i, fut):
|
||||
nonlocal nfinished
|
||||
if outer.done():
|
||||
if not fut.cancelled():
|
||||
# Mark exception retrieved.
|
||||
fut.exception()
|
||||
return
|
||||
|
||||
if fut.cancelled():
|
||||
res = futures.CancelledError()
|
||||
if not return_exceptions:
|
||||
outer.set_exception(res)
|
||||
return
|
||||
elif fut._exception is not None:
|
||||
res = fut.exception() # Mark exception retrieved.
|
||||
if not return_exceptions:
|
||||
outer.set_exception(res)
|
||||
return
|
||||
else:
|
||||
res = fut._result
|
||||
results[i] = res
|
||||
nfinished += 1
|
||||
if nfinished == nchildren:
|
||||
outer.set_result(results)
|
||||
|
||||
for i, fut in enumerate(children):
|
||||
fut.add_done_callback(functools.partial(_done_callback, i))
|
||||
return outer
|
||||
|
||||
|
||||
def shield(arg, *, loop=None):
|
||||
"""Wait for a future, shielding it from cancellation.
|
||||
|
||||
The statement
|
||||
|
||||
res = yield from shield(something())
|
||||
|
||||
is exactly equivalent to the statement
|
||||
|
||||
res = yield from something()
|
||||
|
||||
*except* that if the coroutine containing it is cancelled, the
|
||||
task running in something() is not cancelled. From the POV of
|
||||
something(), the cancellation did not happen. But its caller is
|
||||
still cancelled, so the yield-from expression still raises
|
||||
CancelledError. Note: If something() is cancelled by other means
|
||||
this will still cancel shield().
|
||||
|
||||
If you want to completely ignore cancellation (not recommended)
|
||||
you can combine shield() with a try/except clause, as follows:
|
||||
|
||||
try:
|
||||
res = yield from shield(something())
|
||||
except CancelledError:
|
||||
res = None
|
||||
"""
|
||||
inner = ensure_future(arg, loop=loop)
|
||||
if inner.done():
|
||||
# Shortcut.
|
||||
return inner
|
||||
loop = inner._loop
|
||||
outer = futures.Future(loop=loop)
|
||||
|
||||
def _done_callback(inner):
|
||||
if outer.cancelled():
|
||||
if not inner.cancelled():
|
||||
# Mark inner's result as retrieved.
|
||||
inner.exception()
|
||||
return
|
||||
|
||||
if inner.cancelled():
|
||||
outer.cancel()
|
||||
else:
|
||||
exc = inner.exception()
|
||||
if exc is not None:
|
||||
outer.set_exception(exc)
|
||||
else:
|
||||
outer.set_result(inner.result())
|
||||
|
||||
inner.add_done_callback(_done_callback)
|
||||
return outer
|
||||
|
|
@ -1,446 +0,0 @@
|
|||
"""Utilities shared by tests."""
|
||||
|
||||
import collections
|
||||
import contextlib
|
||||
import io
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import socket
|
||||
import socketserver
|
||||
import sys
|
||||
import tempfile
|
||||
import threading
|
||||
import time
|
||||
import unittest
|
||||
from unittest import mock
|
||||
|
||||
from http.server import HTTPServer
|
||||
from wsgiref.simple_server import WSGIRequestHandler, WSGIServer
|
||||
|
||||
try:
|
||||
import ssl
|
||||
except ImportError: # pragma: no cover
|
||||
ssl = None
|
||||
|
||||
from . import base_events
|
||||
from . import events
|
||||
from . import futures
|
||||
from . import selectors
|
||||
from . import tasks
|
||||
from .coroutines import coroutine
|
||||
from .log import logger
|
||||
|
||||
|
||||
if sys.platform == 'win32': # pragma: no cover
|
||||
from .windows_utils import socketpair
|
||||
else:
|
||||
from socket import socketpair # pragma: no cover
|
||||
|
||||
|
||||
def dummy_ssl_context():
|
||||
if ssl is None:
|
||||
return None
|
||||
else:
|
||||
return ssl.SSLContext(ssl.PROTOCOL_SSLv23)
|
||||
|
||||
|
||||
def run_briefly(loop):
|
||||
@coroutine
|
||||
def once():
|
||||
pass
|
||||
gen = once()
|
||||
t = loop.create_task(gen)
|
||||
# Don't log a warning if the task is not done after run_until_complete().
|
||||
# It occurs if the loop is stopped or if a task raises a BaseException.
|
||||
t._log_destroy_pending = False
|
||||
try:
|
||||
loop.run_until_complete(t)
|
||||
finally:
|
||||
gen.close()
|
||||
|
||||
|
||||
def run_until(loop, pred, timeout=30):
|
||||
deadline = time.time() + timeout
|
||||
while not pred():
|
||||
if timeout is not None:
|
||||
timeout = deadline - time.time()
|
||||
if timeout <= 0:
|
||||
raise futures.TimeoutError()
|
||||
loop.run_until_complete(tasks.sleep(0.001, loop=loop))
|
||||
|
||||
|
||||
def run_once(loop):
|
||||
"""loop.stop() schedules _raise_stop_error()
|
||||
and run_forever() runs until _raise_stop_error() callback.
|
||||
this wont work if test waits for some IO events, because
|
||||
_raise_stop_error() runs before any of io events callbacks.
|
||||
"""
|
||||
loop.stop()
|
||||
loop.run_forever()
|
||||
|
||||
|
||||
class SilentWSGIRequestHandler(WSGIRequestHandler):
|
||||
|
||||
def get_stderr(self):
|
||||
return io.StringIO()
|
||||
|
||||
def log_message(self, format, *args):
|
||||
pass
|
||||
|
||||
|
||||
class SilentWSGIServer(WSGIServer):
|
||||
|
||||
request_timeout = 2
|
||||
|
||||
def get_request(self):
|
||||
request, client_addr = super().get_request()
|
||||
request.settimeout(self.request_timeout)
|
||||
return request, client_addr
|
||||
|
||||
def handle_error(self, request, client_address):
|
||||
pass
|
||||
|
||||
|
||||
class SSLWSGIServerMixin:
|
||||
|
||||
def finish_request(self, request, client_address):
|
||||
# The relative location of our test directory (which
|
||||
# contains the ssl key and certificate files) differs
|
||||
# between the stdlib and stand-alone asyncio.
|
||||
# Prefer our own if we can find it.
|
||||
here = os.path.join(os.path.dirname(__file__), '..', 'tests')
|
||||
if not os.path.isdir(here):
|
||||
here = os.path.join(os.path.dirname(os.__file__),
|
||||
'test', 'test_asyncio')
|
||||
keyfile = os.path.join(here, 'ssl_key.pem')
|
||||
certfile = os.path.join(here, 'ssl_cert.pem')
|
||||
ssock = ssl.wrap_socket(request,
|
||||
keyfile=keyfile,
|
||||
certfile=certfile,
|
||||
server_side=True)
|
||||
try:
|
||||
self.RequestHandlerClass(ssock, client_address, self)
|
||||
ssock.close()
|
||||
except OSError:
|
||||
# maybe socket has been closed by peer
|
||||
pass
|
||||
|
||||
|
||||
class SSLWSGIServer(SSLWSGIServerMixin, SilentWSGIServer):
|
||||
pass
|
||||
|
||||
|
||||
def _run_test_server(*, address, use_ssl=False, server_cls, server_ssl_cls):
|
||||
|
||||
def app(environ, start_response):
|
||||
status = '200 OK'
|
||||
headers = [('Content-type', 'text/plain')]
|
||||
start_response(status, headers)
|
||||
return [b'Test message']
|
||||
|
||||
# Run the test WSGI server in a separate thread in order not to
|
||||
# interfere with event handling in the main thread
|
||||
server_class = server_ssl_cls if use_ssl else server_cls
|
||||
httpd = server_class(address, SilentWSGIRequestHandler)
|
||||
httpd.set_app(app)
|
||||
httpd.address = httpd.server_address
|
||||
server_thread = threading.Thread(
|
||||
target=lambda: httpd.serve_forever(poll_interval=0.05))
|
||||
server_thread.start()
|
||||
try:
|
||||
yield httpd
|
||||
finally:
|
||||
httpd.shutdown()
|
||||
httpd.server_close()
|
||||
server_thread.join()
|
||||
|
||||
|
||||
if hasattr(socket, 'AF_UNIX'):
|
||||
|
||||
class UnixHTTPServer(socketserver.UnixStreamServer, HTTPServer):
|
||||
|
||||
def server_bind(self):
|
||||
socketserver.UnixStreamServer.server_bind(self)
|
||||
self.server_name = '127.0.0.1'
|
||||
self.server_port = 80
|
||||
|
||||
|
||||
class UnixWSGIServer(UnixHTTPServer, WSGIServer):
|
||||
|
||||
request_timeout = 2
|
||||
|
||||
def server_bind(self):
|
||||
UnixHTTPServer.server_bind(self)
|
||||
self.setup_environ()
|
||||
|
||||
def get_request(self):
|
||||
request, client_addr = super().get_request()
|
||||
request.settimeout(self.request_timeout)
|
||||
# Code in the stdlib expects that get_request
|
||||
# will return a socket and a tuple (host, port).
|
||||
# However, this isn't true for UNIX sockets,
|
||||
# as the second return value will be a path;
|
||||
# hence we return some fake data sufficient
|
||||
# to get the tests going
|
||||
return request, ('127.0.0.1', '')
|
||||
|
||||
|
||||
class SilentUnixWSGIServer(UnixWSGIServer):
|
||||
|
||||
def handle_error(self, request, client_address):
|
||||
pass
|
||||
|
||||
|
||||
class UnixSSLWSGIServer(SSLWSGIServerMixin, SilentUnixWSGIServer):
|
||||
pass
|
||||
|
||||
|
||||
def gen_unix_socket_path():
|
||||
with tempfile.NamedTemporaryFile() as file:
|
||||
return file.name
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def unix_socket_path():
|
||||
path = gen_unix_socket_path()
|
||||
try:
|
||||
yield path
|
||||
finally:
|
||||
try:
|
||||
os.unlink(path)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def run_test_unix_server(*, use_ssl=False):
|
||||
with unix_socket_path() as path:
|
||||
yield from _run_test_server(address=path, use_ssl=use_ssl,
|
||||
server_cls=SilentUnixWSGIServer,
|
||||
server_ssl_cls=UnixSSLWSGIServer)
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def run_test_server(*, host='127.0.0.1', port=0, use_ssl=False):
|
||||
yield from _run_test_server(address=(host, port), use_ssl=use_ssl,
|
||||
server_cls=SilentWSGIServer,
|
||||
server_ssl_cls=SSLWSGIServer)
|
||||
|
||||
|
||||
def make_test_protocol(base):
|
||||
dct = {}
|
||||
for name in dir(base):
|
||||
if name.startswith('__') and name.endswith('__'):
|
||||
# skip magic names
|
||||
continue
|
||||
dct[name] = MockCallback(return_value=None)
|
||||
return type('TestProtocol', (base,) + base.__bases__, dct)()
|
||||
|
||||
|
||||
class TestSelector(selectors.BaseSelector):
|
||||
|
||||
def __init__(self):
|
||||
self.keys = {}
|
||||
|
||||
def register(self, fileobj, events, data=None):
|
||||
key = selectors.SelectorKey(fileobj, 0, events, data)
|
||||
self.keys[fileobj] = key
|
||||
return key
|
||||
|
||||
def unregister(self, fileobj):
|
||||
return self.keys.pop(fileobj)
|
||||
|
||||
def select(self, timeout):
|
||||
return []
|
||||
|
||||
def get_map(self):
|
||||
return self.keys
|
||||
|
||||
|
||||
class TestLoop(base_events.BaseEventLoop):
|
||||
"""Loop for unittests.
|
||||
|
||||
It manages self time directly.
|
||||
If something scheduled to be executed later then
|
||||
on next loop iteration after all ready handlers done
|
||||
generator passed to __init__ is calling.
|
||||
|
||||
Generator should be like this:
|
||||
|
||||
def gen():
|
||||
...
|
||||
when = yield ...
|
||||
... = yield time_advance
|
||||
|
||||
Value returned by yield is absolute time of next scheduled handler.
|
||||
Value passed to yield is time advance to move loop's time forward.
|
||||
"""
|
||||
|
||||
def __init__(self, gen=None):
|
||||
super().__init__()
|
||||
|
||||
if gen is None:
|
||||
def gen():
|
||||
yield
|
||||
self._check_on_close = False
|
||||
else:
|
||||
self._check_on_close = True
|
||||
|
||||
self._gen = gen()
|
||||
next(self._gen)
|
||||
self._time = 0
|
||||
self._clock_resolution = 1e-9
|
||||
self._timers = []
|
||||
self._selector = TestSelector()
|
||||
|
||||
self.readers = {}
|
||||
self.writers = {}
|
||||
self.reset_counters()
|
||||
|
||||
def time(self):
|
||||
return self._time
|
||||
|
||||
def advance_time(self, advance):
|
||||
"""Move test time forward."""
|
||||
if advance:
|
||||
self._time += advance
|
||||
|
||||
def close(self):
|
||||
super().close()
|
||||
if self._check_on_close:
|
||||
try:
|
||||
self._gen.send(0)
|
||||
except StopIteration:
|
||||
pass
|
||||
else: # pragma: no cover
|
||||
raise AssertionError("Time generator is not finished")
|
||||
|
||||
def add_reader(self, fd, callback, *args):
|
||||
self.readers[fd] = events.Handle(callback, args, self)
|
||||
|
||||
def remove_reader(self, fd):
|
||||
self.remove_reader_count[fd] += 1
|
||||
if fd in self.readers:
|
||||
del self.readers[fd]
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def assert_reader(self, fd, callback, *args):
|
||||
assert fd in self.readers, 'fd {} is not registered'.format(fd)
|
||||
handle = self.readers[fd]
|
||||
assert handle._callback == callback, '{!r} != {!r}'.format(
|
||||
handle._callback, callback)
|
||||
assert handle._args == args, '{!r} != {!r}'.format(
|
||||
handle._args, args)
|
||||
|
||||
def add_writer(self, fd, callback, *args):
|
||||
self.writers[fd] = events.Handle(callback, args, self)
|
||||
|
||||
def remove_writer(self, fd):
|
||||
self.remove_writer_count[fd] += 1
|
||||
if fd in self.writers:
|
||||
del self.writers[fd]
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def assert_writer(self, fd, callback, *args):
|
||||
assert fd in self.writers, 'fd {} is not registered'.format(fd)
|
||||
handle = self.writers[fd]
|
||||
assert handle._callback == callback, '{!r} != {!r}'.format(
|
||||
handle._callback, callback)
|
||||
assert handle._args == args, '{!r} != {!r}'.format(
|
||||
handle._args, args)
|
||||
|
||||
def reset_counters(self):
|
||||
self.remove_reader_count = collections.defaultdict(int)
|
||||
self.remove_writer_count = collections.defaultdict(int)
|
||||
|
||||
def _run_once(self):
|
||||
super()._run_once()
|
||||
for when in self._timers:
|
||||
advance = self._gen.send(when)
|
||||
self.advance_time(advance)
|
||||
self._timers = []
|
||||
|
||||
def call_at(self, when, callback, *args):
|
||||
self._timers.append(when)
|
||||
return super().call_at(when, callback, *args)
|
||||
|
||||
def _process_events(self, event_list):
|
||||
return
|
||||
|
||||
def _write_to_self(self):
|
||||
pass
|
||||
|
||||
|
||||
def MockCallback(**kwargs):
|
||||
return mock.Mock(spec=['__call__'], **kwargs)
|
||||
|
||||
|
||||
class MockPattern(str):
|
||||
"""A regex based str with a fuzzy __eq__.
|
||||
|
||||
Use this helper with 'mock.assert_called_with', or anywhere
|
||||
where a regex comparison between strings is needed.
|
||||
|
||||
For instance:
|
||||
mock_call.assert_called_with(MockPattern('spam.*ham'))
|
||||
"""
|
||||
def __eq__(self, other):
|
||||
return bool(re.search(str(self), other, re.S))
|
||||
|
||||
|
||||
def get_function_source(func):
|
||||
source = events._get_function_source(func)
|
||||
if source is None:
|
||||
raise ValueError("unable to get the source of %r" % (func,))
|
||||
return source
|
||||
|
||||
|
||||
class TestCase(unittest.TestCase):
|
||||
def set_event_loop(self, loop, *, cleanup=True):
|
||||
assert loop is not None
|
||||
# ensure that the event loop is passed explicitly in asyncio
|
||||
events.set_event_loop(None)
|
||||
if cleanup:
|
||||
self.addCleanup(loop.close)
|
||||
|
||||
def new_test_loop(self, gen=None):
|
||||
loop = TestLoop(gen)
|
||||
self.set_event_loop(loop)
|
||||
return loop
|
||||
|
||||
def tearDown(self):
|
||||
events.set_event_loop(None)
|
||||
|
||||
# Detect CPython bug #23353: ensure that yield/yield-from is not used
|
||||
# in an except block of a generator
|
||||
self.assertEqual(sys.exc_info(), (None, None, None))
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def disable_logger():
|
||||
"""Context manager to disable asyncio logger.
|
||||
|
||||
For example, it can be used to ignore warnings in debug mode.
|
||||
"""
|
||||
old_level = logger.level
|
||||
try:
|
||||
logger.setLevel(logging.CRITICAL+1)
|
||||
yield
|
||||
finally:
|
||||
logger.setLevel(old_level)
|
||||
|
||||
def mock_nonblocking_socket():
|
||||
"""Create a mock of a non-blocking socket."""
|
||||
sock = mock.Mock(socket.socket)
|
||||
sock.gettimeout.return_value = 0.0
|
||||
return sock
|
||||
|
||||
|
||||
def force_legacy_ssl_support():
|
||||
return mock.patch('asyncio.sslproto._is_sslproto_available',
|
||||
return_value=False)
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
################################################################################
|
||||
### Simple tests
|
||||
################################################################################
|
||||
|
||||
# verify that instances can be pickled
|
||||
from collections import namedtuple
|
||||
from pickle import loads, dumps
|
||||
Point = namedtuple('Point', 'x, y', True)
|
||||
p = Point(x=10, y=20)
|
||||
assert p == loads(dumps(p))
|
||||
|
||||
# test and demonstrate ability to override methods
|
||||
class Point(namedtuple('Point', 'x y')):
|
||||
__slots__ = ()
|
||||
@property
|
||||
def hypot(self):
|
||||
return (self.x ** 2 + self.y ** 2) ** 0.5
|
||||
def __str__(self):
|
||||
return 'Point: x=%6.3f y=%6.3f hypot=%6.3f' % (self.x, self.y, self.hypot)
|
||||
|
||||
for p in Point(3, 4), Point(14, 5/7.):
|
||||
print (p)
|
||||
|
||||
class Point(namedtuple('Point', 'x y')):
|
||||
'Point class with optimized _make() and _replace() without error-checking'
|
||||
__slots__ = ()
|
||||
_make = classmethod(tuple.__new__)
|
||||
def _replace(self, _map=map, **kwds):
|
||||
return self._make(_map(kwds.get, ('x', 'y'), self))
|
||||
|
||||
print(Point(11, 22)._replace(x=100))
|
||||
|
||||
Point3D = namedtuple('Point3D', Point._fields + ('z',))
|
||||
print(Point3D.__doc__)
|
||||
|
||||
import doctest, collections
|
||||
TestResults = namedtuple('TestResults', 'failed attempted')
|
||||
print(TestResults(*doctest.testmod(collections)))
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
# Copyright 2009 Brian Quinlan. All Rights Reserved.
|
||||
# Licensed to PSF under a Contributor Agreement.
|
||||
|
||||
"""Execute computations asynchronously using threads or processes."""
|
||||
|
||||
__author__ = 'Brian Quinlan (brian@sweetapp.com)'
|
||||
|
||||
from concurrent.futures._base import (FIRST_COMPLETED,
|
||||
FIRST_EXCEPTION,
|
||||
ALL_COMPLETED,
|
||||
CancelledError,
|
||||
TimeoutError,
|
||||
Future,
|
||||
Executor,
|
||||
wait,
|
||||
as_completed)
|
||||
from concurrent.futures.process import ProcessPoolExecutor
|
||||
from concurrent.futures.thread import ThreadPoolExecutor
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
# This file is transmogrified into Setup.config by config.status.
|
||||
|
||||
# The purpose of this file is to conditionally enable certain modules
|
||||
# based on configure-time options.
|
||||
|
||||
# Threading
|
||||
_thread _threadmodule.c
|
||||
|
||||
# The signal module
|
||||
_signal signalmodule.c
|
||||
|
||||
# The rest of the modules previously listed in this file are built
|
||||
# by the setup.py script in Python 2.1 and later.
|
||||
|
|
@ -1 +0,0 @@
|
|||
../../../Python
|
||||
|
|
@ -1 +0,0 @@
|
|||
../../../Python
|
||||
|
|
@ -1 +0,0 @@
|
|||
../../../Python
|
||||
|
|
@ -1 +0,0 @@
|
|||
../../../Python
|
||||
Binary file not shown.
|
|
@ -1,361 +0,0 @@
|
|||
"""Utilities for with-statement contexts. See PEP 343."""
|
||||
|
||||
import sys
|
||||
from collections import deque
|
||||
from functools import wraps
|
||||
|
||||
__all__ = ["contextmanager", "closing", "ContextDecorator", "ExitStack",
|
||||
"redirect_stdout", "redirect_stderr", "suppress"]
|
||||
|
||||
|
||||
class ContextDecorator(object):
|
||||
"A base class or mixin that enables context managers to work as decorators."
|
||||
|
||||
def _recreate_cm(self):
|
||||
"""Return a recreated instance of self.
|
||||
|
||||
Allows an otherwise one-shot context manager like
|
||||
_GeneratorContextManager to support use as
|
||||
a decorator via implicit recreation.
|
||||
|
||||
This is a private interface just for _GeneratorContextManager.
|
||||
See issue #11647 for details.
|
||||
"""
|
||||
return self
|
||||
|
||||
def __call__(self, func):
|
||||
@wraps(func)
|
||||
def inner(*args, **kwds):
|
||||
with self._recreate_cm():
|
||||
return func(*args, **kwds)
|
||||
return inner
|
||||
|
||||
|
||||
class _GeneratorContextManager(ContextDecorator):
|
||||
"""Helper for @contextmanager decorator."""
|
||||
|
||||
def __init__(self, func, args, kwds):
|
||||
self.gen = func(*args, **kwds)
|
||||
self.func, self.args, self.kwds = func, args, kwds
|
||||
# Issue 19330: ensure context manager instances have good docstrings
|
||||
doc = getattr(func, "__doc__", None)
|
||||
if doc is None:
|
||||
doc = type(self).__doc__
|
||||
self.__doc__ = doc
|
||||
# Unfortunately, this still doesn't provide good help output when
|
||||
# inspecting the created context manager instances, since pydoc
|
||||
# currently bypasses the instance docstring and shows the docstring
|
||||
# for the class instead.
|
||||
# See http://bugs.python.org/issue19404 for more details.
|
||||
|
||||
def _recreate_cm(self):
|
||||
# _GCM instances are one-shot context managers, so the
|
||||
# CM must be recreated each time a decorated function is
|
||||
# called
|
||||
return self.__class__(self.func, self.args, self.kwds)
|
||||
|
||||
def __enter__(self):
|
||||
try:
|
||||
return next(self.gen)
|
||||
except StopIteration:
|
||||
raise RuntimeError("generator didn't yield") from None
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
if type is None:
|
||||
try:
|
||||
next(self.gen)
|
||||
except StopIteration:
|
||||
return
|
||||
else:
|
||||
raise RuntimeError("generator didn't stop")
|
||||
else:
|
||||
if value is None:
|
||||
# Need to force instantiation so we can reliably
|
||||
# tell if we get the same exception back
|
||||
value = type()
|
||||
try:
|
||||
self.gen.throw(type, value, traceback)
|
||||
raise RuntimeError("generator didn't stop after throw()")
|
||||
except StopIteration as exc:
|
||||
# Suppress StopIteration *unless* it's the same exception that
|
||||
# was passed to throw(). This prevents a StopIteration
|
||||
# raised inside the "with" statement from being suppressed.
|
||||
return exc is not value
|
||||
except RuntimeError as exc:
|
||||
# Likewise, avoid suppressing if a StopIteration exception
|
||||
# was passed to throw() and later wrapped into a RuntimeError
|
||||
# (see PEP 479).
|
||||
if exc.__cause__ is value:
|
||||
return False
|
||||
raise
|
||||
except:
|
||||
# only re-raise if it's *not* the exception that was
|
||||
# passed to throw(), because __exit__() must not raise
|
||||
# an exception unless __exit__() itself failed. But throw()
|
||||
# has to raise the exception to signal propagation, so this
|
||||
# fixes the impedance mismatch between the throw() protocol
|
||||
# and the __exit__() protocol.
|
||||
#
|
||||
if sys.exc_info()[1] is not value:
|
||||
raise
|
||||
|
||||
|
||||
def contextmanager(func):
|
||||
"""@contextmanager decorator.
|
||||
|
||||
Typical usage:
|
||||
|
||||
@contextmanager
|
||||
def some_generator(<arguments>):
|
||||
<setup>
|
||||
try:
|
||||
yield <value>
|
||||
finally:
|
||||
<cleanup>
|
||||
|
||||
This makes this:
|
||||
|
||||
with some_generator(<arguments>) as <variable>:
|
||||
<body>
|
||||
|
||||
equivalent to this:
|
||||
|
||||
<setup>
|
||||
try:
|
||||
<variable> = <value>
|
||||
<body>
|
||||
finally:
|
||||
<cleanup>
|
||||
|
||||
"""
|
||||
@wraps(func)
|
||||
def helper(*args, **kwds):
|
||||
return _GeneratorContextManager(func, args, kwds)
|
||||
return helper
|
||||
|
||||
|
||||
class closing(object):
|
||||
"""Context to automatically close something at the end of a block.
|
||||
|
||||
Code like this:
|
||||
|
||||
with closing(<module>.open(<arguments>)) as f:
|
||||
<block>
|
||||
|
||||
is equivalent to this:
|
||||
|
||||
f = <module>.open(<arguments>)
|
||||
try:
|
||||
<block>
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
"""
|
||||
def __init__(self, thing):
|
||||
self.thing = thing
|
||||
def __enter__(self):
|
||||
return self.thing
|
||||
def __exit__(self, *exc_info):
|
||||
self.thing.close()
|
||||
|
||||
|
||||
class _RedirectStream:
|
||||
|
||||
_stream = None
|
||||
|
||||
def __init__(self, new_target):
|
||||
self._new_target = new_target
|
||||
# We use a list of old targets to make this CM re-entrant
|
||||
self._old_targets = []
|
||||
|
||||
def __enter__(self):
|
||||
self._old_targets.append(getattr(sys, self._stream))
|
||||
setattr(sys, self._stream, self._new_target)
|
||||
return self._new_target
|
||||
|
||||
def __exit__(self, exctype, excinst, exctb):
|
||||
setattr(sys, self._stream, self._old_targets.pop())
|
||||
|
||||
|
||||
class redirect_stdout(_RedirectStream):
|
||||
"""Context manager for temporarily redirecting stdout to another file.
|
||||
|
||||
# How to send help() to stderr
|
||||
with redirect_stdout(sys.stderr):
|
||||
help(dir)
|
||||
|
||||
# How to write help() to a file
|
||||
with open('help.txt', 'w') as f:
|
||||
with redirect_stdout(f):
|
||||
help(pow)
|
||||
"""
|
||||
|
||||
_stream = "stdout"
|
||||
|
||||
|
||||
class redirect_stderr(_RedirectStream):
|
||||
"""Context manager for temporarily redirecting stderr to another file."""
|
||||
|
||||
_stream = "stderr"
|
||||
|
||||
|
||||
class suppress:
|
||||
"""Context manager to suppress specified exceptions
|
||||
|
||||
After the exception is suppressed, execution proceeds with the next
|
||||
statement following the with statement.
|
||||
|
||||
with suppress(FileNotFoundError):
|
||||
os.remove(somefile)
|
||||
# Execution still resumes here if the file was already removed
|
||||
"""
|
||||
|
||||
def __init__(self, *exceptions):
|
||||
self._exceptions = exceptions
|
||||
|
||||
def __enter__(self):
|
||||
pass
|
||||
|
||||
def __exit__(self, exctype, excinst, exctb):
|
||||
# Unlike isinstance and issubclass, CPython exception handling
|
||||
# currently only looks at the concrete type hierarchy (ignoring
|
||||
# the instance and subclass checking hooks). While Guido considers
|
||||
# that a bug rather than a feature, it's a fairly hard one to fix
|
||||
# due to various internal implementation details. suppress provides
|
||||
# the simpler issubclass based semantics, rather than trying to
|
||||
# exactly reproduce the limitations of the CPython interpreter.
|
||||
#
|
||||
# See http://bugs.python.org/issue12029 for more details
|
||||
return exctype is not None and issubclass(exctype, self._exceptions)
|
||||
|
||||
|
||||
# Inspired by discussions on http://bugs.python.org/issue13585
|
||||
class ExitStack(object):
|
||||
"""Context manager for dynamic management of a stack of exit callbacks
|
||||
|
||||
For example:
|
||||
|
||||
with ExitStack() as stack:
|
||||
files = [stack.enter_context(open(fname)) for fname in filenames]
|
||||
# All opened files will automatically be closed at the end of
|
||||
# the with statement, even if attempts to open files later
|
||||
# in the list raise an exception
|
||||
|
||||
"""
|
||||
def __init__(self):
|
||||
self._exit_callbacks = deque()
|
||||
|
||||
def pop_all(self):
|
||||
"""Preserve the context stack by transferring it to a new instance"""
|
||||
new_stack = type(self)()
|
||||
new_stack._exit_callbacks = self._exit_callbacks
|
||||
self._exit_callbacks = deque()
|
||||
return new_stack
|
||||
|
||||
def _push_cm_exit(self, cm, cm_exit):
|
||||
"""Helper to correctly register callbacks to __exit__ methods"""
|
||||
def _exit_wrapper(*exc_details):
|
||||
return cm_exit(cm, *exc_details)
|
||||
_exit_wrapper.__self__ = cm
|
||||
self.push(_exit_wrapper)
|
||||
|
||||
def push(self, exit):
|
||||
"""Registers a callback with the standard __exit__ method signature
|
||||
|
||||
Can suppress exceptions the same way __exit__ methods can.
|
||||
|
||||
Also accepts any object with an __exit__ method (registering a call
|
||||
to the method instead of the object itself)
|
||||
"""
|
||||
# We use an unbound method rather than a bound method to follow
|
||||
# the standard lookup behaviour for special methods
|
||||
_cb_type = type(exit)
|
||||
try:
|
||||
exit_method = _cb_type.__exit__
|
||||
except AttributeError:
|
||||
# Not a context manager, so assume its a callable
|
||||
self._exit_callbacks.append(exit)
|
||||
else:
|
||||
self._push_cm_exit(exit, exit_method)
|
||||
return exit # Allow use as a decorator
|
||||
|
||||
def callback(self, callback, *args, **kwds):
|
||||
"""Registers an arbitrary callback and arguments.
|
||||
|
||||
Cannot suppress exceptions.
|
||||
"""
|
||||
def _exit_wrapper(exc_type, exc, tb):
|
||||
callback(*args, **kwds)
|
||||
# We changed the signature, so using @wraps is not appropriate, but
|
||||
# setting __wrapped__ may still help with introspection
|
||||
_exit_wrapper.__wrapped__ = callback
|
||||
self.push(_exit_wrapper)
|
||||
return callback # Allow use as a decorator
|
||||
|
||||
def enter_context(self, cm):
|
||||
"""Enters the supplied context manager
|
||||
|
||||
If successful, also pushes its __exit__ method as a callback and
|
||||
returns the result of the __enter__ method.
|
||||
"""
|
||||
# We look up the special methods on the type to match the with statement
|
||||
_cm_type = type(cm)
|
||||
_exit = _cm_type.__exit__
|
||||
result = _cm_type.__enter__(cm)
|
||||
self._push_cm_exit(cm, _exit)
|
||||
return result
|
||||
|
||||
def close(self):
|
||||
"""Immediately unwind the context stack"""
|
||||
self.__exit__(None, None, None)
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, *exc_details):
|
||||
received_exc = exc_details[0] is not None
|
||||
|
||||
# We manipulate the exception state so it behaves as though
|
||||
# we were actually nesting multiple with statements
|
||||
frame_exc = sys.exc_info()[1]
|
||||
def _fix_exception_context(new_exc, old_exc):
|
||||
# Context may not be correct, so find the end of the chain
|
||||
while 1:
|
||||
exc_context = new_exc.__context__
|
||||
if exc_context is old_exc:
|
||||
# Context is already set correctly (see issue 20317)
|
||||
return
|
||||
if exc_context is None or exc_context is frame_exc:
|
||||
break
|
||||
new_exc = exc_context
|
||||
# Change the end of the chain to point to the exception
|
||||
# we expect it to reference
|
||||
new_exc.__context__ = old_exc
|
||||
|
||||
# Callbacks are invoked in LIFO order to match the behaviour of
|
||||
# nested context managers
|
||||
suppressed_exc = False
|
||||
pending_raise = False
|
||||
while self._exit_callbacks:
|
||||
cb = self._exit_callbacks.pop()
|
||||
try:
|
||||
if cb(*exc_details):
|
||||
suppressed_exc = True
|
||||
pending_raise = False
|
||||
exc_details = (None, None, None)
|
||||
except:
|
||||
new_exc_details = sys.exc_info()
|
||||
# simulate the stack of exceptions by setting the context
|
||||
_fix_exception_context(new_exc_details[1], exc_details[1])
|
||||
pending_raise = True
|
||||
exc_details = new_exc_details
|
||||
if pending_raise:
|
||||
try:
|
||||
# bare "raise exc_details[1]" replaces our carefully
|
||||
# set-up context
|
||||
fixed_ctx = exc_details[1].__context__
|
||||
raise exc_details[1]
|
||||
except BaseException:
|
||||
exc_details[1].__context__ = fixed_ctx
|
||||
raise
|
||||
return received_exc and suppressed_exc
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
"""Wrapper to the POSIX crypt library call and associated functionality."""
|
||||
|
||||
import _crypt
|
||||
import string as _string
|
||||
from random import SystemRandom as _SystemRandom
|
||||
from collections import namedtuple as _namedtuple
|
||||
|
||||
|
||||
_saltchars = _string.ascii_letters + _string.digits + './'
|
||||
_sr = _SystemRandom()
|
||||
|
||||
|
||||
class _Method(_namedtuple('_Method', 'name ident salt_chars total_size')):
|
||||
|
||||
"""Class representing a salt method per the Modular Crypt Format or the
|
||||
legacy 2-character crypt method."""
|
||||
|
||||
def __repr__(self):
|
||||
return '<crypt.METHOD_{}>'.format(self.name)
|
||||
|
||||
|
||||
def mksalt(method=None):
|
||||
"""Generate a salt for the specified method.
|
||||
|
||||
If not specified, the strongest available method will be used.
|
||||
|
||||
"""
|
||||
if method is None:
|
||||
method = methods[0]
|
||||
s = '${}$'.format(method.ident) if method.ident else ''
|
||||
s += ''.join(_sr.choice(_saltchars) for char in range(method.salt_chars))
|
||||
return s
|
||||
|
||||
|
||||
def crypt(word, salt=None):
|
||||
"""Return a string representing the one-way hash of a password, with a salt
|
||||
prepended.
|
||||
|
||||
If ``salt`` is not specified or is ``None``, the strongest
|
||||
available method will be selected and a salt generated. Otherwise,
|
||||
``salt`` may be one of the ``crypt.METHOD_*`` values, or a string as
|
||||
returned by ``crypt.mksalt()``.
|
||||
|
||||
"""
|
||||
if salt is None or isinstance(salt, _Method):
|
||||
salt = mksalt(salt)
|
||||
return _crypt.crypt(word, salt)
|
||||
|
||||
|
||||
# available salting/crypto methods
|
||||
METHOD_CRYPT = _Method('CRYPT', None, 2, 13)
|
||||
METHOD_MD5 = _Method('MD5', '1', 8, 34)
|
||||
METHOD_SHA256 = _Method('SHA256', '5', 16, 63)
|
||||
METHOD_SHA512 = _Method('SHA512', '6', 16, 106)
|
||||
|
||||
methods = []
|
||||
for _method in (METHOD_SHA512, METHOD_SHA256, METHOD_MD5):
|
||||
_result = crypt('', _method)
|
||||
if _result and len(_result) == _method.total_size:
|
||||
methods.append(_method)
|
||||
methods.append(METHOD_CRYPT)
|
||||
del _result, _method
|
||||
|
|
@ -1,91 +0,0 @@
|
|||
import unittest
|
||||
import os
|
||||
import sys
|
||||
import test.support
|
||||
from ctypes import *
|
||||
from ctypes.util import find_library
|
||||
|
||||
# On some systems, loading the OpenGL libraries needs the RTLD_GLOBAL mode.
|
||||
class Test_OpenGL_libs(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
lib_gl = lib_glu = lib_gle = None
|
||||
if sys.platform == "win32":
|
||||
lib_gl = find_library("OpenGL32")
|
||||
lib_glu = find_library("Glu32")
|
||||
elif sys.platform == "darwin":
|
||||
lib_gl = lib_glu = find_library("OpenGL")
|
||||
else:
|
||||
lib_gl = find_library("GL")
|
||||
lib_glu = find_library("GLU")
|
||||
lib_gle = find_library("gle")
|
||||
|
||||
## print, for debugging
|
||||
if test.support.verbose:
|
||||
print("OpenGL libraries:")
|
||||
for item in (("GL", lib_gl),
|
||||
("GLU", lib_glu),
|
||||
("gle", lib_gle)):
|
||||
print("\t", item)
|
||||
|
||||
cls.gl = cls.glu = cls.gle = None
|
||||
if lib_gl:
|
||||
try:
|
||||
cls.gl = CDLL(lib_gl, mode=RTLD_GLOBAL)
|
||||
except OSError:
|
||||
pass
|
||||
if lib_glu:
|
||||
try:
|
||||
cls.glu = CDLL(lib_glu, RTLD_GLOBAL)
|
||||
except OSError:
|
||||
pass
|
||||
if lib_gle:
|
||||
try:
|
||||
cls.gle = CDLL(lib_gle)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.gl = cls.glu = cls.gle = None
|
||||
|
||||
def test_gl(self):
|
||||
if self.gl is None:
|
||||
self.skipTest('lib_gl not available')
|
||||
self.gl.glClearIndex
|
||||
|
||||
def test_glu(self):
|
||||
if self.glu is None:
|
||||
self.skipTest('lib_glu not available')
|
||||
self.glu.gluBeginCurve
|
||||
|
||||
def test_gle(self):
|
||||
if self.gle is None:
|
||||
self.skipTest('lib_gle not available')
|
||||
self.gle.gleGetJoinStyle
|
||||
|
||||
# On platforms where the default shared library suffix is '.so',
|
||||
# at least some libraries can be loaded as attributes of the cdll
|
||||
# object, since ctypes now tries loading the lib again
|
||||
# with '.so' appended of the first try fails.
|
||||
#
|
||||
# Won't work for libc, unfortunately. OTOH, it isn't
|
||||
# needed for libc since this is already mapped into the current
|
||||
# process (?)
|
||||
#
|
||||
# On MAC OSX, it won't work either, because dlopen() needs a full path,
|
||||
# and the default suffix is either none or '.dylib'.
|
||||
@unittest.skip('test disabled')
|
||||
@unittest.skipUnless(os.name=="posix" and sys.platform != "darwin",
|
||||
'test not suitable for this platform')
|
||||
class LoadLibs(unittest.TestCase):
|
||||
def test_libm(self):
|
||||
import math
|
||||
libm = cdll.libm
|
||||
sqrt = libm.sqrt
|
||||
sqrt.argtypes = (c_double,)
|
||||
sqrt.restype = c_double
|
||||
self.assertEqual(sqrt(2), math.sqrt(2))
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Binary file not shown.
Binary file not shown.
|
|
@ -1,6 +0,0 @@
|
|||
[install]
|
||||
prefix=/Users/build/.local
|
||||
|
||||
[build_ext]
|
||||
include_dirs=/Users/build/.local/include:/Users/build/.local/opt/openssl/include:/Users/build/.local/opt/sqlite/include
|
||||
library_dirs=/Users/build/.local/lib:/Users/build/.local/opt/openssl/lib:/Users/build/.local/opt/sqlite/lib
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
"""Tests for distutils.log"""
|
||||
|
||||
import sys
|
||||
import unittest
|
||||
from tempfile import NamedTemporaryFile
|
||||
from test.support import run_unittest
|
||||
|
||||
from distutils import log
|
||||
|
||||
class TestLog(unittest.TestCase):
|
||||
def test_non_ascii(self):
|
||||
# Issue #8663: test that non-ASCII text is escaped with
|
||||
# backslashreplace error handler (stream use ASCII encoding and strict
|
||||
# error handler)
|
||||
old_stdout = sys.stdout
|
||||
old_stderr = sys.stderr
|
||||
try:
|
||||
log.set_threshold(log.DEBUG)
|
||||
with NamedTemporaryFile(mode="w+", encoding='ascii') as stdout, \
|
||||
NamedTemporaryFile(mode="w+", encoding='ascii') as stderr:
|
||||
sys.stdout = stdout
|
||||
sys.stderr = stderr
|
||||
log.debug("debug:\xe9")
|
||||
log.fatal("fatal:\xe9")
|
||||
stdout.seek(0)
|
||||
self.assertEqual(stdout.read().rstrip(), "debug:\\xe9")
|
||||
stderr.seek(0)
|
||||
self.assertEqual(stderr.read().rstrip(), "fatal:\\xe9")
|
||||
finally:
|
||||
sys.stdout = old_stdout
|
||||
sys.stderr = old_stderr
|
||||
|
||||
def test_suite():
|
||||
return unittest.makeSuite(TestLog)
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_unittest(test_suite())
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
"""Tests for distutils.spawn."""
|
||||
import unittest
|
||||
import os
|
||||
import time
|
||||
from test.support import captured_stdout, run_unittest
|
||||
|
||||
from distutils.spawn import _nt_quote_args
|
||||
from distutils.spawn import spawn, find_executable
|
||||
from distutils.errors import DistutilsExecError
|
||||
from distutils.tests import support
|
||||
|
||||
class SpawnTestCase(support.TempdirManager,
|
||||
support.LoggingSilencer,
|
||||
unittest.TestCase):
|
||||
|
||||
def test_nt_quote_args(self):
|
||||
|
||||
for (args, wanted) in ((['with space', 'nospace'],
|
||||
['"with space"', 'nospace']),
|
||||
(['nochange', 'nospace'],
|
||||
['nochange', 'nospace'])):
|
||||
res = _nt_quote_args(args)
|
||||
self.assertEqual(res, wanted)
|
||||
|
||||
|
||||
@unittest.skipUnless(os.name in ('nt', 'posix'),
|
||||
'Runs only under posix or nt')
|
||||
def test_spawn(self):
|
||||
tmpdir = self.mkdtemp()
|
||||
|
||||
# creating something executable
|
||||
# through the shell that returns 1
|
||||
if os.name == 'posix':
|
||||
exe = os.path.join(tmpdir, 'foo.sh')
|
||||
self.write_file(exe, '#!/bin/sh\nexit 1')
|
||||
else:
|
||||
exe = os.path.join(tmpdir, 'foo.bat')
|
||||
self.write_file(exe, 'exit 1')
|
||||
|
||||
os.chmod(exe, 0o777)
|
||||
self.assertRaises(DistutilsExecError, spawn, [exe])
|
||||
|
||||
# now something that works
|
||||
if os.name == 'posix':
|
||||
exe = os.path.join(tmpdir, 'foo.sh')
|
||||
self.write_file(exe, '#!/bin/sh\nexit 0')
|
||||
else:
|
||||
exe = os.path.join(tmpdir, 'foo.bat')
|
||||
self.write_file(exe, 'exit 0')
|
||||
|
||||
os.chmod(exe, 0o777)
|
||||
spawn([exe]) # should work without any error
|
||||
|
||||
def test_suite():
|
||||
return unittest.makeSuite(SpawnTestCase)
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_unittest(test_suite())
|
||||
Binary file not shown.
Binary file not shown.
|
|
@ -1,160 +0,0 @@
|
|||
"""A CallTip window class for Tkinter/IDLE.
|
||||
|
||||
After ToolTip.py, which uses ideas gleaned from PySol
|
||||
Used by the CallTips IDLE extension.
|
||||
"""
|
||||
from tkinter import Toplevel, Label, LEFT, SOLID, TclError
|
||||
|
||||
HIDE_VIRTUAL_EVENT_NAME = "<<calltipwindow-hide>>"
|
||||
HIDE_SEQUENCES = ("<Key-Escape>", "<FocusOut>")
|
||||
CHECKHIDE_VIRTUAL_EVENT_NAME = "<<calltipwindow-checkhide>>"
|
||||
CHECKHIDE_SEQUENCES = ("<KeyRelease>", "<ButtonRelease>")
|
||||
CHECKHIDE_TIME = 100 # miliseconds
|
||||
|
||||
MARK_RIGHT = "calltipwindowregion_right"
|
||||
|
||||
class CallTip:
|
||||
|
||||
def __init__(self, widget):
|
||||
self.widget = widget
|
||||
self.tipwindow = self.label = None
|
||||
self.parenline = self.parencol = None
|
||||
self.lastline = None
|
||||
self.hideid = self.checkhideid = None
|
||||
self.checkhide_after_id = None
|
||||
|
||||
def position_window(self):
|
||||
"""Check if needs to reposition the window, and if so - do it."""
|
||||
curline = int(self.widget.index("insert").split('.')[0])
|
||||
if curline == self.lastline:
|
||||
return
|
||||
self.lastline = curline
|
||||
self.widget.see("insert")
|
||||
if curline == self.parenline:
|
||||
box = self.widget.bbox("%d.%d" % (self.parenline,
|
||||
self.parencol))
|
||||
else:
|
||||
box = self.widget.bbox("%d.0" % curline)
|
||||
if not box:
|
||||
box = list(self.widget.bbox("insert"))
|
||||
# align to left of window
|
||||
box[0] = 0
|
||||
box[2] = 0
|
||||
x = box[0] + self.widget.winfo_rootx() + 2
|
||||
y = box[1] + box[3] + self.widget.winfo_rooty()
|
||||
self.tipwindow.wm_geometry("+%d+%d" % (x, y))
|
||||
|
||||
def showtip(self, text, parenleft, parenright):
|
||||
"""Show the calltip, bind events which will close it and reposition it.
|
||||
"""
|
||||
# Only called in CallTips, where lines are truncated
|
||||
self.text = text
|
||||
if self.tipwindow or not self.text:
|
||||
return
|
||||
|
||||
self.widget.mark_set(MARK_RIGHT, parenright)
|
||||
self.parenline, self.parencol = map(
|
||||
int, self.widget.index(parenleft).split("."))
|
||||
|
||||
self.tipwindow = tw = Toplevel(self.widget)
|
||||
self.position_window()
|
||||
# remove border on calltip window
|
||||
tw.wm_overrideredirect(1)
|
||||
try:
|
||||
# This command is only needed and available on Tk >= 8.4.0 for OSX
|
||||
# Without it, call tips intrude on the typing process by grabbing
|
||||
# the focus.
|
||||
tw.tk.call("::tk::unsupported::MacWindowStyle", "style", tw._w,
|
||||
"help", "noActivates")
|
||||
except TclError:
|
||||
pass
|
||||
self.label = Label(tw, text=self.text, justify=LEFT,
|
||||
background="#ffffe0", relief=SOLID, borderwidth=1,
|
||||
font = self.widget['font'])
|
||||
self.label.pack()
|
||||
|
||||
self.checkhideid = self.widget.bind(CHECKHIDE_VIRTUAL_EVENT_NAME,
|
||||
self.checkhide_event)
|
||||
for seq in CHECKHIDE_SEQUENCES:
|
||||
self.widget.event_add(CHECKHIDE_VIRTUAL_EVENT_NAME, seq)
|
||||
self.widget.after(CHECKHIDE_TIME, self.checkhide_event)
|
||||
self.hideid = self.widget.bind(HIDE_VIRTUAL_EVENT_NAME,
|
||||
self.hide_event)
|
||||
for seq in HIDE_SEQUENCES:
|
||||
self.widget.event_add(HIDE_VIRTUAL_EVENT_NAME, seq)
|
||||
|
||||
def checkhide_event(self, event=None):
|
||||
if not self.tipwindow:
|
||||
# If the event was triggered by the same event that unbinded
|
||||
# this function, the function will be called nevertheless,
|
||||
# so do nothing in this case.
|
||||
return
|
||||
curline, curcol = map(int, self.widget.index("insert").split('.'))
|
||||
if curline < self.parenline or \
|
||||
(curline == self.parenline and curcol <= self.parencol) or \
|
||||
self.widget.compare("insert", ">", MARK_RIGHT):
|
||||
self.hidetip()
|
||||
else:
|
||||
self.position_window()
|
||||
if self.checkhide_after_id is not None:
|
||||
self.widget.after_cancel(self.checkhide_after_id)
|
||||
self.checkhide_after_id = \
|
||||
self.widget.after(CHECKHIDE_TIME, self.checkhide_event)
|
||||
|
||||
def hide_event(self, event):
|
||||
if not self.tipwindow:
|
||||
# See the explanation in checkhide_event.
|
||||
return
|
||||
self.hidetip()
|
||||
|
||||
def hidetip(self):
|
||||
if not self.tipwindow:
|
||||
return
|
||||
|
||||
for seq in CHECKHIDE_SEQUENCES:
|
||||
self.widget.event_delete(CHECKHIDE_VIRTUAL_EVENT_NAME, seq)
|
||||
self.widget.unbind(CHECKHIDE_VIRTUAL_EVENT_NAME, self.checkhideid)
|
||||
self.checkhideid = None
|
||||
for seq in HIDE_SEQUENCES:
|
||||
self.widget.event_delete(HIDE_VIRTUAL_EVENT_NAME, seq)
|
||||
self.widget.unbind(HIDE_VIRTUAL_EVENT_NAME, self.hideid)
|
||||
self.hideid = None
|
||||
|
||||
self.label.destroy()
|
||||
self.label = None
|
||||
self.tipwindow.destroy()
|
||||
self.tipwindow = None
|
||||
|
||||
self.widget.mark_unset(MARK_RIGHT)
|
||||
self.parenline = self.parencol = self.lastline = None
|
||||
|
||||
def is_active(self):
|
||||
return bool(self.tipwindow)
|
||||
|
||||
|
||||
def _calltip_window(parent): # htest #
|
||||
from tkinter import Toplevel, Text, LEFT, BOTH
|
||||
|
||||
top = Toplevel(parent)
|
||||
top.title("Test calltips")
|
||||
top.geometry("200x100+%d+%d" % (parent.winfo_rootx() + 200,
|
||||
parent.winfo_rooty() + 150))
|
||||
text = Text(top)
|
||||
text.pack(side=LEFT, fill=BOTH, expand=1)
|
||||
text.insert("insert", "string.split")
|
||||
top.update()
|
||||
calltip = CallTip(text)
|
||||
|
||||
def calltip_show(event):
|
||||
calltip.showtip("(s=Hello world)", "insert", "end")
|
||||
def calltip_hide(event):
|
||||
calltip.hidetip()
|
||||
text.event_add("<<calltip-show>>", "(")
|
||||
text.event_add("<<calltip-hide>>", ")")
|
||||
text.bind("<<calltip-show>>", calltip_show)
|
||||
text.bind("<<calltip-hide>>", calltip_hide)
|
||||
text.focus_set()
|
||||
|
||||
if __name__=='__main__':
|
||||
from idlelib.idle_test.htest import run
|
||||
run(_calltip_window)
|
||||
|
|
@ -1,236 +0,0 @@
|
|||
"""Class browser.
|
||||
|
||||
XXX TO DO:
|
||||
|
||||
- reparse when source changed (maybe just a button would be OK?)
|
||||
(or recheck on window popup)
|
||||
- add popup menu with more options (e.g. doc strings, base classes, imports)
|
||||
- show function argument list? (have to do pattern matching on source)
|
||||
- should the classes and methods lists also be in the module's menu bar?
|
||||
- add base classes to class browser tree
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import pyclbr
|
||||
|
||||
from idlelib import PyShell
|
||||
from idlelib.WindowList import ListedToplevel
|
||||
from idlelib.TreeWidget import TreeNode, TreeItem, ScrolledCanvas
|
||||
from idlelib.configHandler import idleConf
|
||||
|
||||
file_open = None # Method...Item and Class...Item use this.
|
||||
# Normally PyShell.flist.open, but there is no PyShell.flist for htest.
|
||||
|
||||
class ClassBrowser:
|
||||
|
||||
def __init__(self, flist, name, path, _htest=False):
|
||||
# XXX This API should change, if the file doesn't end in ".py"
|
||||
# XXX the code here is bogus!
|
||||
"""
|
||||
_htest - bool, change box when location running htest.
|
||||
"""
|
||||
global file_open
|
||||
if not _htest:
|
||||
file_open = PyShell.flist.open
|
||||
self.name = name
|
||||
self.file = os.path.join(path[0], self.name + ".py")
|
||||
self._htest = _htest
|
||||
self.init(flist)
|
||||
|
||||
def close(self, event=None):
|
||||
self.top.destroy()
|
||||
self.node.destroy()
|
||||
|
||||
def init(self, flist):
|
||||
self.flist = flist
|
||||
# reset pyclbr
|
||||
pyclbr._modules.clear()
|
||||
# create top
|
||||
self.top = top = ListedToplevel(flist.root)
|
||||
top.protocol("WM_DELETE_WINDOW", self.close)
|
||||
top.bind("<Escape>", self.close)
|
||||
if self._htest: # place dialog below parent if running htest
|
||||
top.geometry("+%d+%d" %
|
||||
(flist.root.winfo_rootx(), flist.root.winfo_rooty() + 200))
|
||||
self.settitle()
|
||||
top.focus_set()
|
||||
# create scrolled canvas
|
||||
theme = idleConf.GetOption('main','Theme','name')
|
||||
background = idleConf.GetHighlight(theme, 'normal')['background']
|
||||
sc = ScrolledCanvas(top, bg=background, highlightthickness=0, takefocus=1)
|
||||
sc.frame.pack(expand=1, fill="both")
|
||||
item = self.rootnode()
|
||||
self.node = node = TreeNode(sc.canvas, None, item)
|
||||
node.update()
|
||||
node.expand()
|
||||
|
||||
def settitle(self):
|
||||
self.top.wm_title("Class Browser - " + self.name)
|
||||
self.top.wm_iconname("Class Browser")
|
||||
|
||||
def rootnode(self):
|
||||
return ModuleBrowserTreeItem(self.file)
|
||||
|
||||
class ModuleBrowserTreeItem(TreeItem):
|
||||
|
||||
def __init__(self, file):
|
||||
self.file = file
|
||||
|
||||
def GetText(self):
|
||||
return os.path.basename(self.file)
|
||||
|
||||
def GetIconName(self):
|
||||
return "python"
|
||||
|
||||
def GetSubList(self):
|
||||
sublist = []
|
||||
for name in self.listclasses():
|
||||
item = ClassBrowserTreeItem(name, self.classes, self.file)
|
||||
sublist.append(item)
|
||||
return sublist
|
||||
|
||||
def OnDoubleClick(self):
|
||||
if os.path.normcase(self.file[-3:]) != ".py":
|
||||
return
|
||||
if not os.path.exists(self.file):
|
||||
return
|
||||
PyShell.flist.open(self.file)
|
||||
|
||||
def IsExpandable(self):
|
||||
return os.path.normcase(self.file[-3:]) == ".py"
|
||||
|
||||
def listclasses(self):
|
||||
dir, file = os.path.split(self.file)
|
||||
name, ext = os.path.splitext(file)
|
||||
if os.path.normcase(ext) != ".py":
|
||||
return []
|
||||
try:
|
||||
dict = pyclbr.readmodule_ex(name, [dir] + sys.path)
|
||||
except ImportError:
|
||||
return []
|
||||
items = []
|
||||
self.classes = {}
|
||||
for key, cl in dict.items():
|
||||
if cl.module == name:
|
||||
s = key
|
||||
if hasattr(cl, 'super') and cl.super:
|
||||
supers = []
|
||||
for sup in cl.super:
|
||||
if type(sup) is type(''):
|
||||
sname = sup
|
||||
else:
|
||||
sname = sup.name
|
||||
if sup.module != cl.module:
|
||||
sname = "%s.%s" % (sup.module, sname)
|
||||
supers.append(sname)
|
||||
s = s + "(%s)" % ", ".join(supers)
|
||||
items.append((cl.lineno, s))
|
||||
self.classes[s] = cl
|
||||
items.sort()
|
||||
list = []
|
||||
for item, s in items:
|
||||
list.append(s)
|
||||
return list
|
||||
|
||||
class ClassBrowserTreeItem(TreeItem):
|
||||
|
||||
def __init__(self, name, classes, file):
|
||||
self.name = name
|
||||
self.classes = classes
|
||||
self.file = file
|
||||
try:
|
||||
self.cl = self.classes[self.name]
|
||||
except (IndexError, KeyError):
|
||||
self.cl = None
|
||||
self.isfunction = isinstance(self.cl, pyclbr.Function)
|
||||
|
||||
def GetText(self):
|
||||
if self.isfunction:
|
||||
return "def " + self.name + "(...)"
|
||||
else:
|
||||
return "class " + self.name
|
||||
|
||||
def GetIconName(self):
|
||||
if self.isfunction:
|
||||
return "python"
|
||||
else:
|
||||
return "folder"
|
||||
|
||||
def IsExpandable(self):
|
||||
if self.cl:
|
||||
try:
|
||||
return not not self.cl.methods
|
||||
except AttributeError:
|
||||
return False
|
||||
|
||||
def GetSubList(self):
|
||||
if not self.cl:
|
||||
return []
|
||||
sublist = []
|
||||
for name in self.listmethods():
|
||||
item = MethodBrowserTreeItem(name, self.cl, self.file)
|
||||
sublist.append(item)
|
||||
return sublist
|
||||
|
||||
def OnDoubleClick(self):
|
||||
if not os.path.exists(self.file):
|
||||
return
|
||||
edit = file_open(self.file)
|
||||
if hasattr(self.cl, 'lineno'):
|
||||
lineno = self.cl.lineno
|
||||
edit.gotoline(lineno)
|
||||
|
||||
def listmethods(self):
|
||||
if not self.cl:
|
||||
return []
|
||||
items = []
|
||||
for name, lineno in self.cl.methods.items():
|
||||
items.append((lineno, name))
|
||||
items.sort()
|
||||
list = []
|
||||
for item, name in items:
|
||||
list.append(name)
|
||||
return list
|
||||
|
||||
class MethodBrowserTreeItem(TreeItem):
|
||||
|
||||
def __init__(self, name, cl, file):
|
||||
self.name = name
|
||||
self.cl = cl
|
||||
self.file = file
|
||||
|
||||
def GetText(self):
|
||||
return "def " + self.name + "(...)"
|
||||
|
||||
def GetIconName(self):
|
||||
return "python" # XXX
|
||||
|
||||
def IsExpandable(self):
|
||||
return 0
|
||||
|
||||
def OnDoubleClick(self):
|
||||
if not os.path.exists(self.file):
|
||||
return
|
||||
edit = file_open(self.file)
|
||||
edit.gotoline(self.cl.methods[self.name])
|
||||
|
||||
def _class_browser(parent): #Wrapper for htest
|
||||
try:
|
||||
file = __file__
|
||||
except NameError:
|
||||
file = sys.argv[0]
|
||||
if sys.argv[1:]:
|
||||
file = sys.argv[1]
|
||||
else:
|
||||
file = sys.argv[0]
|
||||
dir, file = os.path.split(file)
|
||||
name = os.path.splitext(file)[0]
|
||||
flist = PyShell.PyShellFileList(parent)
|
||||
global file_open
|
||||
file_open = flist.open
|
||||
ClassBrowser(flist, name, [dir], _htest=True)
|
||||
|
||||
if __name__ == "__main__":
|
||||
from idlelib.idle_test.htest import run
|
||||
run(_class_browser)
|
||||
|
|
@ -1,176 +0,0 @@
|
|||
"""CodeContext - Extension to display the block context above the edit window
|
||||
|
||||
Once code has scrolled off the top of a window, it can be difficult to
|
||||
determine which block you are in. This extension implements a pane at the top
|
||||
of each IDLE edit window which provides block structure hints. These hints are
|
||||
the lines which contain the block opening keywords, e.g. 'if', for the
|
||||
enclosing block. The number of hint lines is determined by the numlines
|
||||
variable in the CodeContext section of config-extensions.def. Lines which do
|
||||
not open blocks are not shown in the context hints pane.
|
||||
|
||||
"""
|
||||
import tkinter
|
||||
from tkinter.constants import TOP, LEFT, X, W, SUNKEN
|
||||
import re
|
||||
from sys import maxsize as INFINITY
|
||||
from idlelib.configHandler import idleConf
|
||||
|
||||
BLOCKOPENERS = {"class", "def", "elif", "else", "except", "finally", "for",
|
||||
"if", "try", "while", "with"}
|
||||
UPDATEINTERVAL = 100 # millisec
|
||||
FONTUPDATEINTERVAL = 1000 # millisec
|
||||
|
||||
getspacesfirstword =\
|
||||
lambda s, c=re.compile(r"^(\s*)(\w*)"): c.match(s).groups()
|
||||
|
||||
class CodeContext:
|
||||
menudefs = [('options', [('!Code Conte_xt', '<<toggle-code-context>>')])]
|
||||
context_depth = idleConf.GetOption("extensions", "CodeContext",
|
||||
"numlines", type="int", default=3)
|
||||
bgcolor = idleConf.GetOption("extensions", "CodeContext",
|
||||
"bgcolor", type="str", default="LightGray")
|
||||
fgcolor = idleConf.GetOption("extensions", "CodeContext",
|
||||
"fgcolor", type="str", default="Black")
|
||||
def __init__(self, editwin):
|
||||
self.editwin = editwin
|
||||
self.text = editwin.text
|
||||
self.textfont = self.text["font"]
|
||||
self.label = None
|
||||
# self.info is a list of (line number, indent level, line text, block
|
||||
# keyword) tuples providing the block structure associated with
|
||||
# self.topvisible (the linenumber of the line displayed at the top of
|
||||
# the edit window). self.info[0] is initialized as a 'dummy' line which
|
||||
# starts the toplevel 'block' of the module.
|
||||
self.info = [(0, -1, "", False)]
|
||||
self.topvisible = 1
|
||||
visible = idleConf.GetOption("extensions", "CodeContext",
|
||||
"visible", type="bool", default=False)
|
||||
if visible:
|
||||
self.toggle_code_context_event()
|
||||
self.editwin.setvar('<<toggle-code-context>>', True)
|
||||
# Start two update cycles, one for context lines, one for font changes.
|
||||
self.text.after(UPDATEINTERVAL, self.timer_event)
|
||||
self.text.after(FONTUPDATEINTERVAL, self.font_timer_event)
|
||||
|
||||
def toggle_code_context_event(self, event=None):
|
||||
if not self.label:
|
||||
# Calculate the border width and horizontal padding required to
|
||||
# align the context with the text in the main Text widget.
|
||||
#
|
||||
# All values are passed through getint(), since some
|
||||
# values may be pixel objects, which can't simply be added to ints.
|
||||
widgets = self.editwin.text, self.editwin.text_frame
|
||||
# Calculate the required vertical padding
|
||||
padx = 0
|
||||
for widget in widgets:
|
||||
padx += widget.tk.getint(widget.pack_info()['padx'])
|
||||
padx += widget.tk.getint(widget.cget('padx'))
|
||||
# Calculate the required border width
|
||||
border = 0
|
||||
for widget in widgets:
|
||||
border += widget.tk.getint(widget.cget('border'))
|
||||
self.label = tkinter.Label(self.editwin.top,
|
||||
text="\n" * (self.context_depth - 1),
|
||||
anchor=W, justify=LEFT,
|
||||
font=self.textfont,
|
||||
bg=self.bgcolor, fg=self.fgcolor,
|
||||
width=1, #don't request more than we get
|
||||
padx=padx, border=border,
|
||||
relief=SUNKEN)
|
||||
# Pack the label widget before and above the text_frame widget,
|
||||
# thus ensuring that it will appear directly above text_frame
|
||||
self.label.pack(side=TOP, fill=X, expand=False,
|
||||
before=self.editwin.text_frame)
|
||||
else:
|
||||
self.label.destroy()
|
||||
self.label = None
|
||||
idleConf.SetOption("extensions", "CodeContext", "visible",
|
||||
str(self.label is not None))
|
||||
idleConf.SaveUserCfgFiles()
|
||||
|
||||
def get_line_info(self, linenum):
|
||||
"""Get the line indent value, text, and any block start keyword
|
||||
|
||||
If the line does not start a block, the keyword value is False.
|
||||
The indentation of empty lines (or comment lines) is INFINITY.
|
||||
|
||||
"""
|
||||
text = self.text.get("%d.0" % linenum, "%d.end" % linenum)
|
||||
spaces, firstword = getspacesfirstword(text)
|
||||
opener = firstword in BLOCKOPENERS and firstword
|
||||
if len(text) == len(spaces) or text[len(spaces)] == '#':
|
||||
indent = INFINITY
|
||||
else:
|
||||
indent = len(spaces)
|
||||
return indent, text, opener
|
||||
|
||||
def get_context(self, new_topvisible, stopline=1, stopindent=0):
|
||||
"""Get context lines, starting at new_topvisible and working backwards.
|
||||
|
||||
Stop when stopline or stopindent is reached. Return a tuple of context
|
||||
data and the indent level at the top of the region inspected.
|
||||
|
||||
"""
|
||||
assert stopline > 0
|
||||
lines = []
|
||||
# The indentation level we are currently in:
|
||||
lastindent = INFINITY
|
||||
# For a line to be interesting, it must begin with a block opening
|
||||
# keyword, and have less indentation than lastindent.
|
||||
for linenum in range(new_topvisible, stopline-1, -1):
|
||||
indent, text, opener = self.get_line_info(linenum)
|
||||
if indent < lastindent:
|
||||
lastindent = indent
|
||||
if opener in ("else", "elif"):
|
||||
# We also show the if statement
|
||||
lastindent += 1
|
||||
if opener and linenum < new_topvisible and indent >= stopindent:
|
||||
lines.append((linenum, indent, text, opener))
|
||||
if lastindent <= stopindent:
|
||||
break
|
||||
lines.reverse()
|
||||
return lines, lastindent
|
||||
|
||||
def update_code_context(self):
|
||||
"""Update context information and lines visible in the context pane.
|
||||
|
||||
"""
|
||||
new_topvisible = int(self.text.index("@0,0").split('.')[0])
|
||||
if self.topvisible == new_topvisible: # haven't scrolled
|
||||
return
|
||||
if self.topvisible < new_topvisible: # scroll down
|
||||
lines, lastindent = self.get_context(new_topvisible,
|
||||
self.topvisible)
|
||||
# retain only context info applicable to the region
|
||||
# between topvisible and new_topvisible:
|
||||
while self.info[-1][1] >= lastindent:
|
||||
del self.info[-1]
|
||||
elif self.topvisible > new_topvisible: # scroll up
|
||||
stopindent = self.info[-1][1] + 1
|
||||
# retain only context info associated
|
||||
# with lines above new_topvisible:
|
||||
while self.info[-1][0] >= new_topvisible:
|
||||
stopindent = self.info[-1][1]
|
||||
del self.info[-1]
|
||||
lines, lastindent = self.get_context(new_topvisible,
|
||||
self.info[-1][0]+1,
|
||||
stopindent)
|
||||
self.info.extend(lines)
|
||||
self.topvisible = new_topvisible
|
||||
# empty lines in context pane:
|
||||
context_strings = [""] * max(0, self.context_depth - len(self.info))
|
||||
# followed by the context hint lines:
|
||||
context_strings += [x[2] for x in self.info[-self.context_depth:]]
|
||||
self.label["text"] = '\n'.join(context_strings)
|
||||
|
||||
def timer_event(self):
|
||||
if self.label:
|
||||
self.update_code_context()
|
||||
self.text.after(UPDATEINTERVAL, self.timer_event)
|
||||
|
||||
def font_timer_event(self):
|
||||
newtextfont = self.text["font"]
|
||||
if self.label and newtextfont != self.textfont:
|
||||
self.textfont = newtextfont
|
||||
self.label["font"] = self.textfont
|
||||
self.text.after(FONTUPDATEINTERVAL, self.font_timer_event)
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
from tkinter import *
|
||||
|
||||
class MultiStatusBar(Frame):
|
||||
|
||||
def __init__(self, master=None, **kw):
|
||||
if master is None:
|
||||
master = Tk()
|
||||
Frame.__init__(self, master, **kw)
|
||||
self.labels = {}
|
||||
|
||||
def set_label(self, name, text='', side=LEFT):
|
||||
if name not in self.labels:
|
||||
label = Label(self, bd=1, relief=SUNKEN, anchor=W)
|
||||
label.pack(side=side)
|
||||
self.labels[name] = label
|
||||
else:
|
||||
label = self.labels[name]
|
||||
label.config(text=text)
|
||||
|
||||
def _multistatus_bar(parent):
|
||||
root = Tk()
|
||||
width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
|
||||
root.geometry("+%d+%d" %(x, y + 150))
|
||||
root.title("Test multistatus bar")
|
||||
frame = Frame(root)
|
||||
text = Text(frame)
|
||||
text.pack()
|
||||
msb = MultiStatusBar(frame)
|
||||
msb.set_label("one", "hello")
|
||||
msb.set_label("two", "world")
|
||||
msb.pack(side=BOTTOM, fill=X)
|
||||
|
||||
def change():
|
||||
msb.set_label("one", "foo")
|
||||
msb.set_label("two", "bar")
|
||||
|
||||
button = Button(root, text="Update status", command=change)
|
||||
button.pack(side=BOTTOM)
|
||||
frame.pack()
|
||||
frame.mainloop()
|
||||
root.mainloop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
from idlelib.idle_test.htest import run
|
||||
run(_multistatus_bar)
|
||||
|
|
@ -1,144 +0,0 @@
|
|||
from tkinter import *
|
||||
from idlelib.EditorWindow import EditorWindow
|
||||
import re
|
||||
import tkinter.messagebox as tkMessageBox
|
||||
from idlelib import IOBinding
|
||||
|
||||
class OutputWindow(EditorWindow):
|
||||
|
||||
"""An editor window that can serve as an output file.
|
||||
|
||||
Also the future base class for the Python shell window.
|
||||
This class has no input facilities.
|
||||
"""
|
||||
|
||||
def __init__(self, *args):
|
||||
EditorWindow.__init__(self, *args)
|
||||
self.text.bind("<<goto-file-line>>", self.goto_file_line)
|
||||
|
||||
# Customize EditorWindow
|
||||
|
||||
def ispythonsource(self, filename):
|
||||
# No colorization needed
|
||||
return 0
|
||||
|
||||
def short_title(self):
|
||||
return "Output"
|
||||
|
||||
def maybesave(self):
|
||||
# Override base class method -- don't ask any questions
|
||||
if self.get_saved():
|
||||
return "yes"
|
||||
else:
|
||||
return "no"
|
||||
|
||||
# Act as output file
|
||||
|
||||
def write(self, s, tags=(), mark="insert"):
|
||||
if isinstance(s, (bytes, bytes)):
|
||||
s = s.decode(IOBinding.encoding, "replace")
|
||||
self.text.insert(mark, s, tags)
|
||||
self.text.see(mark)
|
||||
self.text.update()
|
||||
return len(s)
|
||||
|
||||
def writelines(self, lines):
|
||||
for line in lines:
|
||||
self.write(line)
|
||||
|
||||
def flush(self):
|
||||
pass
|
||||
|
||||
# Our own right-button menu
|
||||
|
||||
rmenu_specs = [
|
||||
("Cut", "<<cut>>", "rmenu_check_cut"),
|
||||
("Copy", "<<copy>>", "rmenu_check_copy"),
|
||||
("Paste", "<<paste>>", "rmenu_check_paste"),
|
||||
(None, None, None),
|
||||
("Go to file/line", "<<goto-file-line>>", None),
|
||||
]
|
||||
|
||||
file_line_pats = [
|
||||
# order of patterns matters
|
||||
r'file "([^"]*)", line (\d+)',
|
||||
r'([^\s]+)\((\d+)\)',
|
||||
r'^(\s*\S.*?):\s*(\d+):', # Win filename, maybe starting with spaces
|
||||
r'([^\s]+):\s*(\d+):', # filename or path, ltrim
|
||||
r'^\s*(\S.*?):\s*(\d+):', # Win abs path with embedded spaces, ltrim
|
||||
]
|
||||
|
||||
file_line_progs = None
|
||||
|
||||
def goto_file_line(self, event=None):
|
||||
if self.file_line_progs is None:
|
||||
l = []
|
||||
for pat in self.file_line_pats:
|
||||
l.append(re.compile(pat, re.IGNORECASE))
|
||||
self.file_line_progs = l
|
||||
# x, y = self.event.x, self.event.y
|
||||
# self.text.mark_set("insert", "@%d,%d" % (x, y))
|
||||
line = self.text.get("insert linestart", "insert lineend")
|
||||
result = self._file_line_helper(line)
|
||||
if not result:
|
||||
# Try the previous line. This is handy e.g. in tracebacks,
|
||||
# where you tend to right-click on the displayed source line
|
||||
line = self.text.get("insert -1line linestart",
|
||||
"insert -1line lineend")
|
||||
result = self._file_line_helper(line)
|
||||
if not result:
|
||||
tkMessageBox.showerror(
|
||||
"No special line",
|
||||
"The line you point at doesn't look like "
|
||||
"a valid file name followed by a line number.",
|
||||
master=self.text)
|
||||
return
|
||||
filename, lineno = result
|
||||
edit = self.flist.open(filename)
|
||||
edit.gotoline(lineno)
|
||||
|
||||
def _file_line_helper(self, line):
|
||||
for prog in self.file_line_progs:
|
||||
match = prog.search(line)
|
||||
if match:
|
||||
filename, lineno = match.group(1, 2)
|
||||
try:
|
||||
f = open(filename, "r")
|
||||
f.close()
|
||||
break
|
||||
except OSError:
|
||||
continue
|
||||
else:
|
||||
return None
|
||||
try:
|
||||
return filename, int(lineno)
|
||||
except TypeError:
|
||||
return None
|
||||
|
||||
# These classes are currently not used but might come in handy
|
||||
|
||||
class OnDemandOutputWindow:
|
||||
|
||||
tagdefs = {
|
||||
# XXX Should use IdlePrefs.ColorPrefs
|
||||
"stdout": {"foreground": "blue"},
|
||||
"stderr": {"foreground": "#007700"},
|
||||
}
|
||||
|
||||
def __init__(self, flist):
|
||||
self.flist = flist
|
||||
self.owin = None
|
||||
|
||||
def write(self, s, tags, mark):
|
||||
if not self.owin:
|
||||
self.setup()
|
||||
self.owin.write(s, tags, mark)
|
||||
|
||||
def setup(self):
|
||||
self.owin = owin = OutputWindow(self.flist)
|
||||
text = owin.text
|
||||
for tag, cnf in self.tagdefs.items():
|
||||
if cnf:
|
||||
text.tag_configure(tag, **cnf)
|
||||
text.tag_raise('sel')
|
||||
self.write = self.owin.write
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
IDLE is Python's Tkinter-based Integrated DeveLopment Environment.
|
||||
|
||||
IDLE emphasizes a lightweight, clean design with a simple user interface.
|
||||
Although it is suitable for beginners, even advanced users will find that
|
||||
IDLE has everything they really need to develop pure Python code.
|
||||
|
||||
IDLE features a multi-window text editor with multiple undo, Python colorizing,
|
||||
and many other capabilities, e.g. smart indent, call tips, and autocompletion.
|
||||
|
||||
The editor has comprehensive search functions, including searching through
|
||||
multiple files. Class browsers and path browsers provide fast access to
|
||||
code objects from a top level viewpoint without dealing with code folding.
|
||||
|
||||
There is a Python Shell window which features colorizing and command recall.
|
||||
|
||||
IDLE executes Python code in a separate process, which is restarted for each
|
||||
Run (F5) initiated from an editor window. The environment can also be
|
||||
restarted from the Shell window without restarting IDLE.
|
||||
|
||||
This enhancement has often been requested, and is now finally available. The
|
||||
magic "reload/import *" incantations are no longer required when editing and
|
||||
testing a module two or three steps down the import chain.
|
||||
|
||||
(Personal firewall software may warn about the connection IDLE makes to its
|
||||
subprocess using this computer's internal loopback interface. This connection
|
||||
is not visible on any external interface and no data is sent to or received
|
||||
from the Internet.)
|
||||
|
||||
It is possible to interrupt tightly looping user code, even on Windows.
|
||||
|
||||
Applications which cannot support subprocesses and/or sockets can still run
|
||||
IDLE in a single process.
|
||||
|
||||
IDLE has an integrated debugger with stepping, persistent breakpoints, and call
|
||||
stack visibility.
|
||||
|
||||
There is a GUI configuration manager which makes it easy to select fonts,
|
||||
colors, keybindings, and startup options. This facility includes a feature
|
||||
which allows the user to specify additional help sources, either locally or on
|
||||
the web.
|
||||
|
||||
IDLE is coded in 100% pure Python, using the Tkinter GUI toolkit (Tk/Tcl)
|
||||
and is cross-platform, working on Unix, Mac, and Windows.
|
||||
|
||||
IDLE accepts command line arguments. Try idle -h to see the options.
|
||||
|
||||
|
||||
If you find bugs or have suggestions or patches, let us know about
|
||||
them by using the Python issue tracker:
|
||||
|
||||
http://bugs.python.org
|
||||
|
||||
For further details and links, read the Help files and check the IDLE home
|
||||
page at
|
||||
|
||||
http://www.python.org/idle/
|
||||
|
||||
There is a mail list for IDLE: idle-dev@python.org. You can join at
|
||||
|
||||
http://mail.python.org/mailman/listinfo/idle-dev
|
||||
|
|
@ -1,97 +0,0 @@
|
|||
# general purpose 'tooltip' routines - currently unused in idlefork
|
||||
# (although the 'calltips' extension is partly based on this code)
|
||||
# may be useful for some purposes in (or almost in ;) the current project scope
|
||||
# Ideas gleaned from PySol
|
||||
|
||||
from tkinter import *
|
||||
|
||||
class ToolTipBase:
|
||||
|
||||
def __init__(self, button):
|
||||
self.button = button
|
||||
self.tipwindow = None
|
||||
self.id = None
|
||||
self.x = self.y = 0
|
||||
self._id1 = self.button.bind("<Enter>", self.enter)
|
||||
self._id2 = self.button.bind("<Leave>", self.leave)
|
||||
self._id3 = self.button.bind("<ButtonPress>", self.leave)
|
||||
|
||||
def enter(self, event=None):
|
||||
self.schedule()
|
||||
|
||||
def leave(self, event=None):
|
||||
self.unschedule()
|
||||
self.hidetip()
|
||||
|
||||
def schedule(self):
|
||||
self.unschedule()
|
||||
self.id = self.button.after(1500, self.showtip)
|
||||
|
||||
def unschedule(self):
|
||||
id = self.id
|
||||
self.id = None
|
||||
if id:
|
||||
self.button.after_cancel(id)
|
||||
|
||||
def showtip(self):
|
||||
if self.tipwindow:
|
||||
return
|
||||
# The tip window must be completely outside the button;
|
||||
# otherwise when the mouse enters the tip window we get
|
||||
# a leave event and it disappears, and then we get an enter
|
||||
# event and it reappears, and so on forever :-(
|
||||
x = self.button.winfo_rootx() + 20
|
||||
y = self.button.winfo_rooty() + self.button.winfo_height() + 1
|
||||
self.tipwindow = tw = Toplevel(self.button)
|
||||
tw.wm_overrideredirect(1)
|
||||
tw.wm_geometry("+%d+%d" % (x, y))
|
||||
self.showcontents()
|
||||
|
||||
def showcontents(self, text="Your text here"):
|
||||
# Override this in derived class
|
||||
label = Label(self.tipwindow, text=text, justify=LEFT,
|
||||
background="#ffffe0", relief=SOLID, borderwidth=1)
|
||||
label.pack()
|
||||
|
||||
def hidetip(self):
|
||||
tw = self.tipwindow
|
||||
self.tipwindow = None
|
||||
if tw:
|
||||
tw.destroy()
|
||||
|
||||
class ToolTip(ToolTipBase):
|
||||
def __init__(self, button, text):
|
||||
ToolTipBase.__init__(self, button)
|
||||
self.text = text
|
||||
def showcontents(self):
|
||||
ToolTipBase.showcontents(self, self.text)
|
||||
|
||||
class ListboxToolTip(ToolTipBase):
|
||||
def __init__(self, button, items):
|
||||
ToolTipBase.__init__(self, button)
|
||||
self.items = items
|
||||
def showcontents(self):
|
||||
listbox = Listbox(self.tipwindow, background="#ffffe0")
|
||||
listbox.pack()
|
||||
for item in self.items:
|
||||
listbox.insert(END, item)
|
||||
|
||||
def _tooltip(parent):
|
||||
root = Tk()
|
||||
root.title("Test tooltip")
|
||||
width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
|
||||
root.geometry("+%d+%d"%(x, y + 150))
|
||||
label = Label(root, text="Place your mouse over buttons")
|
||||
label.pack()
|
||||
button1 = Button(root, text="Button 1")
|
||||
button2 = Button(root, text="Button 2")
|
||||
button1.pack()
|
||||
button2.pack()
|
||||
ToolTip(button1, "This is tooltip text for button1.")
|
||||
ListboxToolTip(button2, ["This is","multiple line",
|
||||
"tooltip text","for button2"])
|
||||
root.mainloop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
from idlelib.idle_test.htest import run
|
||||
run(_tooltip)
|
||||
|
|
@ -1 +0,0 @@
|
|||
# Dummy file to make this a package.
|
||||
|
|
@ -1,146 +0,0 @@
|
|||
"""About Dialog for IDLE
|
||||
|
||||
"""
|
||||
|
||||
import os
|
||||
from sys import version
|
||||
from tkinter import *
|
||||
from idlelib import textView
|
||||
|
||||
class AboutDialog(Toplevel):
|
||||
"""Modal about dialog for idle
|
||||
|
||||
"""
|
||||
def __init__(self, parent, title, _htest=False):
|
||||
"""
|
||||
_htest - bool, change box location when running htest
|
||||
"""
|
||||
Toplevel.__init__(self, parent)
|
||||
self.configure(borderwidth=5)
|
||||
# place dialog below parent if running htest
|
||||
self.geometry("+%d+%d" % (
|
||||
parent.winfo_rootx()+30,
|
||||
parent.winfo_rooty()+(30 if not _htest else 100)))
|
||||
self.bg = "#707070"
|
||||
self.fg = "#ffffff"
|
||||
self.CreateWidgets()
|
||||
self.resizable(height=FALSE, width=FALSE)
|
||||
self.title(title)
|
||||
self.transient(parent)
|
||||
self.grab_set()
|
||||
self.protocol("WM_DELETE_WINDOW", self.Ok)
|
||||
self.parent = parent
|
||||
self.buttonOk.focus_set()
|
||||
self.bind('<Return>',self.Ok) #dismiss dialog
|
||||
self.bind('<Escape>',self.Ok) #dismiss dialog
|
||||
self.wait_window()
|
||||
|
||||
def CreateWidgets(self):
|
||||
release = version[:version.index(' ')]
|
||||
frameMain = Frame(self, borderwidth=2, relief=SUNKEN)
|
||||
frameButtons = Frame(self)
|
||||
frameButtons.pack(side=BOTTOM, fill=X)
|
||||
frameMain.pack(side=TOP, expand=TRUE, fill=BOTH)
|
||||
self.buttonOk = Button(frameButtons, text='Close',
|
||||
command=self.Ok)
|
||||
self.buttonOk.pack(padx=5, pady=5)
|
||||
#self.picture = Image('photo', data=self.pictureData)
|
||||
frameBg = Frame(frameMain, bg=self.bg)
|
||||
frameBg.pack(expand=TRUE, fill=BOTH)
|
||||
labelTitle = Label(frameBg, text='IDLE', fg=self.fg, bg=self.bg,
|
||||
font=('courier', 24, 'bold'))
|
||||
labelTitle.grid(row=0, column=0, sticky=W, padx=10, pady=10)
|
||||
#labelPicture = Label(frameBg, text='[picture]')
|
||||
#image=self.picture, bg=self.bg)
|
||||
#labelPicture.grid(row=1, column=1, sticky=W, rowspan=2,
|
||||
# padx=0, pady=3)
|
||||
byline = "Python's Integrated DeveLopment Environment" + 5*'\n'
|
||||
labelDesc = Label(frameBg, text=byline, justify=LEFT,
|
||||
fg=self.fg, bg=self.bg)
|
||||
labelDesc.grid(row=2, column=0, sticky=W, columnspan=3, padx=10, pady=5)
|
||||
labelEmail = Label(frameBg, text='email: idle-dev@python.org',
|
||||
justify=LEFT, fg=self.fg, bg=self.bg)
|
||||
labelEmail.grid(row=6, column=0, columnspan=2,
|
||||
sticky=W, padx=10, pady=0)
|
||||
labelWWW = Label(frameBg, text='https://docs.python.org/' +
|
||||
version[:3] + '/library/idle.html',
|
||||
justify=LEFT, fg=self.fg, bg=self.bg)
|
||||
labelWWW.grid(row=7, column=0, columnspan=2, sticky=W, padx=10, pady=0)
|
||||
Frame(frameBg, borderwidth=1, relief=SUNKEN,
|
||||
height=2, bg=self.bg).grid(row=8, column=0, sticky=EW,
|
||||
columnspan=3, padx=5, pady=5)
|
||||
labelPythonVer = Label(frameBg, text='Python version: ' +
|
||||
release, fg=self.fg, bg=self.bg)
|
||||
labelPythonVer.grid(row=9, column=0, sticky=W, padx=10, pady=0)
|
||||
tkVer = self.tk.call('info', 'patchlevel')
|
||||
labelTkVer = Label(frameBg, text='Tk version: '+
|
||||
tkVer, fg=self.fg, bg=self.bg)
|
||||
labelTkVer.grid(row=9, column=1, sticky=W, padx=2, pady=0)
|
||||
py_button_f = Frame(frameBg, bg=self.bg)
|
||||
py_button_f.grid(row=10, column=0, columnspan=2, sticky=NSEW)
|
||||
buttonLicense = Button(py_button_f, text='License', width=8,
|
||||
highlightbackground=self.bg,
|
||||
command=self.ShowLicense)
|
||||
buttonLicense.pack(side=LEFT, padx=10, pady=10)
|
||||
buttonCopyright = Button(py_button_f, text='Copyright', width=8,
|
||||
highlightbackground=self.bg,
|
||||
command=self.ShowCopyright)
|
||||
buttonCopyright.pack(side=LEFT, padx=10, pady=10)
|
||||
buttonCredits = Button(py_button_f, text='Credits', width=8,
|
||||
highlightbackground=self.bg,
|
||||
command=self.ShowPythonCredits)
|
||||
buttonCredits.pack(side=LEFT, padx=10, pady=10)
|
||||
Frame(frameBg, borderwidth=1, relief=SUNKEN,
|
||||
height=2, bg=self.bg).grid(row=11, column=0, sticky=EW,
|
||||
columnspan=3, padx=5, pady=5)
|
||||
idle_v = Label(frameBg, text='IDLE version: ' + release,
|
||||
fg=self.fg, bg=self.bg)
|
||||
idle_v.grid(row=12, column=0, sticky=W, padx=10, pady=0)
|
||||
idle_button_f = Frame(frameBg, bg=self.bg)
|
||||
idle_button_f.grid(row=13, column=0, columnspan=3, sticky=NSEW)
|
||||
idle_about_b = Button(idle_button_f, text='README', width=8,
|
||||
highlightbackground=self.bg,
|
||||
command=self.ShowIDLEAbout)
|
||||
idle_about_b.pack(side=LEFT, padx=10, pady=10)
|
||||
idle_news_b = Button(idle_button_f, text='NEWS', width=8,
|
||||
highlightbackground=self.bg,
|
||||
command=self.ShowIDLENEWS)
|
||||
idle_news_b.pack(side=LEFT, padx=10, pady=10)
|
||||
idle_credits_b = Button(idle_button_f, text='Credits', width=8,
|
||||
highlightbackground=self.bg,
|
||||
command=self.ShowIDLECredits)
|
||||
idle_credits_b.pack(side=LEFT, padx=10, pady=10)
|
||||
|
||||
def ShowLicense(self):
|
||||
self.display_printer_text('About - License', license)
|
||||
|
||||
def ShowCopyright(self):
|
||||
self.display_printer_text('About - Copyright', copyright)
|
||||
|
||||
def ShowPythonCredits(self):
|
||||
self.display_printer_text('About - Python Credits', credits)
|
||||
|
||||
def ShowIDLECredits(self):
|
||||
self.display_file_text('About - Credits', 'CREDITS.txt', 'iso-8859-1')
|
||||
|
||||
def ShowIDLEAbout(self):
|
||||
self.display_file_text('About - Readme', 'README.txt')
|
||||
|
||||
def ShowIDLENEWS(self):
|
||||
self.display_file_text('About - NEWS', 'NEWS.txt')
|
||||
|
||||
def display_printer_text(self, title, printer):
|
||||
printer._Printer__setup()
|
||||
text = '\n'.join(printer._Printer__lines)
|
||||
textView.view_text(self, title, text)
|
||||
|
||||
def display_file_text(self, title, filename, encoding=None):
|
||||
fn = os.path.join(os.path.abspath(os.path.dirname(__file__)), filename)
|
||||
textView.view_file(self, title, fn, encoding)
|
||||
|
||||
def Ok(self, event=None):
|
||||
self.destroy()
|
||||
|
||||
if __name__ == '__main__':
|
||||
from idlelib.idle_test.htest import run
|
||||
run(AboutDialog)
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
# IDLE reads several config files to determine user preferences. This
|
||||
# file is the default config file for general idle settings.
|
||||
#
|
||||
# When IDLE starts, it will look in
|
||||
# the following two sets of files, in order:
|
||||
#
|
||||
# default configuration
|
||||
# ---------------------
|
||||
# config-main.def the default general config file
|
||||
# config-extensions.def the default extension config file
|
||||
# config-highlight.def the default highlighting config file
|
||||
# config-keys.def the default keybinding config file
|
||||
#
|
||||
# user configuration
|
||||
# -------------------
|
||||
# ~/.idlerc/config-main.cfg the user general config file
|
||||
# ~/.idlerc/config-extensions.cfg the user extension config file
|
||||
# ~/.idlerc/config-highlight.cfg the user highlighting config file
|
||||
# ~/.idlerc/config-keys.cfg the user keybinding config file
|
||||
#
|
||||
# On Windows2000 and Windows XP the .idlerc directory is at
|
||||
# Documents and Settings\<username>\.idlerc
|
||||
#
|
||||
# On Windows98 it is at c:\.idlerc
|
||||
#
|
||||
# Any options the user saves through the config dialog will be saved to
|
||||
# the relevant user config file. Reverting any general setting to the
|
||||
# default causes that entry to be wiped from the user file and re-read
|
||||
# from the default file. User highlighting themes or keybinding sets are
|
||||
# retained unless specifically deleted within the config dialog. Choosing
|
||||
# one of the default themes or keysets just applies the relevant settings
|
||||
# from the default file.
|
||||
#
|
||||
# Additional help sources are listed in the [HelpFiles] section and must be
|
||||
# viewable by a web browser (or the Windows Help viewer in the case of .chm
|
||||
# files). These sources will be listed on the Help menu. The pattern is
|
||||
# <sequence_number = menu item;/path/to/help/source>
|
||||
# You can't use a semi-colon in a menu item or path. The path will be platform
|
||||
# specific because of path separators, drive specs etc.
|
||||
#
|
||||
# It is best to use the Configuration GUI to set up additional help sources!
|
||||
# Example:
|
||||
#1 = My Extra Help Source;/usr/share/doc/foo/index.html
|
||||
#2 = Another Help Source;/path/to/another.pdf
|
||||
|
||||
[General]
|
||||
editor-on-startup= 0
|
||||
autosave= 0
|
||||
print-command-posix=lpr %%s
|
||||
print-command-win=start /min notepad /p %%s
|
||||
delete-exitfunc= 1
|
||||
|
||||
[EditorWindow]
|
||||
width= 80
|
||||
height= 40
|
||||
font= TkFixedFont
|
||||
font-size= 10
|
||||
font-bold= 0
|
||||
encoding= none
|
||||
|
||||
[Indent]
|
||||
use-spaces= 1
|
||||
num-spaces= 4
|
||||
|
||||
[Theme]
|
||||
default= 1
|
||||
name= IDLE Classic
|
||||
|
||||
[Keys]
|
||||
default= 1
|
||||
name= IDLE Classic OSX
|
||||
|
||||
[History]
|
||||
cyclic=1
|
||||
|
||||
[HelpFiles]
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,166 +0,0 @@
|
|||
"Dialog to specify or edit the parameters for a user configured help source."
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
from tkinter import *
|
||||
import tkinter.messagebox as tkMessageBox
|
||||
import tkinter.filedialog as tkFileDialog
|
||||
|
||||
class GetHelpSourceDialog(Toplevel):
|
||||
def __init__(self, parent, title, menuItem='', filePath='', _htest=False):
|
||||
"""Get menu entry and url/ local file location for Additional Help
|
||||
|
||||
User selects a name for the Help resource and provides a web url
|
||||
or a local file as its source. The user can enter a url or browse
|
||||
for the file.
|
||||
|
||||
_htest - bool, change box location when running htest
|
||||
"""
|
||||
Toplevel.__init__(self, parent)
|
||||
self.configure(borderwidth=5)
|
||||
self.resizable(height=FALSE, width=FALSE)
|
||||
self.title(title)
|
||||
self.transient(parent)
|
||||
self.grab_set()
|
||||
self.protocol("WM_DELETE_WINDOW", self.Cancel)
|
||||
self.parent = parent
|
||||
self.result = None
|
||||
self.CreateWidgets()
|
||||
self.menu.set(menuItem)
|
||||
self.path.set(filePath)
|
||||
self.withdraw() #hide while setting geometry
|
||||
#needs to be done here so that the winfo_reqwidth is valid
|
||||
self.update_idletasks()
|
||||
#centre dialog over parent. below parent if running htest.
|
||||
self.geometry(
|
||||
"+%d+%d" % (
|
||||
parent.winfo_rootx() +
|
||||
(parent.winfo_width()/2 - self.winfo_reqwidth()/2),
|
||||
parent.winfo_rooty() +
|
||||
((parent.winfo_height()/2 - self.winfo_reqheight()/2)
|
||||
if not _htest else 150)))
|
||||
self.deiconify() #geometry set, unhide
|
||||
self.bind('<Return>', self.Ok)
|
||||
self.wait_window()
|
||||
|
||||
def CreateWidgets(self):
|
||||
self.menu = StringVar(self)
|
||||
self.path = StringVar(self)
|
||||
self.fontSize = StringVar(self)
|
||||
self.frameMain = Frame(self, borderwidth=2, relief=GROOVE)
|
||||
self.frameMain.pack(side=TOP, expand=TRUE, fill=BOTH)
|
||||
labelMenu = Label(self.frameMain, anchor=W, justify=LEFT,
|
||||
text='Menu Item:')
|
||||
self.entryMenu = Entry(self.frameMain, textvariable=self.menu,
|
||||
width=30)
|
||||
self.entryMenu.focus_set()
|
||||
labelPath = Label(self.frameMain, anchor=W, justify=LEFT,
|
||||
text='Help File Path: Enter URL or browse for file')
|
||||
self.entryPath = Entry(self.frameMain, textvariable=self.path,
|
||||
width=40)
|
||||
self.entryMenu.focus_set()
|
||||
labelMenu.pack(anchor=W, padx=5, pady=3)
|
||||
self.entryMenu.pack(anchor=W, padx=5, pady=3)
|
||||
labelPath.pack(anchor=W, padx=5, pady=3)
|
||||
self.entryPath.pack(anchor=W, padx=5, pady=3)
|
||||
browseButton = Button(self.frameMain, text='Browse', width=8,
|
||||
command=self.browseFile)
|
||||
browseButton.pack(pady=3)
|
||||
frameButtons = Frame(self)
|
||||
frameButtons.pack(side=BOTTOM, fill=X)
|
||||
self.buttonOk = Button(frameButtons, text='OK',
|
||||
width=8, default=ACTIVE, command=self.Ok)
|
||||
self.buttonOk.grid(row=0, column=0, padx=5,pady=5)
|
||||
self.buttonCancel = Button(frameButtons, text='Cancel',
|
||||
width=8, command=self.Cancel)
|
||||
self.buttonCancel.grid(row=0, column=1, padx=5, pady=5)
|
||||
|
||||
def browseFile(self):
|
||||
filetypes = [
|
||||
("HTML Files", "*.htm *.html", "TEXT"),
|
||||
("PDF Files", "*.pdf", "TEXT"),
|
||||
("Windows Help Files", "*.chm"),
|
||||
("Text Files", "*.txt", "TEXT"),
|
||||
("All Files", "*")]
|
||||
path = self.path.get()
|
||||
if path:
|
||||
dir, base = os.path.split(path)
|
||||
else:
|
||||
base = None
|
||||
if sys.platform[:3] == 'win':
|
||||
dir = os.path.join(os.path.dirname(sys.executable), 'Doc')
|
||||
if not os.path.isdir(dir):
|
||||
dir = os.getcwd()
|
||||
else:
|
||||
dir = os.getcwd()
|
||||
opendialog = tkFileDialog.Open(parent=self, filetypes=filetypes)
|
||||
file = opendialog.show(initialdir=dir, initialfile=base)
|
||||
if file:
|
||||
self.path.set(file)
|
||||
|
||||
def MenuOk(self):
|
||||
"Simple validity check for a sensible menu item name"
|
||||
menuOk = True
|
||||
menu = self.menu.get()
|
||||
menu.strip()
|
||||
if not menu:
|
||||
tkMessageBox.showerror(title='Menu Item Error',
|
||||
message='No menu item specified',
|
||||
parent=self)
|
||||
self.entryMenu.focus_set()
|
||||
menuOk = False
|
||||
elif len(menu) > 30:
|
||||
tkMessageBox.showerror(title='Menu Item Error',
|
||||
message='Menu item too long:'
|
||||
'\nLimit 30 characters.',
|
||||
parent=self)
|
||||
self.entryMenu.focus_set()
|
||||
menuOk = False
|
||||
return menuOk
|
||||
|
||||
def PathOk(self):
|
||||
"Simple validity check for menu file path"
|
||||
pathOk = True
|
||||
path = self.path.get()
|
||||
path.strip()
|
||||
if not path: #no path specified
|
||||
tkMessageBox.showerror(title='File Path Error',
|
||||
message='No help file path specified.',
|
||||
parent=self)
|
||||
self.entryPath.focus_set()
|
||||
pathOk = False
|
||||
elif path.startswith(('www.', 'http')):
|
||||
pass
|
||||
else:
|
||||
if path[:5] == 'file:':
|
||||
path = path[5:]
|
||||
if not os.path.exists(path):
|
||||
tkMessageBox.showerror(title='File Path Error',
|
||||
message='Help file path does not exist.',
|
||||
parent=self)
|
||||
self.entryPath.focus_set()
|
||||
pathOk = False
|
||||
return pathOk
|
||||
|
||||
def Ok(self, event=None):
|
||||
if self.MenuOk() and self.PathOk():
|
||||
self.result = (self.menu.get().strip(),
|
||||
self.path.get().strip())
|
||||
if sys.platform == 'darwin':
|
||||
path = self.result[1]
|
||||
if path.startswith(('www', 'file:', 'http:')):
|
||||
pass
|
||||
else:
|
||||
# Mac Safari insists on using the URI form for local files
|
||||
self.result = list(self.result)
|
||||
self.result[1] = "file://" + path
|
||||
self.destroy()
|
||||
|
||||
def Cancel(self, event=None):
|
||||
self.result = None
|
||||
self.destroy()
|
||||
|
||||
if __name__ == '__main__':
|
||||
from idlelib.idle_test.htest import run
|
||||
run(GetHelpSourceDialog)
|
||||
|
|
@ -1,98 +0,0 @@
|
|||
"""
|
||||
Dialog that allows user to specify a new config file section name.
|
||||
Used to get new highlight theme and keybinding set names.
|
||||
The 'return value' for the dialog, used two placed in configDialog.py,
|
||||
is the .result attribute set in the Ok and Cancel methods.
|
||||
"""
|
||||
from tkinter import *
|
||||
import tkinter.messagebox as tkMessageBox
|
||||
|
||||
class GetCfgSectionNameDialog(Toplevel):
|
||||
def __init__(self, parent, title, message, used_names, _htest=False):
|
||||
"""
|
||||
message - string, informational message to display
|
||||
used_names - string collection, names already in use for validity check
|
||||
_htest - bool, change box location when running htest
|
||||
"""
|
||||
Toplevel.__init__(self, parent)
|
||||
self.configure(borderwidth=5)
|
||||
self.resizable(height=FALSE, width=FALSE)
|
||||
self.title(title)
|
||||
self.transient(parent)
|
||||
self.grab_set()
|
||||
self.protocol("WM_DELETE_WINDOW", self.Cancel)
|
||||
self.parent = parent
|
||||
self.message = message
|
||||
self.used_names = used_names
|
||||
self.create_widgets()
|
||||
self.withdraw() #hide while setting geometry
|
||||
self.update_idletasks()
|
||||
#needs to be done here so that the winfo_reqwidth is valid
|
||||
self.messageInfo.config(width=self.frameMain.winfo_reqwidth())
|
||||
self.geometry(
|
||||
"+%d+%d" % (
|
||||
parent.winfo_rootx() +
|
||||
(parent.winfo_width()/2 - self.winfo_reqwidth()/2),
|
||||
parent.winfo_rooty() +
|
||||
((parent.winfo_height()/2 - self.winfo_reqheight()/2)
|
||||
if not _htest else 100)
|
||||
) ) #centre dialog over parent (or below htest box)
|
||||
self.deiconify() #geometry set, unhide
|
||||
self.wait_window()
|
||||
|
||||
def create_widgets(self):
|
||||
self.name = StringVar(self.parent)
|
||||
self.fontSize = StringVar(self.parent)
|
||||
self.frameMain = Frame(self, borderwidth=2, relief=SUNKEN)
|
||||
self.frameMain.pack(side=TOP, expand=TRUE, fill=BOTH)
|
||||
self.messageInfo = Message(self.frameMain, anchor=W, justify=LEFT,
|
||||
padx=5, pady=5, text=self.message) #,aspect=200)
|
||||
entryName = Entry(self.frameMain, textvariable=self.name, width=30)
|
||||
entryName.focus_set()
|
||||
self.messageInfo.pack(padx=5, pady=5) #, expand=TRUE, fill=BOTH)
|
||||
entryName.pack(padx=5, pady=5)
|
||||
|
||||
frameButtons = Frame(self, pady=2)
|
||||
frameButtons.pack(side=BOTTOM)
|
||||
self.buttonOk = Button(frameButtons, text='Ok',
|
||||
width=8, command=self.Ok)
|
||||
self.buttonOk.pack(side=LEFT, padx=5)
|
||||
self.buttonCancel = Button(frameButtons, text='Cancel',
|
||||
width=8, command=self.Cancel)
|
||||
self.buttonCancel.pack(side=RIGHT, padx=5)
|
||||
|
||||
def name_ok(self):
|
||||
''' After stripping entered name, check that it is a sensible
|
||||
ConfigParser file section name. Return it if it is, '' if not.
|
||||
'''
|
||||
name = self.name.get().strip()
|
||||
if not name: #no name specified
|
||||
tkMessageBox.showerror(title='Name Error',
|
||||
message='No name specified.', parent=self)
|
||||
elif len(name)>30: #name too long
|
||||
tkMessageBox.showerror(title='Name Error',
|
||||
message='Name too long. It should be no more than '+
|
||||
'30 characters.', parent=self)
|
||||
name = ''
|
||||
elif name in self.used_names:
|
||||
tkMessageBox.showerror(title='Name Error',
|
||||
message='This name is already in use.', parent=self)
|
||||
name = ''
|
||||
return name
|
||||
|
||||
def Ok(self, event=None):
|
||||
name = self.name_ok()
|
||||
if name:
|
||||
self.result = name
|
||||
self.destroy()
|
||||
|
||||
def Cancel(self, event=None):
|
||||
self.result = ''
|
||||
self.destroy()
|
||||
|
||||
if __name__ == '__main__':
|
||||
import unittest
|
||||
unittest.main('idlelib.idle_test.test_config_name', verbosity=2, exit=False)
|
||||
|
||||
from idlelib.idle_test.htest import run
|
||||
run(GetCfgSectionNameDialog)
|
||||
|
|
@ -1,368 +0,0 @@
|
|||
[See the end of this file for ** TIPS ** on using IDLE !!]
|
||||
|
||||
IDLE is the Python IDE built with the tkinter GUI toolkit.
|
||||
|
||||
IDLE has the following features:
|
||||
-coded in 100% pure Python, using the tkinter GUI toolkit
|
||||
-cross-platform: works on Windows, Unix, and OS X
|
||||
-multi-window text editor with multiple undo, Python colorizing, smart indent,
|
||||
call tips, and many other features
|
||||
-Python shell window (a.k.a interactive interpreter)
|
||||
-debugger (not complete, but you can set breakpoints, view and step)
|
||||
|
||||
Menus:
|
||||
|
||||
IDLE has two window types the Shell window and the Editor window. It is
|
||||
possible to have multiple editor windows simultaneously. IDLE's
|
||||
menus dynamically change based on which window is currently selected. Each menu
|
||||
documented below indicates which window type it is associated with.
|
||||
|
||||
File Menu (Shell and Editor):
|
||||
|
||||
New File -- Create a new file editing window
|
||||
Open... -- Open an existing file
|
||||
Open Module... -- Open an existing module (searches sys.path)
|
||||
Recent Files... -- Open a list of recent files
|
||||
Class Browser -- Show classes and methods in current file
|
||||
Path Browser -- Show sys.path directories, modules, classes,
|
||||
and methods
|
||||
---
|
||||
Save -- Save current window to the associated file (unsaved
|
||||
windows have a * before and after the window title)
|
||||
|
||||
Save As... -- Save current window to new file, which becomes
|
||||
the associated file
|
||||
Save Copy As... -- Save current window to different file
|
||||
without changing the associated file
|
||||
---
|
||||
Print Window -- Print the current window
|
||||
---
|
||||
Close -- Close current window (asks to save if unsaved)
|
||||
Exit -- Close all windows, quit (asks to save if unsaved)
|
||||
|
||||
Edit Menu (Shell and Editor):
|
||||
|
||||
Undo -- Undo last change to current window
|
||||
(a maximum of 1000 changes may be undone)
|
||||
Redo -- Redo last undone change to current window
|
||||
---
|
||||
Cut -- Copy a selection into system-wide clipboard,
|
||||
then delete the selection
|
||||
Copy -- Copy selection into system-wide clipboard
|
||||
Paste -- Insert system-wide clipboard into window
|
||||
Select All -- Select the entire contents of the edit buffer
|
||||
---
|
||||
Find... -- Open a search dialog box with many options
|
||||
Find Again -- Repeat last search
|
||||
Find Selection -- Search for the string in the selection
|
||||
Find in Files... -- Open a search dialog box for searching files
|
||||
Replace... -- Open a search-and-replace dialog box
|
||||
Go to Line -- Ask for a line number and show that line
|
||||
Expand Word -- Expand the word you have typed to match another
|
||||
word in the same buffer; repeat to get a
|
||||
different expansion
|
||||
Show Calltip -- After an unclosed parenthesis for a function, open
|
||||
a small window with function parameter hints
|
||||
Show Parens -- Highlight the surrounding parenthesis
|
||||
Show Completions -- Open a scroll window allowing selection keywords
|
||||
and attributes. (see '*TIPS*', below)
|
||||
|
||||
Format Menu (Editor window only):
|
||||
|
||||
Indent Region -- Shift selected lines right by the indent width
|
||||
(default 4 spaces)
|
||||
Dedent Region -- Shift selected lines left by the indent width
|
||||
(default 4 spaces)
|
||||
Comment Out Region -- Insert ## in front of selected lines
|
||||
Uncomment Region -- Remove leading # or ## from selected lines
|
||||
Tabify Region -- Turns *leading* stretches of spaces into tabs.
|
||||
(Note: We recommend using 4 space blocks to indent Python code.)
|
||||
Untabify Region -- Turn *all* tabs into the corrent number of spaces
|
||||
Toggle tabs -- Open a dialog to switch between indenting with
|
||||
spaces and tabs.
|
||||
New Indent Width... -- Open a dialog to change indent width. The
|
||||
accepted default by the Python community is 4
|
||||
spaces.
|
||||
Format Paragraph -- Reformat the current blank-line-separated
|
||||
paragraph. All lines in the paragraph will be
|
||||
formatted to less than 80 columns.
|
||||
---
|
||||
Strip trailing whitespace -- Removed any space characters after the end
|
||||
of the last non-space character
|
||||
|
||||
Run Menu (Editor window only):
|
||||
|
||||
Python Shell -- Open or wake up the Python shell window
|
||||
---
|
||||
Check Module -- Check the syntax of the module currently open in the
|
||||
Editor window. If the module has not been saved IDLE
|
||||
will prompt the user to save the code.
|
||||
Run Module -- Restart the shell to clean the environment, then
|
||||
execute the currently open module. If the module has
|
||||
not been saved IDLE will prompt the user to save the
|
||||
code.
|
||||
|
||||
Shell Menu (Shell window only):
|
||||
|
||||
View Last Restart -- Scroll the shell window to the last Shell restart
|
||||
Restart Shell -- Restart the shell to clean the environment
|
||||
|
||||
Debug Menu (Shell window only):
|
||||
|
||||
Go to File/Line -- Look around the insert point for a filename
|
||||
and line number, open the file, and show the line.
|
||||
Useful to view the source lines referenced in an
|
||||
exception traceback. Available in the context
|
||||
menu of the Shell window.
|
||||
Debugger (toggle) -- This feature is not complete and considered
|
||||
experimental. Run commands in the shell under the
|
||||
debugger.
|
||||
Stack Viewer -- Show the stack traceback of the last exception
|
||||
Auto-open Stack Viewer (toggle) -- Toggle automatically opening the
|
||||
stack viewer on unhandled
|
||||
exception
|
||||
|
||||
Options Menu (Shell and Editor):
|
||||
|
||||
Configure IDLE -- Open a configuration dialog. Fonts, indentation,
|
||||
keybindings, and color themes may be altered.
|
||||
Startup Preferences may be set, and additional Help
|
||||
sources can be specified. On OS X, open the
|
||||
configuration dialog by selecting Preferences
|
||||
in the application menu.
|
||||
|
||||
---
|
||||
Code Context (toggle) -- Open a pane at the top of the edit window
|
||||
which shows the block context of the section
|
||||
of code which is scrolling off the top or the
|
||||
window. This is not present in the Shell
|
||||
window only the Editor window.
|
||||
|
||||
Window Menu (Shell and Editor):
|
||||
|
||||
Zoom Height -- Toggles the window between normal size (40x80 initial
|
||||
setting) and maximum height. The initial size is in the Configure
|
||||
IDLE dialog under the general tab.
|
||||
---
|
||||
The rest of this menu lists the names of all open windows;
|
||||
select one to bring it to the foreground (deiconifying it if
|
||||
necessary).
|
||||
|
||||
Help Menu:
|
||||
|
||||
About IDLE -- Version, copyright, license, credits
|
||||
---
|
||||
IDLE Help -- Display this file which is a help file for IDLE
|
||||
detailing the menu options, basic editing and navigation,
|
||||
and other tips.
|
||||
Python Docs -- Access local Python documentation, if
|
||||
installed. Or will start a web browser and open
|
||||
docs.python.org showing the latest Python documentation.
|
||||
---
|
||||
Additional help sources may be added here with the Configure IDLE
|
||||
dialog under the General tab.
|
||||
|
||||
Editor context menu (Right-click / Control-click on OS X in Edit window):
|
||||
|
||||
Cut -- Copy a selection into system-wide clipboard,
|
||||
then delete the selection
|
||||
Copy -- Copy selection into system-wide clipboard
|
||||
Paste -- Insert system-wide clipboard into window
|
||||
Set Breakpoint -- Sets a breakpoint. Breakpoints are only enabled
|
||||
when the debugger is open.
|
||||
Clear Breakpoint -- Clears the breakpoint on that line
|
||||
|
||||
Shell context menu (Right-click / Control-click on OS X in Shell window):
|
||||
|
||||
Cut -- Copy a selection into system-wide clipboard,
|
||||
then delete the selection
|
||||
Copy -- Copy selection into system-wide clipboard
|
||||
Paste -- Insert system-wide clipboard into window
|
||||
---
|
||||
Go to file/line -- Same as in Debug menu
|
||||
|
||||
|
||||
** TIPS **
|
||||
==========
|
||||
|
||||
Additional Help Sources:
|
||||
|
||||
Windows users can Google on zopeshelf.chm to access Zope help files in
|
||||
the Windows help format. The Additional Help Sources feature of the
|
||||
configuration GUI supports .chm, along with any other filetypes
|
||||
supported by your browser. Supply a Menu Item title, and enter the
|
||||
location in the Help File Path slot of the New Help Source dialog. Use
|
||||
http:// and/or www. to identify external URLs, or download the file and
|
||||
browse for its path on your machine using the Browse button.
|
||||
|
||||
All users can access the extensive sources of help, including
|
||||
tutorials, available at docs.python.org. Selected URLs can be added
|
||||
or removed from the Help menu at any time using Configure IDLE.
|
||||
|
||||
Basic editing and navigation:
|
||||
|
||||
Backspace deletes char to the left; DEL deletes char to the right.
|
||||
Control-backspace deletes word left, Control-DEL deletes word right.
|
||||
Arrow keys and Page Up/Down move around.
|
||||
Control-left/right Arrow moves by words in a strange but useful way.
|
||||
Home/End go to begin/end of line.
|
||||
Control-Home/End go to begin/end of file.
|
||||
Some useful Emacs bindings are inherited from Tcl/Tk:
|
||||
Control-a beginning of line
|
||||
Control-e end of line
|
||||
Control-k kill line (but doesn't put it in clipboard)
|
||||
Control-l center window around the insertion point
|
||||
Standard keybindings (like Control-c to copy and Control-v to
|
||||
paste) may work. Keybindings are selected in the Configure IDLE
|
||||
dialog.
|
||||
|
||||
Automatic indentation:
|
||||
|
||||
After a block-opening statement, the next line is indented by 4 spaces
|
||||
(in the Python Shell window by one tab). After certain keywords
|
||||
(break, return etc.) the next line is dedented. In leading
|
||||
indentation, Backspace deletes up to 4 spaces if they are there. Tab
|
||||
inserts spaces (in the Python Shell window one tab), number depends on
|
||||
Indent Width. Currently tabs are restricted to four spaces due
|
||||
to Tcl/Tk limitations.
|
||||
|
||||
See also the indent/dedent region commands in the edit menu.
|
||||
|
||||
Completions:
|
||||
|
||||
Completions are supplied for functions, classes, and attributes of
|
||||
classes, both built-in and user-defined. Completions are also provided
|
||||
for filenames.
|
||||
|
||||
The AutoCompleteWindow (ACW) will open after a predefined delay
|
||||
(default is two seconds) after a '.' or (in a string) an os.sep is
|
||||
typed. If after one of those characters (plus zero or more other
|
||||
characters) a tab is typed the ACW will open immediately if a possible
|
||||
continuation is found.
|
||||
|
||||
If there is only one possible completion for the characters entered, a
|
||||
tab will supply that completion without opening the ACW.
|
||||
|
||||
'Show Completions' will force open a completions window, by default the
|
||||
Control-space keys will open a completions window. In an empty
|
||||
string, this will contain the files in the current directory. On a
|
||||
blank line, it will contain the built-in and user-defined functions and
|
||||
classes in the current name spaces, plus any modules imported. If some
|
||||
characters have been entered, the ACW will attempt to be more specific.
|
||||
|
||||
If string of characters is typed, the ACW selection will jump to the
|
||||
entry most closely matching those characters. Entering a tab will cause
|
||||
the longest non-ambiguous match to be entered in the Edit window or
|
||||
Shell. Two tabs in a row will supply the current ACW selection, as
|
||||
will return or a double click. Cursor keys, Page Up/Down, mouse
|
||||
selection, and the scroll wheel all operate on the ACW.
|
||||
|
||||
"Hidden" attributes can be accessed by typing the beginning of hidden
|
||||
name after a '.', e.g. '_'. This allows access to modules with
|
||||
'__all__' set, or to class-private attributes.
|
||||
|
||||
Completions and the 'Expand Word' facility can save a lot of typing!
|
||||
|
||||
Completions are currently limited to those in the namespaces. Names in
|
||||
an Editor window which are not via __main__ or sys.modules will not be
|
||||
found. Run the module once with your imports to correct this
|
||||
situation. Note that IDLE itself places quite a few modules in
|
||||
sys.modules, so much can be found by default, e.g. the re module.
|
||||
|
||||
If you don't like the ACW popping up unbidden, simply make the delay
|
||||
longer or disable the extension. Or another option is the delay could
|
||||
be set to zero. Another alternative to preventing ACW popups is to
|
||||
disable the call tips extension.
|
||||
|
||||
Python Shell window:
|
||||
|
||||
Control-c interrupts executing command.
|
||||
Control-d sends end-of-file; closes window if typed at >>> prompt.
|
||||
Alt-/ expand word is also useful to reduce typing.
|
||||
|
||||
Command history:
|
||||
|
||||
Alt-p retrieves previous command matching what you have typed. On OS X
|
||||
use Control-p.
|
||||
Alt-n retrieves next. On OS X use Control-n.
|
||||
Return while cursor is on a previous command retrieves that command.
|
||||
|
||||
Syntax colors:
|
||||
|
||||
The coloring is applied in a background "thread", so you may
|
||||
occasionally see uncolorized text. To change the color
|
||||
scheme, use the Configure IDLE / Highlighting dialog.
|
||||
|
||||
Python default syntax colors:
|
||||
|
||||
Keywords orange
|
||||
Builtins royal purple
|
||||
Strings green
|
||||
Comments red
|
||||
Definitions blue
|
||||
|
||||
Shell default colors:
|
||||
|
||||
Console output brown
|
||||
stdout blue
|
||||
stderr red
|
||||
stdin black
|
||||
|
||||
Other preferences:
|
||||
|
||||
The font preferences, highlighting, keys, and general preferences can
|
||||
be changed via the Configure IDLE menu option. Be sure to note that
|
||||
keys can be user defined, IDLE ships with four built in key sets. In
|
||||
addition a user can create a custom key set in the Configure IDLE
|
||||
dialog under the keys tab.
|
||||
|
||||
Command line usage:
|
||||
|
||||
Enter idle -h at the command prompt to get a usage message.
|
||||
|
||||
idle.py [-c command] [-d] [-e] [-s] [-t title] [arg] ...
|
||||
|
||||
-c command run this command
|
||||
-d enable debugger
|
||||
-e edit mode; arguments are files to be edited
|
||||
-s run $IDLESTARTUP or $PYTHONSTARTUP first
|
||||
-t title set title of shell window
|
||||
|
||||
If there are arguments:
|
||||
1. If -e is used, arguments are files opened for editing and sys.argv
|
||||
reflects the arguments passed to IDLE itself.
|
||||
2. Otherwise, if -c is used, all arguments are placed in
|
||||
sys.argv[1:...], with sys.argv[0] set to -c.
|
||||
3. Otherwise, if neither -e nor -c is used, the first argument is a
|
||||
script which is executed with the remaining arguments in
|
||||
sys.argv[1:...] and sys.argv[0] set to the script name. If the
|
||||
script name is -, no script is executed but an interactive Python
|
||||
session is started; the arguments are still available in sys.argv.
|
||||
|
||||
Running without a subprocess: (DEPRECATED in Python 3.4 see Issue 16123)
|
||||
|
||||
If IDLE is started with the -n command line switch it will run in a
|
||||
single process and will not create the subprocess which runs the RPC
|
||||
Python execution server. This can be useful if Python cannot create
|
||||
the subprocess or the RPC socket interface on your platform. However,
|
||||
in this mode user code is not isolated from IDLE itself. Also, the
|
||||
environment is not restarted when Run/Run Module (F5) is selected. If
|
||||
your code has been modified, you must reload() the affected modules and
|
||||
re-import any specific items (e.g. from foo import baz) if the changes
|
||||
are to take effect. For these reasons, it is preferable to run IDLE
|
||||
with the default subprocess if at all possible.
|
||||
|
||||
Extensions:
|
||||
|
||||
IDLE contains an extension facility. See the beginning of
|
||||
config-extensions.def in the idlelib directory for further information.
|
||||
The default extensions are currently:
|
||||
|
||||
FormatParagraph
|
||||
AutoExpand
|
||||
ZoomHeight
|
||||
ScriptBinding
|
||||
CallTips
|
||||
ParenMatch
|
||||
AutoComplete
|
||||
CodeContext
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
import os.path
|
||||
import sys
|
||||
|
||||
# If we are working on a development version of IDLE, we need to prepend the
|
||||
# parent of this idlelib dir to sys.path. Otherwise, importing idlelib gets
|
||||
# the version installed with the Python used to call this module:
|
||||
idlelib_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
sys.path.insert(0, idlelib_dir)
|
||||
|
||||
import idlelib.PyShell
|
||||
idlelib.PyShell.main()
|
||||
|
|
@ -1,143 +0,0 @@
|
|||
README FOR IDLE TESTS IN IDLELIB.IDLE_TEST
|
||||
|
||||
0. Quick Start
|
||||
|
||||
Automated unit tests were added in 2.7 for Python 2.x and 3.3 for Python 3.x.
|
||||
To run the tests from a command line:
|
||||
|
||||
python -m test.test_idle
|
||||
|
||||
Human-mediated tests were added later in 2.7 and in 3.4.
|
||||
|
||||
python -m idlelib.idle_test.htest
|
||||
|
||||
|
||||
1. Test Files
|
||||
|
||||
The idle directory, idlelib, has over 60 xyz.py files. The idle_test
|
||||
subdirectory should contain a test_xyz.py for each, where 'xyz' is lowercased
|
||||
even if xyz.py is not. Here is a possible template, with the blanks after after
|
||||
'.' and 'as', and before and after '_' to be filled in.
|
||||
|
||||
import unittest
|
||||
from test.support import requires
|
||||
import idlelib. as
|
||||
|
||||
class _Test(unittest.TestCase):
|
||||
|
||||
def test_(self):
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
|
||||
Add the following at the end of xyy.py, with the appropriate name added after
|
||||
'test_'. Some files already have something like this for htest. If so, insert
|
||||
the import and unittest.main lines before the htest lines.
|
||||
|
||||
if __name__ == "__main__":
|
||||
import unittest
|
||||
unittest.main('idlelib.idle_test.test_', verbosity=2, exit=False)
|
||||
|
||||
|
||||
|
||||
2. GUI Tests
|
||||
|
||||
When run as part of the Python test suite, Idle gui tests need to run
|
||||
test.support.requires('gui') (test.test_support in 2.7). A test is a gui test
|
||||
if it creates a Tk root or master object either directly or indirectly by
|
||||
instantiating a tkinter or idle class. For the benefit of test processes that
|
||||
either have no graphical environment available or are not allowed to use it, gui
|
||||
tests must be 'guarded' by "requires('gui')" in a setUp function or method.
|
||||
This will typically be setUpClass.
|
||||
|
||||
To avoid interfering with other gui tests, all gui objects must be destroyed and
|
||||
deleted by the end of the test. Widgets, such as a Tk root, created in a setUpX
|
||||
function, should be destroyed in the corresponding tearDownX. Module and class
|
||||
widget attributes should also be deleted..
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = tk.Tk()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
|
||||
Requires('gui') causes the test(s) it guards to be skipped if any of
|
||||
a few conditions are met:
|
||||
|
||||
- The tests are being run by regrtest.py, and it was started without enabling
|
||||
the "gui" resource with the "-u" command line option.
|
||||
|
||||
- The tests are being run on Windows by a service that is not allowed to
|
||||
interact with the graphical environment.
|
||||
|
||||
- The tests are being run on Mac OSX in a process that cannot make a window
|
||||
manager connection.
|
||||
|
||||
- tkinter.Tk cannot be successfully instantiated for some reason.
|
||||
|
||||
- test.support.use_resources has been set by something other than
|
||||
regrtest.py and does not contain "gui".
|
||||
|
||||
Tests of non-gui operations should avoid creating tk widgets. Incidental uses of
|
||||
tk variables and messageboxes can be replaced by the mock classes in
|
||||
idle_test/mock_tk.py. The mock text handles some uses of the tk Text widget.
|
||||
|
||||
|
||||
3. Running Unit Tests
|
||||
|
||||
Assume that xyz.py and test_xyz.py both end with a unittest.main() call.
|
||||
Running either from an Idle editor runs all tests in the test_xyz file with the
|
||||
version of Python running Idle. Test output appears in the Shell window. The
|
||||
'verbosity=2' option lists all test methods in the file, which is appropriate
|
||||
when developing tests. The 'exit=False' option is needed in xyx.py files when an
|
||||
htest follows.
|
||||
|
||||
The following command lines also run all test methods, including
|
||||
gui tests, in test_xyz.py. (Both '-m idlelib' and '-m idlelib.idle' start
|
||||
Idle and so cannot run tests.)
|
||||
|
||||
python -m idlelib.xyz
|
||||
python -m idlelib.idle_test.test_xyz
|
||||
|
||||
The following runs all idle_test/test_*.py tests interactively.
|
||||
|
||||
>>> import unittest
|
||||
>>> unittest.main('idlelib.idle_test', verbosity=2)
|
||||
|
||||
The following run all Idle tests at a command line. Option '-v' is the same as
|
||||
'verbosity=2'. (For 2.7, replace 'test' in the second line with
|
||||
'test.regrtest'.)
|
||||
|
||||
python -m unittest -v idlelib.idle_test
|
||||
python -m test -v -ugui test_idle
|
||||
python -m test.test_idle
|
||||
|
||||
The idle tests are 'discovered' by idlelib.idle_test.__init__.load_tests,
|
||||
which is also imported into test.test_idle. Normally, neither file should be
|
||||
changed when working on individual test modules. The third command runs
|
||||
unittest indirectly through regrtest. The same happens when the entire test
|
||||
suite is run with 'python -m test'. So that command must work for buildbots
|
||||
to stay green. Idle tests must not disturb the environment in a way that
|
||||
makes other tests fail (issue 18081).
|
||||
|
||||
To run an individual Testcase or test method, extend the dotted name given to
|
||||
unittest on the command line.
|
||||
|
||||
python -m unittest -v idlelib.idle_test.test_xyz.Test_case.test_meth
|
||||
|
||||
|
||||
4. Human-mediated Tests
|
||||
|
||||
Human-mediated tests are widget tests that cannot be automated but need human
|
||||
verification. They are contained in idlelib/idle_test/htest.py, which has
|
||||
instructions. (Some modules need an auxiliary function, identified with # htest
|
||||
# on the header line.) The set is about complete, though some tests need
|
||||
improvement. To run all htests, run the htest file from an editor or from the
|
||||
command line with:
|
||||
|
||||
python -m idlelib.idle_test.htest
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
"""Unit tests for idlelib.configSectionNameDialog"""
|
||||
import unittest
|
||||
from idlelib.idle_test.mock_tk import Var, Mbox
|
||||
from idlelib import configSectionNameDialog as name_dialog_module
|
||||
|
||||
name_dialog = name_dialog_module.GetCfgSectionNameDialog
|
||||
|
||||
class Dummy_name_dialog:
|
||||
# Mock for testing the following methods of name_dialog
|
||||
name_ok = name_dialog.name_ok
|
||||
Ok = name_dialog.Ok
|
||||
Cancel = name_dialog.Cancel
|
||||
# Attributes, constant or variable, needed for tests
|
||||
used_names = ['used']
|
||||
name = Var()
|
||||
result = None
|
||||
destroyed = False
|
||||
def destroy(self):
|
||||
self.destroyed = True
|
||||
|
||||
# name_ok calls Mbox.showerror if name is not ok
|
||||
orig_mbox = name_dialog_module.tkMessageBox
|
||||
showerror = Mbox.showerror
|
||||
|
||||
class ConfigNameTest(unittest.TestCase):
|
||||
dialog = Dummy_name_dialog()
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
name_dialog_module.tkMessageBox = Mbox
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
name_dialog_module.tkMessageBox = orig_mbox
|
||||
|
||||
def test_blank_name(self):
|
||||
self.dialog.name.set(' ')
|
||||
self.assertEqual(self.dialog.name_ok(), '')
|
||||
self.assertEqual(showerror.title, 'Name Error')
|
||||
self.assertIn('No', showerror.message)
|
||||
|
||||
def test_used_name(self):
|
||||
self.dialog.name.set('used')
|
||||
self.assertEqual(self.dialog.name_ok(), '')
|
||||
self.assertEqual(showerror.title, 'Name Error')
|
||||
self.assertIn('use', showerror.message)
|
||||
|
||||
def test_long_name(self):
|
||||
self.dialog.name.set('good'*8)
|
||||
self.assertEqual(self.dialog.name_ok(), '')
|
||||
self.assertEqual(showerror.title, 'Name Error')
|
||||
self.assertIn('too long', showerror.message)
|
||||
|
||||
def test_good_name(self):
|
||||
self.dialog.name.set(' good ')
|
||||
showerror.title = 'No Error' # should not be called
|
||||
self.assertEqual(self.dialog.name_ok(), 'good')
|
||||
self.assertEqual(showerror.title, 'No Error')
|
||||
|
||||
def test_ok(self):
|
||||
self.dialog.destroyed = False
|
||||
self.dialog.name.set('good')
|
||||
self.dialog.Ok()
|
||||
self.assertEqual(self.dialog.result, 'good')
|
||||
self.assertTrue(self.dialog.destroyed)
|
||||
|
||||
def test_cancel(self):
|
||||
self.dialog.destroyed = False
|
||||
self.dialog.Cancel()
|
||||
self.assertEqual(self.dialog.result, '')
|
||||
self.assertTrue(self.dialog.destroyed)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2, exit=False)
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
'''Unittests for idlelib/configHandler.py
|
||||
|
||||
Coverage: 46% just by creating dialog. The other half is change code.
|
||||
|
||||
'''
|
||||
import unittest
|
||||
from test.support import requires
|
||||
from tkinter import Tk
|
||||
from idlelib.configDialog import ConfigDialog
|
||||
from idlelib.macosxSupport import _initializeTkVariantTests
|
||||
|
||||
|
||||
class ConfigDialogTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = Tk()
|
||||
_initializeTkVariantTests(cls.root)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def test_dialog(self):
|
||||
d=ConfigDialog(self.root, 'Test', _utest=True)
|
||||
d.destroy()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
import unittest
|
||||
from tkinter import Tk, Text
|
||||
from idlelib.EditorWindow import EditorWindow
|
||||
from test.support import requires
|
||||
|
||||
class Editor_func_test(unittest.TestCase):
|
||||
def test_filename_to_unicode(self):
|
||||
func = EditorWindow._filename_to_unicode
|
||||
class dummy(): filesystemencoding = 'utf-8'
|
||||
pairs = (('abc', 'abc'), ('a\U00011111c', 'a\ufffdc'),
|
||||
(b'abc', 'abc'), (b'a\xf0\x91\x84\x91c', 'a\ufffdc'))
|
||||
for inp, out in pairs:
|
||||
self.assertEqual(func(dummy, inp), out)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
|
|
@ -1,109 +0,0 @@
|
|||
"""Test idlelib.ParenMatch."""
|
||||
# This must currently be a gui test because ParenMatch methods use
|
||||
# several text methods not defined on idlelib.idle_test.mock_tk.Text.
|
||||
|
||||
import unittest
|
||||
from unittest.mock import Mock
|
||||
from test.support import requires
|
||||
from tkinter import Tk, Text
|
||||
from idlelib.ParenMatch import ParenMatch
|
||||
|
||||
class DummyEditwin:
|
||||
def __init__(self, text):
|
||||
self.text = text
|
||||
self.indentwidth = 8
|
||||
self.tabwidth = 8
|
||||
self.context_use_ps1 = True
|
||||
|
||||
|
||||
class ParenMatchTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
requires('gui')
|
||||
cls.root = Tk()
|
||||
cls.text = Text(cls.root)
|
||||
cls.editwin = DummyEditwin(cls.text)
|
||||
cls.editwin.text_frame = Mock()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
del cls.text, cls.editwin
|
||||
cls.root.destroy()
|
||||
del cls.root
|
||||
|
||||
def tearDown(self):
|
||||
self.text.delete('1.0', 'end')
|
||||
|
||||
def test_paren_expression(self):
|
||||
"""
|
||||
Test ParenMatch with 'expression' style.
|
||||
"""
|
||||
text = self.text
|
||||
pm = ParenMatch(self.editwin)
|
||||
pm.set_style('expression')
|
||||
|
||||
text.insert('insert', 'def foobar(a, b')
|
||||
pm.flash_paren_event('event')
|
||||
self.assertIn('<<parenmatch-check-restore>>', text.event_info())
|
||||
self.assertTupleEqual(text.tag_prevrange('paren', 'end'),
|
||||
('1.10', '1.15'))
|
||||
text.insert('insert', ')')
|
||||
pm.restore_event()
|
||||
self.assertNotIn('<<parenmatch-check-restore>>', text.event_info())
|
||||
self.assertEqual(text.tag_prevrange('paren', 'end'), ())
|
||||
|
||||
# paren_closed_event can only be tested as below
|
||||
pm.paren_closed_event('event')
|
||||
self.assertTupleEqual(text.tag_prevrange('paren', 'end'),
|
||||
('1.10', '1.16'))
|
||||
|
||||
def test_paren_default(self):
|
||||
"""
|
||||
Test ParenMatch with 'default' style.
|
||||
"""
|
||||
text = self.text
|
||||
pm = ParenMatch(self.editwin)
|
||||
pm.set_style('default')
|
||||
|
||||
text.insert('insert', 'def foobar(a, b')
|
||||
pm.flash_paren_event('event')
|
||||
self.assertIn('<<parenmatch-check-restore>>', text.event_info())
|
||||
self.assertTupleEqual(text.tag_prevrange('paren', 'end'),
|
||||
('1.10', '1.11'))
|
||||
text.insert('insert', ')')
|
||||
pm.restore_event()
|
||||
self.assertNotIn('<<parenmatch-check-restore>>', text.event_info())
|
||||
self.assertEqual(text.tag_prevrange('paren', 'end'), ())
|
||||
|
||||
def test_paren_corner(self):
|
||||
"""
|
||||
Test corner cases in flash_paren_event and paren_closed_event.
|
||||
|
||||
These cases force conditional expression and alternate paths.
|
||||
"""
|
||||
text = self.text
|
||||
pm = ParenMatch(self.editwin)
|
||||
|
||||
text.insert('insert', '# this is a commen)')
|
||||
self.assertIsNone(pm.paren_closed_event('event'))
|
||||
|
||||
text.insert('insert', '\ndef')
|
||||
self.assertIsNone(pm.flash_paren_event('event'))
|
||||
self.assertIsNone(pm.paren_closed_event('event'))
|
||||
|
||||
text.insert('insert', ' a, *arg)')
|
||||
self.assertIsNone(pm.paren_closed_event('event'))
|
||||
|
||||
def test_handle_restore_timer(self):
|
||||
pm = ParenMatch(self.editwin)
|
||||
pm.restore_event = Mock()
|
||||
pm.handle_restore_timer(0)
|
||||
self.assertTrue(pm.restore_event.called)
|
||||
pm.restore_event.reset_mock()
|
||||
pm.handle_restore_timer(1)
|
||||
self.assertFalse(pm.restore_event.called)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
import unittest
|
||||
import os
|
||||
import sys
|
||||
import idlelib
|
||||
from idlelib import PathBrowser
|
||||
|
||||
class PathBrowserTest(unittest.TestCase):
|
||||
|
||||
def test_DirBrowserTreeItem(self):
|
||||
# Issue16226 - make sure that getting a sublist works
|
||||
d = PathBrowser.DirBrowserTreeItem('')
|
||||
d.GetSubList()
|
||||
self.assertEqual('', d.GetText())
|
||||
|
||||
dir = os.path.split(os.path.abspath(idlelib.__file__))[0]
|
||||
self.assertEqual(d.ispackagedir(dir), True)
|
||||
self.assertEqual(d.ispackagedir(dir + '/Icons'), False)
|
||||
|
||||
def test_PathBrowserTreeItem(self):
|
||||
p = PathBrowser.PathBrowserTreeItem()
|
||||
self.assertEqual(p.GetText(), 'sys.path')
|
||||
sub = p.GetSubList()
|
||||
self.assertEqual(len(sub), len(sys.path))
|
||||
self.assertEqual(type(sub[0]), PathBrowser.DirBrowserTreeItem)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2, exit=False)
|
||||
|
|
@ -1,97 +0,0 @@
|
|||
'''Test the functions and main class method of textView.py.
|
||||
|
||||
Since all methods and functions create (or destroy) a TextViewer, which
|
||||
is a widget containing multiple widgets, all tests must be gui tests.
|
||||
Using mock Text would not change this. Other mocks are used to retrieve
|
||||
information about calls.
|
||||
|
||||
The coverage is essentially 100%.
|
||||
'''
|
||||
from test.support import requires
|
||||
requires('gui')
|
||||
|
||||
import unittest
|
||||
import os
|
||||
from tkinter import Tk
|
||||
from idlelib import textView as tv
|
||||
from idlelib.idle_test.mock_idle import Func
|
||||
from idlelib.idle_test.mock_tk import Mbox
|
||||
|
||||
def setUpModule():
|
||||
global root
|
||||
root = Tk()
|
||||
|
||||
def tearDownModule():
|
||||
global root
|
||||
root.destroy() # pyflakes falsely sees root as undefined
|
||||
del root
|
||||
|
||||
|
||||
class TV(tv.TextViewer): # used by TextViewTest
|
||||
transient = Func()
|
||||
grab_set = Func()
|
||||
wait_window = Func()
|
||||
|
||||
class TextViewTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
TV.transient.__init__()
|
||||
TV.grab_set.__init__()
|
||||
TV.wait_window.__init__()
|
||||
|
||||
def test_init_modal(self):
|
||||
view = TV(root, 'Title', 'test text')
|
||||
self.assertTrue(TV.transient.called)
|
||||
self.assertTrue(TV.grab_set.called)
|
||||
self.assertTrue(TV.wait_window.called)
|
||||
view.Ok()
|
||||
|
||||
def test_init_nonmodal(self):
|
||||
view = TV(root, 'Title', 'test text', modal=False)
|
||||
self.assertFalse(TV.transient.called)
|
||||
self.assertFalse(TV.grab_set.called)
|
||||
self.assertFalse(TV.wait_window.called)
|
||||
view.Ok()
|
||||
|
||||
def test_ok(self):
|
||||
view = TV(root, 'Title', 'test text', modal=False)
|
||||
view.destroy = Func()
|
||||
view.Ok()
|
||||
self.assertTrue(view.destroy.called)
|
||||
del view.destroy # unmask real function
|
||||
view.destroy
|
||||
|
||||
|
||||
class textviewTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.orig_mbox = tv.tkMessageBox
|
||||
tv.tkMessageBox = Mbox
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
tv.tkMessageBox = cls.orig_mbox
|
||||
del cls.orig_mbox
|
||||
|
||||
def test_view_text(self):
|
||||
# If modal True, tkinter will error with 'can't invoke "event" command'
|
||||
view = tv.view_text(root, 'Title', 'test text', modal=False)
|
||||
self.assertIsInstance(view, tv.TextViewer)
|
||||
|
||||
def test_view_file(self):
|
||||
test_dir = os.path.dirname(__file__)
|
||||
testfile = os.path.join(test_dir, 'test_textview.py')
|
||||
view = tv.view_file(root, 'Title', testfile, modal=False)
|
||||
self.assertIsInstance(view, tv.TextViewer)
|
||||
self.assertIn('Test', view.textView.get('1.0', '1.end'))
|
||||
view.Ok()
|
||||
|
||||
# Mock messagebox will be used and view_file will not return anything
|
||||
testfile = os.path.join(test_dir, '../notthere.py')
|
||||
view = tv.view_file(root, 'Title', testfile, modal=False)
|
||||
self.assertIsNone(view)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=2)
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
"""Unused by Idle: there is no separate Idle version anymore.
|
||||
Kept only for possible existing extension use."""
|
||||
from sys import version
|
||||
IDLE_VERSION = version[:version.index(' ')]
|
||||
|
|
@ -1,498 +0,0 @@
|
|||
"""An implementation of tabbed pages using only standard Tkinter.
|
||||
|
||||
Originally developed for use in IDLE. Based on tabpage.py.
|
||||
|
||||
Classes exported:
|
||||
TabbedPageSet -- A Tkinter implementation of a tabbed-page widget.
|
||||
TabSet -- A widget containing tabs (buttons) in one or more rows.
|
||||
|
||||
"""
|
||||
from tkinter import *
|
||||
|
||||
class InvalidNameError(Exception): pass
|
||||
class AlreadyExistsError(Exception): pass
|
||||
|
||||
|
||||
class TabSet(Frame):
|
||||
"""A widget containing tabs (buttons) in one or more rows.
|
||||
|
||||
Only one tab may be selected at a time.
|
||||
|
||||
"""
|
||||
def __init__(self, page_set, select_command,
|
||||
tabs=None, n_rows=1, max_tabs_per_row=5,
|
||||
expand_tabs=False, **kw):
|
||||
"""Constructor arguments:
|
||||
|
||||
select_command -- A callable which will be called when a tab is
|
||||
selected. It is called with the name of the selected tab as an
|
||||
argument.
|
||||
|
||||
tabs -- A list of strings, the names of the tabs. Should be specified in
|
||||
the desired tab order. The first tab will be the default and first
|
||||
active tab. If tabs is None or empty, the TabSet will be initialized
|
||||
empty.
|
||||
|
||||
n_rows -- Number of rows of tabs to be shown. If n_rows <= 0 or is
|
||||
None, then the number of rows will be decided by TabSet. See
|
||||
_arrange_tabs() for details.
|
||||
|
||||
max_tabs_per_row -- Used for deciding how many rows of tabs are needed,
|
||||
when the number of rows is not constant. See _arrange_tabs() for
|
||||
details.
|
||||
|
||||
"""
|
||||
Frame.__init__(self, page_set, **kw)
|
||||
self.select_command = select_command
|
||||
self.n_rows = n_rows
|
||||
self.max_tabs_per_row = max_tabs_per_row
|
||||
self.expand_tabs = expand_tabs
|
||||
self.page_set = page_set
|
||||
|
||||
self._tabs = {}
|
||||
self._tab2row = {}
|
||||
if tabs:
|
||||
self._tab_names = list(tabs)
|
||||
else:
|
||||
self._tab_names = []
|
||||
self._selected_tab = None
|
||||
self._tab_rows = []
|
||||
|
||||
self.padding_frame = Frame(self, height=2,
|
||||
borderwidth=0, relief=FLAT,
|
||||
background=self.cget('background'))
|
||||
self.padding_frame.pack(side=TOP, fill=X, expand=False)
|
||||
|
||||
self._arrange_tabs()
|
||||
|
||||
def add_tab(self, tab_name):
|
||||
"""Add a new tab with the name given in tab_name."""
|
||||
if not tab_name:
|
||||
raise InvalidNameError("Invalid Tab name: '%s'" % tab_name)
|
||||
if tab_name in self._tab_names:
|
||||
raise AlreadyExistsError("Tab named '%s' already exists" %tab_name)
|
||||
|
||||
self._tab_names.append(tab_name)
|
||||
self._arrange_tabs()
|
||||
|
||||
def remove_tab(self, tab_name):
|
||||
"""Remove the tab named <tab_name>"""
|
||||
if not tab_name in self._tab_names:
|
||||
raise KeyError("No such Tab: '%s" % tab_name)
|
||||
|
||||
self._tab_names.remove(tab_name)
|
||||
self._arrange_tabs()
|
||||
|
||||
def set_selected_tab(self, tab_name):
|
||||
"""Show the tab named <tab_name> as the selected one"""
|
||||
if tab_name == self._selected_tab:
|
||||
return
|
||||
if tab_name is not None and tab_name not in self._tabs:
|
||||
raise KeyError("No such Tab: '%s" % tab_name)
|
||||
|
||||
# deselect the current selected tab
|
||||
if self._selected_tab is not None:
|
||||
self._tabs[self._selected_tab].set_normal()
|
||||
self._selected_tab = None
|
||||
|
||||
if tab_name is not None:
|
||||
# activate the tab named tab_name
|
||||
self._selected_tab = tab_name
|
||||
tab = self._tabs[tab_name]
|
||||
tab.set_selected()
|
||||
# move the tab row with the selected tab to the bottom
|
||||
tab_row = self._tab2row[tab]
|
||||
tab_row.pack_forget()
|
||||
tab_row.pack(side=TOP, fill=X, expand=0)
|
||||
|
||||
def _add_tab_row(self, tab_names, expand_tabs):
|
||||
if not tab_names:
|
||||
return
|
||||
|
||||
tab_row = Frame(self)
|
||||
tab_row.pack(side=TOP, fill=X, expand=0)
|
||||
self._tab_rows.append(tab_row)
|
||||
|
||||
for tab_name in tab_names:
|
||||
tab = TabSet.TabButton(tab_name, self.select_command,
|
||||
tab_row, self)
|
||||
if expand_tabs:
|
||||
tab.pack(side=LEFT, fill=X, expand=True)
|
||||
else:
|
||||
tab.pack(side=LEFT)
|
||||
self._tabs[tab_name] = tab
|
||||
self._tab2row[tab] = tab_row
|
||||
|
||||
# tab is the last one created in the above loop
|
||||
tab.is_last_in_row = True
|
||||
|
||||
def _reset_tab_rows(self):
|
||||
while self._tab_rows:
|
||||
tab_row = self._tab_rows.pop()
|
||||
tab_row.destroy()
|
||||
self._tab2row = {}
|
||||
|
||||
def _arrange_tabs(self):
|
||||
"""
|
||||
Arrange the tabs in rows, in the order in which they were added.
|
||||
|
||||
If n_rows >= 1, this will be the number of rows used. Otherwise the
|
||||
number of rows will be calculated according to the number of tabs and
|
||||
max_tabs_per_row. In this case, the number of rows may change when
|
||||
adding/removing tabs.
|
||||
|
||||
"""
|
||||
# remove all tabs and rows
|
||||
while self._tabs:
|
||||
self._tabs.popitem()[1].destroy()
|
||||
self._reset_tab_rows()
|
||||
|
||||
if not self._tab_names:
|
||||
return
|
||||
|
||||
if self.n_rows is not None and self.n_rows > 0:
|
||||
n_rows = self.n_rows
|
||||
else:
|
||||
# calculate the required number of rows
|
||||
n_rows = (len(self._tab_names) - 1) // self.max_tabs_per_row + 1
|
||||
|
||||
# not expanding the tabs with more than one row is very ugly
|
||||
expand_tabs = self.expand_tabs or n_rows > 1
|
||||
i = 0 # index in self._tab_names
|
||||
for row_index in range(n_rows):
|
||||
# calculate required number of tabs in this row
|
||||
n_tabs = (len(self._tab_names) - i - 1) // (n_rows - row_index) + 1
|
||||
tab_names = self._tab_names[i:i + n_tabs]
|
||||
i += n_tabs
|
||||
self._add_tab_row(tab_names, expand_tabs)
|
||||
|
||||
# re-select selected tab so it is properly displayed
|
||||
selected = self._selected_tab
|
||||
self.set_selected_tab(None)
|
||||
if selected in self._tab_names:
|
||||
self.set_selected_tab(selected)
|
||||
|
||||
class TabButton(Frame):
|
||||
"""A simple tab-like widget."""
|
||||
|
||||
bw = 2 # borderwidth
|
||||
|
||||
def __init__(self, name, select_command, tab_row, tab_set):
|
||||
"""Constructor arguments:
|
||||
|
||||
name -- The tab's name, which will appear in its button.
|
||||
|
||||
select_command -- The command to be called upon selection of the
|
||||
tab. It is called with the tab's name as an argument.
|
||||
|
||||
"""
|
||||
Frame.__init__(self, tab_row, borderwidth=self.bw, relief=RAISED)
|
||||
|
||||
self.name = name
|
||||
self.select_command = select_command
|
||||
self.tab_set = tab_set
|
||||
self.is_last_in_row = False
|
||||
|
||||
self.button = Radiobutton(
|
||||
self, text=name, command=self._select_event,
|
||||
padx=5, pady=1, takefocus=FALSE, indicatoron=FALSE,
|
||||
highlightthickness=0, selectcolor='', borderwidth=0)
|
||||
self.button.pack(side=LEFT, fill=X, expand=True)
|
||||
|
||||
self._init_masks()
|
||||
self.set_normal()
|
||||
|
||||
def _select_event(self, *args):
|
||||
"""Event handler for tab selection.
|
||||
|
||||
With TabbedPageSet, this calls TabbedPageSet.change_page, so that
|
||||
selecting a tab changes the page.
|
||||
|
||||
Note that this does -not- call set_selected -- it will be called by
|
||||
TabSet.set_selected_tab, which should be called when whatever the
|
||||
tabs are related to changes.
|
||||
|
||||
"""
|
||||
self.select_command(self.name)
|
||||
return
|
||||
|
||||
def set_selected(self):
|
||||
"""Assume selected look"""
|
||||
self._place_masks(selected=True)
|
||||
|
||||
def set_normal(self):
|
||||
"""Assume normal look"""
|
||||
self._place_masks(selected=False)
|
||||
|
||||
def _init_masks(self):
|
||||
page_set = self.tab_set.page_set
|
||||
background = page_set.pages_frame.cget('background')
|
||||
# mask replaces the middle of the border with the background color
|
||||
self.mask = Frame(page_set, borderwidth=0, relief=FLAT,
|
||||
background=background)
|
||||
# mskl replaces the bottom-left corner of the border with a normal
|
||||
# left border
|
||||
self.mskl = Frame(page_set, borderwidth=0, relief=FLAT,
|
||||
background=background)
|
||||
self.mskl.ml = Frame(self.mskl, borderwidth=self.bw,
|
||||
relief=RAISED)
|
||||
self.mskl.ml.place(x=0, y=-self.bw,
|
||||
width=2*self.bw, height=self.bw*4)
|
||||
# mskr replaces the bottom-right corner of the border with a normal
|
||||
# right border
|
||||
self.mskr = Frame(page_set, borderwidth=0, relief=FLAT,
|
||||
background=background)
|
||||
self.mskr.mr = Frame(self.mskr, borderwidth=self.bw,
|
||||
relief=RAISED)
|
||||
|
||||
def _place_masks(self, selected=False):
|
||||
height = self.bw
|
||||
if selected:
|
||||
height += self.bw
|
||||
|
||||
self.mask.place(in_=self,
|
||||
relx=0.0, x=0,
|
||||
rely=1.0, y=0,
|
||||
relwidth=1.0, width=0,
|
||||
relheight=0.0, height=height)
|
||||
|
||||
self.mskl.place(in_=self,
|
||||
relx=0.0, x=-self.bw,
|
||||
rely=1.0, y=0,
|
||||
relwidth=0.0, width=self.bw,
|
||||
relheight=0.0, height=height)
|
||||
|
||||
page_set = self.tab_set.page_set
|
||||
if selected and ((not self.is_last_in_row) or
|
||||
(self.winfo_rootx() + self.winfo_width() <
|
||||
page_set.winfo_rootx() + page_set.winfo_width())
|
||||
):
|
||||
# for a selected tab, if its rightmost edge isn't on the
|
||||
# rightmost edge of the page set, the right mask should be one
|
||||
# borderwidth shorter (vertically)
|
||||
height -= self.bw
|
||||
|
||||
self.mskr.place(in_=self,
|
||||
relx=1.0, x=0,
|
||||
rely=1.0, y=0,
|
||||
relwidth=0.0, width=self.bw,
|
||||
relheight=0.0, height=height)
|
||||
|
||||
self.mskr.mr.place(x=-self.bw, y=-self.bw,
|
||||
width=2*self.bw, height=height + self.bw*2)
|
||||
|
||||
# finally, lower the tab set so that all of the frames we just
|
||||
# placed hide it
|
||||
self.tab_set.lower()
|
||||
|
||||
class TabbedPageSet(Frame):
|
||||
"""A Tkinter tabbed-pane widget.
|
||||
|
||||
Constains set of 'pages' (or 'panes') with tabs above for selecting which
|
||||
page is displayed. Only one page will be displayed at a time.
|
||||
|
||||
Pages may be accessed through the 'pages' attribute, which is a dictionary
|
||||
of pages, using the name given as the key. A page is an instance of a
|
||||
subclass of Tk's Frame widget.
|
||||
|
||||
The page widgets will be created (and destroyed when required) by the
|
||||
TabbedPageSet. Do not call the page's pack/place/grid/destroy methods.
|
||||
|
||||
Pages may be added or removed at any time using the add_page() and
|
||||
remove_page() methods.
|
||||
|
||||
"""
|
||||
class Page(object):
|
||||
"""Abstract base class for TabbedPageSet's pages.
|
||||
|
||||
Subclasses must override the _show() and _hide() methods.
|
||||
|
||||
"""
|
||||
uses_grid = False
|
||||
|
||||
def __init__(self, page_set):
|
||||
self.frame = Frame(page_set, borderwidth=2, relief=RAISED)
|
||||
|
||||
def _show(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def _hide(self):
|
||||
raise NotImplementedError
|
||||
|
||||
class PageRemove(Page):
|
||||
"""Page class using the grid placement manager's "remove" mechanism."""
|
||||
uses_grid = True
|
||||
|
||||
def _show(self):
|
||||
self.frame.grid(row=0, column=0, sticky=NSEW)
|
||||
|
||||
def _hide(self):
|
||||
self.frame.grid_remove()
|
||||
|
||||
class PageLift(Page):
|
||||
"""Page class using the grid placement manager's "lift" mechanism."""
|
||||
uses_grid = True
|
||||
|
||||
def __init__(self, page_set):
|
||||
super(TabbedPageSet.PageLift, self).__init__(page_set)
|
||||
self.frame.grid(row=0, column=0, sticky=NSEW)
|
||||
self.frame.lower()
|
||||
|
||||
def _show(self):
|
||||
self.frame.lift()
|
||||
|
||||
def _hide(self):
|
||||
self.frame.lower()
|
||||
|
||||
class PagePackForget(Page):
|
||||
"""Page class using the pack placement manager's "forget" mechanism."""
|
||||
def _show(self):
|
||||
self.frame.pack(fill=BOTH, expand=True)
|
||||
|
||||
def _hide(self):
|
||||
self.frame.pack_forget()
|
||||
|
||||
def __init__(self, parent, page_names=None, page_class=PageLift,
|
||||
n_rows=1, max_tabs_per_row=5, expand_tabs=False,
|
||||
**kw):
|
||||
"""Constructor arguments:
|
||||
|
||||
page_names -- A list of strings, each will be the dictionary key to a
|
||||
page's widget, and the name displayed on the page's tab. Should be
|
||||
specified in the desired page order. The first page will be the default
|
||||
and first active page. If page_names is None or empty, the
|
||||
TabbedPageSet will be initialized empty.
|
||||
|
||||
n_rows, max_tabs_per_row -- Parameters for the TabSet which will
|
||||
manage the tabs. See TabSet's docs for details.
|
||||
|
||||
page_class -- Pages can be shown/hidden using three mechanisms:
|
||||
|
||||
* PageLift - All pages will be rendered one on top of the other. When
|
||||
a page is selected, it will be brought to the top, thus hiding all
|
||||
other pages. Using this method, the TabbedPageSet will not be resized
|
||||
when pages are switched. (It may still be resized when pages are
|
||||
added/removed.)
|
||||
|
||||
* PageRemove - When a page is selected, the currently showing page is
|
||||
hidden, and the new page shown in its place. Using this method, the
|
||||
TabbedPageSet may resize when pages are changed.
|
||||
|
||||
* PagePackForget - This mechanism uses the pack placement manager.
|
||||
When a page is shown it is packed, and when it is hidden it is
|
||||
unpacked (i.e. pack_forget). This mechanism may also cause the
|
||||
TabbedPageSet to resize when the page is changed.
|
||||
|
||||
"""
|
||||
Frame.__init__(self, parent, **kw)
|
||||
|
||||
self.page_class = page_class
|
||||
self.pages = {}
|
||||
self._pages_order = []
|
||||
self._current_page = None
|
||||
self._default_page = None
|
||||
|
||||
self.columnconfigure(0, weight=1)
|
||||
self.rowconfigure(1, weight=1)
|
||||
|
||||
self.pages_frame = Frame(self)
|
||||
self.pages_frame.grid(row=1, column=0, sticky=NSEW)
|
||||
if self.page_class.uses_grid:
|
||||
self.pages_frame.columnconfigure(0, weight=1)
|
||||
self.pages_frame.rowconfigure(0, weight=1)
|
||||
|
||||
# the order of the following commands is important
|
||||
self._tab_set = TabSet(self, self.change_page, n_rows=n_rows,
|
||||
max_tabs_per_row=max_tabs_per_row,
|
||||
expand_tabs=expand_tabs)
|
||||
if page_names:
|
||||
for name in page_names:
|
||||
self.add_page(name)
|
||||
self._tab_set.grid(row=0, column=0, sticky=NSEW)
|
||||
|
||||
self.change_page(self._default_page)
|
||||
|
||||
def add_page(self, page_name):
|
||||
"""Add a new page with the name given in page_name."""
|
||||
if not page_name:
|
||||
raise InvalidNameError("Invalid TabPage name: '%s'" % page_name)
|
||||
if page_name in self.pages:
|
||||
raise AlreadyExistsError(
|
||||
"TabPage named '%s' already exists" % page_name)
|
||||
|
||||
self.pages[page_name] = self.page_class(self.pages_frame)
|
||||
self._pages_order.append(page_name)
|
||||
self._tab_set.add_tab(page_name)
|
||||
|
||||
if len(self.pages) == 1: # adding first page
|
||||
self._default_page = page_name
|
||||
self.change_page(page_name)
|
||||
|
||||
def remove_page(self, page_name):
|
||||
"""Destroy the page whose name is given in page_name."""
|
||||
if not page_name in self.pages:
|
||||
raise KeyError("No such TabPage: '%s" % page_name)
|
||||
|
||||
self._pages_order.remove(page_name)
|
||||
|
||||
# handle removing last remaining, default, or currently shown page
|
||||
if len(self._pages_order) > 0:
|
||||
if page_name == self._default_page:
|
||||
# set a new default page
|
||||
self._default_page = self._pages_order[0]
|
||||
else:
|
||||
self._default_page = None
|
||||
|
||||
if page_name == self._current_page:
|
||||
self.change_page(self._default_page)
|
||||
|
||||
self._tab_set.remove_tab(page_name)
|
||||
page = self.pages.pop(page_name)
|
||||
page.frame.destroy()
|
||||
|
||||
def change_page(self, page_name):
|
||||
"""Show the page whose name is given in page_name."""
|
||||
if self._current_page == page_name:
|
||||
return
|
||||
if page_name is not None and page_name not in self.pages:
|
||||
raise KeyError("No such TabPage: '%s'" % page_name)
|
||||
|
||||
if self._current_page is not None:
|
||||
self.pages[self._current_page]._hide()
|
||||
self._current_page = None
|
||||
|
||||
if page_name is not None:
|
||||
self._current_page = page_name
|
||||
self.pages[page_name]._show()
|
||||
|
||||
self._tab_set.set_selected_tab(page_name)
|
||||
|
||||
def _tabbed_pages(parent):
|
||||
# test dialog
|
||||
root=Tk()
|
||||
width, height, x, y = list(map(int, re.split('[x+]', parent.geometry())))
|
||||
root.geometry("+%d+%d"%(x, y + 175))
|
||||
root.title("Test tabbed pages")
|
||||
tabPage=TabbedPageSet(root, page_names=['Foobar','Baz'], n_rows=0,
|
||||
expand_tabs=False,
|
||||
)
|
||||
tabPage.pack(side=TOP, expand=TRUE, fill=BOTH)
|
||||
Label(tabPage.pages['Foobar'].frame, text='Foo', pady=20).pack()
|
||||
Label(tabPage.pages['Foobar'].frame, text='Bar', pady=20).pack()
|
||||
Label(tabPage.pages['Baz'].frame, text='Baz').pack()
|
||||
entryPgName=Entry(root)
|
||||
buttonAdd=Button(root, text='Add Page',
|
||||
command=lambda:tabPage.add_page(entryPgName.get()))
|
||||
buttonRemove=Button(root, text='Remove Page',
|
||||
command=lambda:tabPage.remove_page(entryPgName.get()))
|
||||
labelPgName=Label(root, text='name of page to add/remove:')
|
||||
buttonAdd.pack(padx=5, pady=5)
|
||||
buttonRemove.pack(padx=5, pady=5)
|
||||
labelPgName.pack(padx=5)
|
||||
entryPgName.pack(padx=5)
|
||||
root.mainloop()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from idlelib.idle_test.htest import run
|
||||
run(_tabbed_pages)
|
||||
|
|
@ -1,86 +0,0 @@
|
|||
"""Simple text browser for IDLE
|
||||
|
||||
"""
|
||||
|
||||
from tkinter import *
|
||||
import tkinter.messagebox as tkMessageBox
|
||||
|
||||
class TextViewer(Toplevel):
|
||||
"""A simple text viewer dialog for IDLE
|
||||
|
||||
"""
|
||||
def __init__(self, parent, title, text, modal=True, _htest=False):
|
||||
"""Show the given text in a scrollable window with a 'close' button
|
||||
|
||||
If modal option set to False, user can interact with other windows,
|
||||
otherwise they will be unable to interact with other windows until
|
||||
the textview window is closed.
|
||||
|
||||
_htest - bool; change box location when running htest.
|
||||
"""
|
||||
Toplevel.__init__(self, parent)
|
||||
self.configure(borderwidth=5)
|
||||
# place dialog below parent if running htest
|
||||
self.geometry("=%dx%d+%d+%d" % (625, 500,
|
||||
parent.winfo_rootx() + 10,
|
||||
parent.winfo_rooty() + (10 if not _htest else 100)))
|
||||
#elguavas - config placeholders til config stuff completed
|
||||
self.bg = '#ffffff'
|
||||
self.fg = '#000000'
|
||||
|
||||
self.CreateWidgets()
|
||||
self.title(title)
|
||||
self.protocol("WM_DELETE_WINDOW", self.Ok)
|
||||
self.parent = parent
|
||||
self.textView.focus_set()
|
||||
#key bindings for this dialog
|
||||
self.bind('<Return>',self.Ok) #dismiss dialog
|
||||
self.bind('<Escape>',self.Ok) #dismiss dialog
|
||||
self.textView.insert(0.0, text)
|
||||
self.textView.config(state=DISABLED)
|
||||
|
||||
if modal:
|
||||
self.transient(parent)
|
||||
self.grab_set()
|
||||
self.wait_window()
|
||||
|
||||
def CreateWidgets(self):
|
||||
frameText = Frame(self, relief=SUNKEN, height=700)
|
||||
frameButtons = Frame(self)
|
||||
self.buttonOk = Button(frameButtons, text='Close',
|
||||
command=self.Ok, takefocus=FALSE)
|
||||
self.scrollbarView = Scrollbar(frameText, orient=VERTICAL,
|
||||
takefocus=FALSE, highlightthickness=0)
|
||||
self.textView = Text(frameText, wrap=WORD, highlightthickness=0,
|
||||
fg=self.fg, bg=self.bg)
|
||||
self.scrollbarView.config(command=self.textView.yview)
|
||||
self.textView.config(yscrollcommand=self.scrollbarView.set)
|
||||
self.buttonOk.pack()
|
||||
self.scrollbarView.pack(side=RIGHT,fill=Y)
|
||||
self.textView.pack(side=LEFT,expand=TRUE,fill=BOTH)
|
||||
frameButtons.pack(side=BOTTOM,fill=X)
|
||||
frameText.pack(side=TOP,expand=TRUE,fill=BOTH)
|
||||
|
||||
def Ok(self, event=None):
|
||||
self.destroy()
|
||||
|
||||
|
||||
def view_text(parent, title, text, modal=True):
|
||||
return TextViewer(parent, title, text, modal)
|
||||
|
||||
def view_file(parent, title, filename, encoding=None, modal=True):
|
||||
try:
|
||||
with open(filename, 'r', encoding=encoding) as file:
|
||||
contents = file.read()
|
||||
except IOError:
|
||||
tkMessageBox.showerror(title='File Load Error',
|
||||
message='Unable to load file %r .' % filename,
|
||||
parent=parent)
|
||||
else:
|
||||
return view_text(parent, title, contents, modal)
|
||||
|
||||
if __name__ == '__main__':
|
||||
import unittest
|
||||
unittest.main('idlelib.idle_test.test_textview', verbosity=2, exit=False)
|
||||
from idlelib.idle_test.htest import run
|
||||
run(TextViewer)
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue