Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- lib/cmake/expat-2.7.1/expat-config-version.cmake +65 -0
- lib/cmake/expat-2.7.1/expat-config.cmake +99 -0
- lib/cmake/expat-2.7.1/expat-noconfig.cmake +19 -0
- lib/cmake/expat-2.7.1/expat.cmake +107 -0
- lib/cmake/libdeflate/libdeflate-config-version.cmake +43 -0
- lib/cmake/libdeflate/libdeflate-config.cmake +27 -0
- lib/cmake/libdeflate/libdeflate-targets-release.cmake +19 -0
- lib/itcl4.2.4/itcl.tcl +151 -0
- lib/itcl4.2.4/itclConfig.sh +67 -0
- lib/itcl4.2.4/itclHullCmds.tcl +562 -0
- lib/itcl4.2.4/itclWidget.tcl +447 -0
- lib/itcl4.2.4/libitclstub4.2.4.a +0 -0
- lib/itcl4.2.4/pkgIndex.tcl +14 -0
- lib/python3.10/py_compile.py +212 -0
- lib/python3.10/pyclbr.py +314 -0
- lib/python3.10/pydoc.py +0 -0
- lib/python3.10/queue.py +326 -0
- lib/python3.10/quopri.py +242 -0
- lib/python3.10/random.py +930 -0
- lib/python3.10/re.py +383 -0
- lib/python3.10/reprlib.py +161 -0
- lib/python3.10/rlcompleter.py +219 -0
- lib/python3.10/runpy.py +321 -0
- lib/python3.10/sched.py +167 -0
- lib/python3.10/secrets.py +72 -0
- lib/python3.10/selectors.py +619 -0
- lib/python3.10/shelve.py +243 -0
- lib/python3.10/shlex.py +350 -0
- lib/python3.10/shutil.py +1517 -0
- lib/python3.10/signal.py +92 -0
- lib/python3.10/uu.py +213 -0
- lib/python3.10/uuid.py +733 -0
- lib/python3.10/warnings.py +549 -0
- lib/python3.10/wave.py +513 -0
- lib/python3.10/weakref.py +675 -0
- lib/sqlite3.44.2/pkgIndex.tcl +12 -0
- lib/tcl8.6/auto.tcl +648 -0
- lib/tcl8.6/clock.tcl +0 -0
- lib/tcl8.6/history.tcl +335 -0
- lib/tcl8.6/init.tcl +827 -0
- lib/tcl8.6/package.tcl +751 -0
- lib/tcl8.6/parray.tcl +28 -0
- lib/tcl8.6/safe.tcl +1289 -0
- lib/tcl8.6/tclAppInit.c +176 -0
- lib/tcl8.6/tclIndex +79 -0
- lib/tcl8.6/tm.tcl +380 -0
- lib/tcl8.6/word.tcl +154 -0
- lib/tdbc1.1.7/libtdbc1.1.7.so +0 -0
- lib/tdbc1.1.7/libtdbcstub1.1.7.a +0 -0
- lib/tdbc1.1.7/pkgIndex.tcl +26 -0
lib/cmake/expat-2.7.1/expat-config-version.cmake
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This is a basic version file for the Config-mode of find_package().
|
| 2 |
+
# It is used by write_basic_package_version_file() as input file for configure_file()
|
| 3 |
+
# to create a version-file which can be installed along a config.cmake file.
|
| 4 |
+
#
|
| 5 |
+
# The created file sets PACKAGE_VERSION_EXACT if the current version string and
|
| 6 |
+
# the requested version string are exactly the same and it sets
|
| 7 |
+
# PACKAGE_VERSION_COMPATIBLE if the current version is >= requested version,
|
| 8 |
+
# but only if the requested major version is the same as the current one.
|
| 9 |
+
# The variable CVF_VERSION must be set before calling configure_file().
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
set(PACKAGE_VERSION "2.7.1")
|
| 13 |
+
|
| 14 |
+
if(PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION)
|
| 15 |
+
set(PACKAGE_VERSION_COMPATIBLE FALSE)
|
| 16 |
+
else()
|
| 17 |
+
|
| 18 |
+
if("2.7.1" MATCHES "^([0-9]+)\\.")
|
| 19 |
+
set(CVF_VERSION_MAJOR "${CMAKE_MATCH_1}")
|
| 20 |
+
if(NOT CVF_VERSION_MAJOR VERSION_EQUAL 0)
|
| 21 |
+
string(REGEX REPLACE "^0+" "" CVF_VERSION_MAJOR "${CVF_VERSION_MAJOR}")
|
| 22 |
+
endif()
|
| 23 |
+
else()
|
| 24 |
+
set(CVF_VERSION_MAJOR "2.7.1")
|
| 25 |
+
endif()
|
| 26 |
+
|
| 27 |
+
if(PACKAGE_FIND_VERSION_RANGE)
|
| 28 |
+
# both endpoints of the range must have the expected major version
|
| 29 |
+
math (EXPR CVF_VERSION_MAJOR_NEXT "${CVF_VERSION_MAJOR} + 1")
|
| 30 |
+
if (NOT PACKAGE_FIND_VERSION_MIN_MAJOR STREQUAL CVF_VERSION_MAJOR
|
| 31 |
+
OR ((PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "INCLUDE" AND NOT PACKAGE_FIND_VERSION_MAX_MAJOR STREQUAL CVF_VERSION_MAJOR)
|
| 32 |
+
OR (PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "EXCLUDE" AND NOT PACKAGE_FIND_VERSION_MAX VERSION_LESS_EQUAL CVF_VERSION_MAJOR_NEXT)))
|
| 33 |
+
set(PACKAGE_VERSION_COMPATIBLE FALSE)
|
| 34 |
+
elseif(PACKAGE_FIND_VERSION_MIN_MAJOR STREQUAL CVF_VERSION_MAJOR
|
| 35 |
+
AND ((PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "INCLUDE" AND PACKAGE_VERSION VERSION_LESS_EQUAL PACKAGE_FIND_VERSION_MAX)
|
| 36 |
+
OR (PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "EXCLUDE" AND PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION_MAX)))
|
| 37 |
+
set(PACKAGE_VERSION_COMPATIBLE TRUE)
|
| 38 |
+
else()
|
| 39 |
+
set(PACKAGE_VERSION_COMPATIBLE FALSE)
|
| 40 |
+
endif()
|
| 41 |
+
else()
|
| 42 |
+
if(PACKAGE_FIND_VERSION_MAJOR STREQUAL CVF_VERSION_MAJOR)
|
| 43 |
+
set(PACKAGE_VERSION_COMPATIBLE TRUE)
|
| 44 |
+
else()
|
| 45 |
+
set(PACKAGE_VERSION_COMPATIBLE FALSE)
|
| 46 |
+
endif()
|
| 47 |
+
|
| 48 |
+
if(PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION)
|
| 49 |
+
set(PACKAGE_VERSION_EXACT TRUE)
|
| 50 |
+
endif()
|
| 51 |
+
endif()
|
| 52 |
+
endif()
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
# if the installed or the using project don't have CMAKE_SIZEOF_VOID_P set, ignore it:
|
| 56 |
+
if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "" OR "8" STREQUAL "")
|
| 57 |
+
return()
|
| 58 |
+
endif()
|
| 59 |
+
|
| 60 |
+
# check that the installed version has the same 32/64bit-ness as the one which is currently searching:
|
| 61 |
+
if(NOT CMAKE_SIZEOF_VOID_P STREQUAL "8")
|
| 62 |
+
math(EXPR installedBits "8 * 8")
|
| 63 |
+
set(PACKAGE_VERSION "${PACKAGE_VERSION} (${installedBits}bit)")
|
| 64 |
+
set(PACKAGE_VERSION_UNSUITABLE TRUE)
|
| 65 |
+
endif()
|
lib/cmake/expat-2.7.1/expat-config.cmake
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# __ __ _
|
| 2 |
+
# ___\ \/ /_ __ __ _| |_
|
| 3 |
+
# / _ \\ /| '_ \ / _` | __|
|
| 4 |
+
# | __// \| |_) | (_| | |_
|
| 5 |
+
# \___/_/\_\ .__/ \__,_|\__|
|
| 6 |
+
# |_| XML parser
|
| 7 |
+
#
|
| 8 |
+
# Copyright (c) 2019 Expat development team
|
| 9 |
+
# Licensed under the MIT license:
|
| 10 |
+
#
|
| 11 |
+
# Permission is hereby granted, free of charge, to any person obtaining
|
| 12 |
+
# a copy of this software and associated documentation files (the
|
| 13 |
+
# "Software"), to deal in the Software without restriction, including
|
| 14 |
+
# without limitation the rights to use, copy, modify, merge, publish,
|
| 15 |
+
# distribute, sublicense, and/or sell copies of the Software, and to permit
|
| 16 |
+
# persons to whom the Software is furnished to do so, subject to the
|
| 17 |
+
# following conditions:
|
| 18 |
+
#
|
| 19 |
+
# The above copyright notice and this permission notice shall be included
|
| 20 |
+
# in all copies or substantial portions of the Software.
|
| 21 |
+
#
|
| 22 |
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
| 23 |
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
| 24 |
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
| 25 |
+
# NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
| 26 |
+
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
| 27 |
+
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
| 28 |
+
# USE OR OTHER DEALINGS IN THE SOFTWARE.
|
| 29 |
+
#
|
| 30 |
+
if(NOT _expat_config_included)
|
| 31 |
+
# Protect against multiple inclusion
|
| 32 |
+
set(_expat_config_included TRUE)
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
include("${CMAKE_CURRENT_LIST_DIR}/expat.cmake")
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
####### Expanded from @PACKAGE_INIT@ by configure_package_config_file() #######
|
| 39 |
+
####### Any changes to this file will be overwritten by the next CMake run ####
|
| 40 |
+
####### The input file was expat-config.cmake.in ########
|
| 41 |
+
|
| 42 |
+
get_filename_component(PACKAGE_PREFIX_DIR "${CMAKE_CURRENT_LIST_DIR}/../../../" ABSOLUTE)
|
| 43 |
+
|
| 44 |
+
macro(set_and_check _var _file)
|
| 45 |
+
set(${_var} "${_file}")
|
| 46 |
+
if(NOT EXISTS "${_file}")
|
| 47 |
+
message(FATAL_ERROR "File or directory ${_file} referenced by variable ${_var} does not exist !")
|
| 48 |
+
endif()
|
| 49 |
+
endmacro()
|
| 50 |
+
|
| 51 |
+
macro(check_required_components _NAME)
|
| 52 |
+
foreach(comp ${${_NAME}_FIND_COMPONENTS})
|
| 53 |
+
if(NOT ${_NAME}_${comp}_FOUND)
|
| 54 |
+
if(${_NAME}_FIND_REQUIRED_${comp})
|
| 55 |
+
set(${_NAME}_FOUND FALSE)
|
| 56 |
+
endif()
|
| 57 |
+
endif()
|
| 58 |
+
endforeach()
|
| 59 |
+
endmacro()
|
| 60 |
+
|
| 61 |
+
####################################################################################
|
| 62 |
+
|
| 63 |
+
#
|
| 64 |
+
# Supported components
|
| 65 |
+
#
|
| 66 |
+
macro(expat_register_component _NAME _AVAILABE)
|
| 67 |
+
set(expat_${_NAME}_FOUND ${_AVAILABE})
|
| 68 |
+
endmacro()
|
| 69 |
+
|
| 70 |
+
expat_register_component(attr_info OFF)
|
| 71 |
+
expat_register_component(dtd ON)
|
| 72 |
+
expat_register_component(large_size OFF)
|
| 73 |
+
expat_register_component(min_size OFF)
|
| 74 |
+
expat_register_component(ns ON)
|
| 75 |
+
|
| 76 |
+
if(1024)
|
| 77 |
+
expat_register_component(context_bytes ON)
|
| 78 |
+
else()
|
| 79 |
+
expat_register_component(context_bytes OFF)
|
| 80 |
+
endif()
|
| 81 |
+
|
| 82 |
+
if("char" STREQUAL "char")
|
| 83 |
+
expat_register_component(char ON)
|
| 84 |
+
expat_register_component(ushort OFF)
|
| 85 |
+
expat_register_component(wchar_t OFF)
|
| 86 |
+
elseif("char" STREQUAL "ushort")
|
| 87 |
+
expat_register_component(char OFF)
|
| 88 |
+
expat_register_component(ushort ON)
|
| 89 |
+
expat_register_component(wchar_t OFF)
|
| 90 |
+
elseif("char" STREQUAL "wchar_t")
|
| 91 |
+
expat_register_component(char OFF)
|
| 92 |
+
expat_register_component(ushort OFF)
|
| 93 |
+
expat_register_component(wchar_t ON)
|
| 94 |
+
endif()
|
| 95 |
+
|
| 96 |
+
check_required_components(expat)
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
endif(NOT _expat_config_included)
|
lib/cmake/expat-2.7.1/expat-noconfig.cmake
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#----------------------------------------------------------------
|
| 2 |
+
# Generated CMake target import file for configuration "NoConfig".
|
| 3 |
+
#----------------------------------------------------------------
|
| 4 |
+
|
| 5 |
+
# Commands may need to know the format version.
|
| 6 |
+
set(CMAKE_IMPORT_FILE_VERSION 1)
|
| 7 |
+
|
| 8 |
+
# Import target "expat::expat" for configuration "NoConfig"
|
| 9 |
+
set_property(TARGET expat::expat APPEND PROPERTY IMPORTED_CONFIGURATIONS NOCONFIG)
|
| 10 |
+
set_target_properties(expat::expat PROPERTIES
|
| 11 |
+
IMPORTED_LOCATION_NOCONFIG "${_IMPORT_PREFIX}/lib/libexpat.so.1.10.2"
|
| 12 |
+
IMPORTED_SONAME_NOCONFIG "libexpat.so.1"
|
| 13 |
+
)
|
| 14 |
+
|
| 15 |
+
list(APPEND _cmake_import_check_targets expat::expat )
|
| 16 |
+
list(APPEND _cmake_import_check_files_for_expat::expat "${_IMPORT_PREFIX}/lib/libexpat.so.1.10.2" )
|
| 17 |
+
|
| 18 |
+
# Commands beyond this point should not need to know the version.
|
| 19 |
+
set(CMAKE_IMPORT_FILE_VERSION)
|
lib/cmake/expat-2.7.1/expat.cmake
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Generated by CMake
|
| 2 |
+
|
| 3 |
+
if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" LESS 2.8)
|
| 4 |
+
message(FATAL_ERROR "CMake >= 2.8.12 required")
|
| 5 |
+
endif()
|
| 6 |
+
if(CMAKE_VERSION VERSION_LESS "2.8.12")
|
| 7 |
+
message(FATAL_ERROR "CMake >= 2.8.12 required")
|
| 8 |
+
endif()
|
| 9 |
+
cmake_policy(PUSH)
|
| 10 |
+
cmake_policy(VERSION 2.8.12...3.29)
|
| 11 |
+
#----------------------------------------------------------------
|
| 12 |
+
# Generated CMake target import file.
|
| 13 |
+
#----------------------------------------------------------------
|
| 14 |
+
|
| 15 |
+
# Commands may need to know the format version.
|
| 16 |
+
set(CMAKE_IMPORT_FILE_VERSION 1)
|
| 17 |
+
|
| 18 |
+
# Protect against multiple inclusion, which would fail when already imported targets are added once more.
|
| 19 |
+
set(_cmake_targets_defined "")
|
| 20 |
+
set(_cmake_targets_not_defined "")
|
| 21 |
+
set(_cmake_expected_targets "")
|
| 22 |
+
foreach(_cmake_expected_target IN ITEMS expat::expat)
|
| 23 |
+
list(APPEND _cmake_expected_targets "${_cmake_expected_target}")
|
| 24 |
+
if(TARGET "${_cmake_expected_target}")
|
| 25 |
+
list(APPEND _cmake_targets_defined "${_cmake_expected_target}")
|
| 26 |
+
else()
|
| 27 |
+
list(APPEND _cmake_targets_not_defined "${_cmake_expected_target}")
|
| 28 |
+
endif()
|
| 29 |
+
endforeach()
|
| 30 |
+
unset(_cmake_expected_target)
|
| 31 |
+
if(_cmake_targets_defined STREQUAL _cmake_expected_targets)
|
| 32 |
+
unset(_cmake_targets_defined)
|
| 33 |
+
unset(_cmake_targets_not_defined)
|
| 34 |
+
unset(_cmake_expected_targets)
|
| 35 |
+
unset(CMAKE_IMPORT_FILE_VERSION)
|
| 36 |
+
cmake_policy(POP)
|
| 37 |
+
return()
|
| 38 |
+
endif()
|
| 39 |
+
if(NOT _cmake_targets_defined STREQUAL "")
|
| 40 |
+
string(REPLACE ";" ", " _cmake_targets_defined_text "${_cmake_targets_defined}")
|
| 41 |
+
string(REPLACE ";" ", " _cmake_targets_not_defined_text "${_cmake_targets_not_defined}")
|
| 42 |
+
message(FATAL_ERROR "Some (but not all) targets in this export set were already defined.\nTargets Defined: ${_cmake_targets_defined_text}\nTargets not yet defined: ${_cmake_targets_not_defined_text}\n")
|
| 43 |
+
endif()
|
| 44 |
+
unset(_cmake_targets_defined)
|
| 45 |
+
unset(_cmake_targets_not_defined)
|
| 46 |
+
unset(_cmake_expected_targets)
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
# Compute the installation prefix relative to this file.
|
| 50 |
+
get_filename_component(_IMPORT_PREFIX "${CMAKE_CURRENT_LIST_FILE}" PATH)
|
| 51 |
+
get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH)
|
| 52 |
+
get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH)
|
| 53 |
+
get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH)
|
| 54 |
+
if(_IMPORT_PREFIX STREQUAL "/")
|
| 55 |
+
set(_IMPORT_PREFIX "")
|
| 56 |
+
endif()
|
| 57 |
+
|
| 58 |
+
# Create imported target expat::expat
|
| 59 |
+
add_library(expat::expat SHARED IMPORTED)
|
| 60 |
+
|
| 61 |
+
set_target_properties(expat::expat PROPERTIES
|
| 62 |
+
INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include"
|
| 63 |
+
INTERFACE_LINK_LIBRARIES "m"
|
| 64 |
+
)
|
| 65 |
+
|
| 66 |
+
# Load information for each installed configuration.
|
| 67 |
+
file(GLOB _cmake_config_files "${CMAKE_CURRENT_LIST_DIR}/expat-*.cmake")
|
| 68 |
+
foreach(_cmake_config_file IN LISTS _cmake_config_files)
|
| 69 |
+
include("${_cmake_config_file}")
|
| 70 |
+
endforeach()
|
| 71 |
+
unset(_cmake_config_file)
|
| 72 |
+
unset(_cmake_config_files)
|
| 73 |
+
|
| 74 |
+
# Cleanup temporary variables.
|
| 75 |
+
set(_IMPORT_PREFIX)
|
| 76 |
+
|
| 77 |
+
# Loop over all imported files and verify that they actually exist
|
| 78 |
+
foreach(_cmake_target IN LISTS _cmake_import_check_targets)
|
| 79 |
+
if(CMAKE_VERSION VERSION_LESS "3.28"
|
| 80 |
+
OR NOT DEFINED _cmake_import_check_xcframework_for_${_cmake_target}
|
| 81 |
+
OR NOT IS_DIRECTORY "${_cmake_import_check_xcframework_for_${_cmake_target}}")
|
| 82 |
+
foreach(_cmake_file IN LISTS "_cmake_import_check_files_for_${_cmake_target}")
|
| 83 |
+
if(NOT EXISTS "${_cmake_file}")
|
| 84 |
+
message(FATAL_ERROR "The imported target \"${_cmake_target}\" references the file
|
| 85 |
+
\"${_cmake_file}\"
|
| 86 |
+
but this file does not exist. Possible reasons include:
|
| 87 |
+
* The file was deleted, renamed, or moved to another location.
|
| 88 |
+
* An install or uninstall procedure did not complete successfully.
|
| 89 |
+
* The installation package was faulty and contained
|
| 90 |
+
\"${CMAKE_CURRENT_LIST_FILE}\"
|
| 91 |
+
but not all the files it references.
|
| 92 |
+
")
|
| 93 |
+
endif()
|
| 94 |
+
endforeach()
|
| 95 |
+
endif()
|
| 96 |
+
unset(_cmake_file)
|
| 97 |
+
unset("_cmake_import_check_files_for_${_cmake_target}")
|
| 98 |
+
endforeach()
|
| 99 |
+
unset(_cmake_target)
|
| 100 |
+
unset(_cmake_import_check_targets)
|
| 101 |
+
|
| 102 |
+
# This file does not depend on other imported targets which have
|
| 103 |
+
# been exported from the same project but in a separate export set.
|
| 104 |
+
|
| 105 |
+
# Commands beyond this point should not need to know the version.
|
| 106 |
+
set(CMAKE_IMPORT_FILE_VERSION)
|
| 107 |
+
cmake_policy(POP)
|
lib/cmake/libdeflate/libdeflate-config-version.cmake
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This is a basic version file for the Config-mode of find_package().
|
| 2 |
+
# It is used by write_basic_package_version_file() as input file for configure_file()
|
| 3 |
+
# to create a version-file which can be installed along a config.cmake file.
|
| 4 |
+
#
|
| 5 |
+
# The created file sets PACKAGE_VERSION_EXACT if the current version string and
|
| 6 |
+
# the requested version string are exactly the same and it sets
|
| 7 |
+
# PACKAGE_VERSION_COMPATIBLE if the current version is >= requested version.
|
| 8 |
+
# The variable CVF_VERSION must be set before calling configure_file().
|
| 9 |
+
|
| 10 |
+
set(PACKAGE_VERSION "1.22")
|
| 11 |
+
|
| 12 |
+
if (PACKAGE_FIND_VERSION_RANGE)
|
| 13 |
+
# Package version must be in the requested version range
|
| 14 |
+
if ((PACKAGE_FIND_VERSION_RANGE_MIN STREQUAL "INCLUDE" AND PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION_MIN)
|
| 15 |
+
OR ((PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "INCLUDE" AND PACKAGE_VERSION VERSION_GREATER PACKAGE_FIND_VERSION_MAX)
|
| 16 |
+
OR (PACKAGE_FIND_VERSION_RANGE_MAX STREQUAL "EXCLUDE" AND PACKAGE_VERSION VERSION_GREATER_EQUAL PACKAGE_FIND_VERSION_MAX)))
|
| 17 |
+
set(PACKAGE_VERSION_COMPATIBLE FALSE)
|
| 18 |
+
else()
|
| 19 |
+
set(PACKAGE_VERSION_COMPATIBLE TRUE)
|
| 20 |
+
endif()
|
| 21 |
+
else()
|
| 22 |
+
if(PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION)
|
| 23 |
+
set(PACKAGE_VERSION_COMPATIBLE FALSE)
|
| 24 |
+
else()
|
| 25 |
+
set(PACKAGE_VERSION_COMPATIBLE TRUE)
|
| 26 |
+
if(PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION)
|
| 27 |
+
set(PACKAGE_VERSION_EXACT TRUE)
|
| 28 |
+
endif()
|
| 29 |
+
endif()
|
| 30 |
+
endif()
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
# if the installed or the using project don't have CMAKE_SIZEOF_VOID_P set, ignore it:
|
| 34 |
+
if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "" OR "8" STREQUAL "")
|
| 35 |
+
return()
|
| 36 |
+
endif()
|
| 37 |
+
|
| 38 |
+
# check that the installed version has the same 32/64bit-ness as the one which is currently searching:
|
| 39 |
+
if(NOT CMAKE_SIZEOF_VOID_P STREQUAL "8")
|
| 40 |
+
math(EXPR installedBits "8 * 8")
|
| 41 |
+
set(PACKAGE_VERSION "${PACKAGE_VERSION} (${installedBits}bit)")
|
| 42 |
+
set(PACKAGE_VERSION_UNSUITABLE TRUE)
|
| 43 |
+
endif()
|
lib/cmake/libdeflate/libdeflate-config.cmake
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
####### Expanded from @PACKAGE_INIT@ by configure_package_config_file() #######
|
| 3 |
+
####### Any changes to this file will be overwritten by the next CMake run ####
|
| 4 |
+
####### The input file was libdeflate-config.cmake.in ########
|
| 5 |
+
|
| 6 |
+
get_filename_component(PACKAGE_PREFIX_DIR "${CMAKE_CURRENT_LIST_DIR}/../../../" ABSOLUTE)
|
| 7 |
+
|
| 8 |
+
macro(set_and_check _var _file)
|
| 9 |
+
set(${_var} "${_file}")
|
| 10 |
+
if(NOT EXISTS "${_file}")
|
| 11 |
+
message(FATAL_ERROR "File or directory ${_file} referenced by variable ${_var} does not exist !")
|
| 12 |
+
endif()
|
| 13 |
+
endmacro()
|
| 14 |
+
|
| 15 |
+
macro(check_required_components _NAME)
|
| 16 |
+
foreach(comp ${${_NAME}_FIND_COMPONENTS})
|
| 17 |
+
if(NOT ${_NAME}_${comp}_FOUND)
|
| 18 |
+
if(${_NAME}_FIND_REQUIRED_${comp})
|
| 19 |
+
set(${_NAME}_FOUND FALSE)
|
| 20 |
+
endif()
|
| 21 |
+
endif()
|
| 22 |
+
endforeach()
|
| 23 |
+
endmacro()
|
| 24 |
+
|
| 25 |
+
####################################################################################
|
| 26 |
+
|
| 27 |
+
include("${CMAKE_CURRENT_LIST_DIR}/libdeflate-targets.cmake")
|
lib/cmake/libdeflate/libdeflate-targets-release.cmake
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#----------------------------------------------------------------
|
| 2 |
+
# Generated CMake target import file for configuration "Release".
|
| 3 |
+
#----------------------------------------------------------------
|
| 4 |
+
|
| 5 |
+
# Commands may need to know the format version.
|
| 6 |
+
set(CMAKE_IMPORT_FILE_VERSION 1)
|
| 7 |
+
|
| 8 |
+
# Import target "libdeflate::libdeflate_shared" for configuration "Release"
|
| 9 |
+
set_property(TARGET libdeflate::libdeflate_shared APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
|
| 10 |
+
set_target_properties(libdeflate::libdeflate_shared PROPERTIES
|
| 11 |
+
IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/lib/libdeflate.so.0"
|
| 12 |
+
IMPORTED_SONAME_RELEASE "libdeflate.so.0"
|
| 13 |
+
)
|
| 14 |
+
|
| 15 |
+
list(APPEND _cmake_import_check_targets libdeflate::libdeflate_shared )
|
| 16 |
+
list(APPEND _cmake_import_check_files_for_libdeflate::libdeflate_shared "${_IMPORT_PREFIX}/lib/libdeflate.so.0" )
|
| 17 |
+
|
| 18 |
+
# Commands beyond this point should not need to know the version.
|
| 19 |
+
set(CMAKE_IMPORT_FILE_VERSION)
|
lib/itcl4.2.4/itcl.tcl
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#
|
| 2 |
+
# itcl.tcl
|
| 3 |
+
# ----------------------------------------------------------------------
|
| 4 |
+
# Invoked automatically upon startup to customize the interpreter
|
| 5 |
+
# for [incr Tcl].
|
| 6 |
+
# ----------------------------------------------------------------------
|
| 7 |
+
# AUTHOR: Michael J. McLennan
|
| 8 |
+
# Bell Labs Innovations for Lucent Technologies
|
| 9 |
+
# mmclennan@lucent.com
|
| 10 |
+
# http://www.tcltk.com/itcl
|
| 11 |
+
# ----------------------------------------------------------------------
|
| 12 |
+
# Copyright (c) 1993-1998 Lucent Technologies, Inc.
|
| 13 |
+
# ======================================================================
|
| 14 |
+
# See the file "license.terms" for information on usage and
|
| 15 |
+
# redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
| 16 |
+
|
| 17 |
+
proc ::itcl::delete_helper { name args } {
|
| 18 |
+
::itcl::delete object $name
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
# ----------------------------------------------------------------------
|
| 22 |
+
# USAGE: local <className> <objName> ?<arg> <arg>...?
|
| 23 |
+
#
|
| 24 |
+
# Creates a new object called <objName> in class <className>, passing
|
| 25 |
+
# the remaining <arg>'s to the constructor. Unlike the usual
|
| 26 |
+
# [incr Tcl] objects, however, an object created by this procedure
|
| 27 |
+
# will be automatically deleted when the local call frame is destroyed.
|
| 28 |
+
# This command is useful for creating objects that should only remain
|
| 29 |
+
# alive until a procedure exits.
|
| 30 |
+
# ----------------------------------------------------------------------
|
| 31 |
+
proc ::itcl::local {class name args} {
|
| 32 |
+
set ptr [uplevel [list $class $name] $args]
|
| 33 |
+
uplevel [list set itcl-local-$ptr $ptr]
|
| 34 |
+
set cmd [uplevel namespace which -command $ptr]
|
| 35 |
+
uplevel [list trace add variable itcl-local-$ptr unset \
|
| 36 |
+
"::itcl::delete_helper $cmd"]
|
| 37 |
+
return $ptr
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
# ----------------------------------------------------------------------
|
| 41 |
+
# auto_mkindex
|
| 42 |
+
# ----------------------------------------------------------------------
|
| 43 |
+
# Define Itcl commands that will be recognized by the auto_mkindex
|
| 44 |
+
# parser in Tcl...
|
| 45 |
+
#
|
| 46 |
+
|
| 47 |
+
#
|
| 48 |
+
# USAGE: itcl::class name body
|
| 49 |
+
# Adds an entry for the given class declaration.
|
| 50 |
+
#
|
| 51 |
+
foreach __cmd {itcl::class class itcl::type type ictl::widget widget itcl::widgetadaptor widgetadaptor itcl::extendedclass extendedclass} {
|
| 52 |
+
auto_mkindex_parser::command $__cmd {name body} {
|
| 53 |
+
variable index
|
| 54 |
+
variable scriptFile
|
| 55 |
+
append index "set [list auto_index([fullname $name])]"
|
| 56 |
+
append index " \[list source \[file join \$dir [list $scriptFile]\]\]\n"
|
| 57 |
+
|
| 58 |
+
variable parser
|
| 59 |
+
variable contextStack
|
| 60 |
+
set contextStack [linsert $contextStack 0 $name]
|
| 61 |
+
$parser eval $body
|
| 62 |
+
set contextStack [lrange $contextStack 1 end]
|
| 63 |
+
}
|
| 64 |
+
}
|
| 65 |
+
|
| 66 |
+
#
|
| 67 |
+
# USAGE: itcl::body name arglist body
|
| 68 |
+
# Adds an entry for the given method/proc body.
|
| 69 |
+
#
|
| 70 |
+
foreach __cmd {itcl::body body} {
|
| 71 |
+
auto_mkindex_parser::command $__cmd {name arglist body} {
|
| 72 |
+
variable index
|
| 73 |
+
variable scriptFile
|
| 74 |
+
append index "set [list auto_index([fullname $name])]"
|
| 75 |
+
append index " \[list source \[file join \$dir [list $scriptFile]\]\]\n"
|
| 76 |
+
}
|
| 77 |
+
}
|
| 78 |
+
|
| 79 |
+
#
|
| 80 |
+
# USAGE: itcl::configbody name arglist body
|
| 81 |
+
# Adds an entry for the given method/proc body.
|
| 82 |
+
#
|
| 83 |
+
foreach __cmd {itcl::configbody configbody} {
|
| 84 |
+
auto_mkindex_parser::command $__cmd {name body} {
|
| 85 |
+
variable index
|
| 86 |
+
variable scriptFile
|
| 87 |
+
append index "set [list auto_index([fullname $name])]"
|
| 88 |
+
append index " \[list source \[file join \$dir [list $scriptFile]\]\]\n"
|
| 89 |
+
}
|
| 90 |
+
}
|
| 91 |
+
|
| 92 |
+
#
|
| 93 |
+
# USAGE: ensemble name ?body?
|
| 94 |
+
# Adds an entry to the auto index list for the given ensemble name.
|
| 95 |
+
#
|
| 96 |
+
foreach __cmd {itcl::ensemble ensemble} {
|
| 97 |
+
auto_mkindex_parser::command $__cmd {name {body ""}} {
|
| 98 |
+
variable index
|
| 99 |
+
variable scriptFile
|
| 100 |
+
append index "set [list auto_index([fullname $name])]"
|
| 101 |
+
append index " \[list source \[file join \$dir [list $scriptFile]\]\]\n"
|
| 102 |
+
}
|
| 103 |
+
}
|
| 104 |
+
|
| 105 |
+
#
|
| 106 |
+
# USAGE: public arg ?arg arg...?
|
| 107 |
+
# protected arg ?arg arg...?
|
| 108 |
+
# private arg ?arg arg...?
|
| 109 |
+
#
|
| 110 |
+
# Evaluates the arguments as commands, so we can recognize proc
|
| 111 |
+
# declarations within classes.
|
| 112 |
+
#
|
| 113 |
+
foreach __cmd {public protected private} {
|
| 114 |
+
auto_mkindex_parser::command $__cmd {args} {
|
| 115 |
+
variable parser
|
| 116 |
+
$parser eval $args
|
| 117 |
+
}
|
| 118 |
+
}
|
| 119 |
+
|
| 120 |
+
# SF bug #246 unset variable __cmd to avoid problems in user programs!!
|
| 121 |
+
unset __cmd
|
| 122 |
+
|
| 123 |
+
# ----------------------------------------------------------------------
|
| 124 |
+
# auto_import
|
| 125 |
+
# ----------------------------------------------------------------------
|
| 126 |
+
# This procedure overrides the usual "auto_import" function in the
|
| 127 |
+
# Tcl library. It is invoked during "namespace import" to make see
|
| 128 |
+
# if the imported commands reside in an autoloaded library. If so,
|
| 129 |
+
# stubs are created to represent the commands. Executing a stub
|
| 130 |
+
# later on causes the real implementation to be autoloaded.
|
| 131 |
+
#
|
| 132 |
+
# Arguments -
|
| 133 |
+
# pattern The pattern of commands being imported (like "foo::*")
|
| 134 |
+
# a canonical namespace as returned by [namespace current]
|
| 135 |
+
|
| 136 |
+
proc auto_import {pattern} {
|
| 137 |
+
global auto_index
|
| 138 |
+
|
| 139 |
+
set ns [uplevel namespace current]
|
| 140 |
+
set patternList [auto_qualify $pattern $ns]
|
| 141 |
+
|
| 142 |
+
auto_load_index
|
| 143 |
+
|
| 144 |
+
foreach pattern $patternList {
|
| 145 |
+
foreach name [array names auto_index $pattern] {
|
| 146 |
+
if {"" == [info commands $name]} {
|
| 147 |
+
::itcl::import::stub create $name
|
| 148 |
+
}
|
| 149 |
+
}
|
| 150 |
+
}
|
| 151 |
+
}
|
lib/itcl4.2.4/itclConfig.sh
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# itclConfig.sh --
|
| 2 |
+
#
|
| 3 |
+
# This shell script (for sh) is generated automatically by Itcl's
|
| 4 |
+
# configure script. It will create shell variables for most of
|
| 5 |
+
# the configuration options discovered by the configure script.
|
| 6 |
+
# This script is intended to be included by the configure scripts
|
| 7 |
+
# for Itcl extensions so that they don't have to figure this all
|
| 8 |
+
# out for themselves. This file does not duplicate information
|
| 9 |
+
# already provided by tclConfig.sh, so you may need to use that
|
| 10 |
+
# file in addition to this one.
|
| 11 |
+
#
|
| 12 |
+
# The information in this file is specific to a single platform.
|
| 13 |
+
|
| 14 |
+
# Itcl's version number.
|
| 15 |
+
itcl_VERSION='4.2.4'
|
| 16 |
+
ITCL_VERSION='4.2.4'
|
| 17 |
+
|
| 18 |
+
# The name of the Itcl library (may be either a .a file or a shared library):
|
| 19 |
+
itcl_LIB_FILE=libitcl4.2.4.so
|
| 20 |
+
ITCL_LIB_FILE=libitcl4.2.4.so
|
| 21 |
+
|
| 22 |
+
# String to pass to linker to pick up the Itcl library from its
|
| 23 |
+
# build directory.
|
| 24 |
+
itcl_BUILD_LIB_SPEC='-L/croot/tk_1748849386456/work/tcl8.6.14/unix/pkgs/itcl4.2.4 -litcl4.2.4'
|
| 25 |
+
ITCL_BUILD_LIB_SPEC='-L/croot/tk_1748849386456/work/tcl8.6.14/unix/pkgs/itcl4.2.4 -litcl4.2.4'
|
| 26 |
+
|
| 27 |
+
# String to pass to linker to pick up the Itcl library from its
|
| 28 |
+
# installed directory.
|
| 29 |
+
itcl_LIB_SPEC='-L/home/aioz-nghiale/anaconda3/envs/testing_softzoo_pointe/lib/itcl4.2.4 -litcl4.2.4'
|
| 30 |
+
ITCL_LIB_SPEC='-L/home/aioz-nghiale/anaconda3/envs/testing_softzoo_pointe/lib/itcl4.2.4 -litcl4.2.4'
|
| 31 |
+
|
| 32 |
+
# The name of the Itcl stub library (a .a file):
|
| 33 |
+
itcl_STUB_LIB_FILE=libitclstub4.2.4.a
|
| 34 |
+
ITCL_STUB_LIB_FILE=libitclstub4.2.4.a
|
| 35 |
+
|
| 36 |
+
# String to pass to linker to pick up the Itcl stub library from its
|
| 37 |
+
# build directory.
|
| 38 |
+
itcl_BUILD_STUB_LIB_SPEC='-L/croot/tk_1748849386456/work/tcl8.6.14/unix/pkgs/itcl4.2.4 -litclstub4.2.4'
|
| 39 |
+
ITCL_BUILD_STUB_LIB_SPEC='-L/croot/tk_1748849386456/work/tcl8.6.14/unix/pkgs/itcl4.2.4 -litclstub4.2.4'
|
| 40 |
+
|
| 41 |
+
# String to pass to linker to pick up the Itcl stub library from its
|
| 42 |
+
# installed directory.
|
| 43 |
+
itcl_STUB_LIB_SPEC='-L/home/aioz-nghiale/anaconda3/envs/testing_softzoo_pointe/lib/itcl4.2.4 -litclstub4.2.4'
|
| 44 |
+
ITCL_STUB_LIB_SPEC='-L/home/aioz-nghiale/anaconda3/envs/testing_softzoo_pointe/lib/itcl4.2.4 -litclstub4.2.4'
|
| 45 |
+
|
| 46 |
+
# String to pass to linker to pick up the Itcl stub library from its
|
| 47 |
+
# build directory.
|
| 48 |
+
itcl_BUILD_STUB_LIB_PATH='/croot/tk_1748849386456/work/tcl8.6.14/unix/pkgs/itcl4.2.4/libitclstub4.2.4.a'
|
| 49 |
+
ITCL_BUILD_STUB_LIB_PATH='/croot/tk_1748849386456/work/tcl8.6.14/unix/pkgs/itcl4.2.4/libitclstub4.2.4.a'
|
| 50 |
+
|
| 51 |
+
# String to pass to linker to pick up the Itcl stub library from its
|
| 52 |
+
# installed directory.
|
| 53 |
+
itcl_STUB_LIB_PATH='/home/aioz-nghiale/anaconda3/envs/testing_softzoo_pointe/lib/itcl4.2.4/libitclstub4.2.4.a'
|
| 54 |
+
ITCL_STUB_LIB_PATH='/home/aioz-nghiale/anaconda3/envs/testing_softzoo_pointe/lib/itcl4.2.4/libitclstub4.2.4.a'
|
| 55 |
+
|
| 56 |
+
# Location of the top-level source directories from which [incr Tcl]
|
| 57 |
+
# was built. This is the directory that contains generic, unix, etc.
|
| 58 |
+
# If [incr Tcl] was compiled in a different place than the directory
|
| 59 |
+
# containing the source files, this points to the location of the sources,
|
| 60 |
+
# not the location where [incr Tcl] was compiled.
|
| 61 |
+
itcl_SRC_DIR='/croot/tk_1748849386456/work/tcl8.6.14/pkgs/itcl4.2.4'
|
| 62 |
+
ITCL_SRC_DIR='/croot/tk_1748849386456/work/tcl8.6.14/pkgs/itcl4.2.4'
|
| 63 |
+
|
| 64 |
+
# String to pass to the compiler so that an extension can
|
| 65 |
+
# find installed Itcl headers.
|
| 66 |
+
itcl_INCLUDE_SPEC=''
|
| 67 |
+
ITCL_INCLUDE_SPEC=''
|
lib/itcl4.2.4/itclHullCmds.tcl
ADDED
|
@@ -0,0 +1,562 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#
|
| 2 |
+
# itclHullCmds.tcl
|
| 3 |
+
# ----------------------------------------------------------------------
|
| 4 |
+
# Invoked automatically upon startup to customize the interpreter
|
| 5 |
+
# for [incr Tcl] when one of setupcomponent or createhull is called.
|
| 6 |
+
# ----------------------------------------------------------------------
|
| 7 |
+
# AUTHOR: Arnulf P. Wiedemann
|
| 8 |
+
#
|
| 9 |
+
# ----------------------------------------------------------------------
|
| 10 |
+
# Copyright (c) 2008 Arnulf P. Wiedemann
|
| 11 |
+
# ======================================================================
|
| 12 |
+
# See the file "license.terms" for information on usage and
|
| 13 |
+
# redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
| 14 |
+
|
| 15 |
+
package require Tk 8.6 9
|
| 16 |
+
|
| 17 |
+
namespace eval ::itcl::internal::commands {
|
| 18 |
+
|
| 19 |
+
# ======================= widgetDeleted ===========================
|
| 20 |
+
|
| 21 |
+
proc widgetDeleted {oldName newName op} {
|
| 22 |
+
# The widget is beeing deleted, so we have to delete the object
|
| 23 |
+
# which had the widget as itcl_hull too!
|
| 24 |
+
# We have to get the real name from for example
|
| 25 |
+
# ::itcl::internal::widgets::hull1.lw
|
| 26 |
+
# we need only .lw here
|
| 27 |
+
|
| 28 |
+
#puts stderr "widgetDeleted!$oldName!$newName!$op!"
|
| 29 |
+
set cmdName [namespace tail $oldName]
|
| 30 |
+
set flds [split $cmdName {.}]
|
| 31 |
+
set cmdName .[join [lrange $flds 1 end] {.}]
|
| 32 |
+
#puts stderr "DELWIDGET![namespace current]!$cmdName![::info command $cmdName]!"
|
| 33 |
+
rename $cmdName {}
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
namespace eval ::itcl::builtin {
|
| 39 |
+
|
| 40 |
+
# ======================= createhull ===========================
|
| 41 |
+
# the hull widget is a tk widget which is the (mega) widget handled behind the itcl
|
| 42 |
+
# extendedclass/itcl widget.
|
| 43 |
+
# It is created be renaming the itcl class object to a temporary name <itcl object name>_
|
| 44 |
+
# creating the widget with the
|
| 45 |
+
# appropriate options and the installing that as the "hull" widget (the container)
|
| 46 |
+
# All the options in args and the options delegated to component itcl_hull are used
|
| 47 |
+
# Then a unique name (hull_widget_name) in the itcl namespace is created for widget:
|
| 48 |
+
# ::itcl::internal::widgets::hull<unique number><namespace tail path>
|
| 49 |
+
# and widget is renamed to that name
|
| 50 |
+
# Finally the <itcl object name>_ is renamed to the original <itcl object name> again
|
| 51 |
+
# Component itcl_hull is created if not existent
|
| 52 |
+
# itcl_hull is set to the hull_widget_name and the <itcl object name>
|
| 53 |
+
# is returned to the caller
|
| 54 |
+
# ==============================================================
|
| 55 |
+
|
| 56 |
+
proc createhull {widget_type path args} {
|
| 57 |
+
variable hullCount
|
| 58 |
+
upvar this this
|
| 59 |
+
upvar win win
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
#puts stderr "il-1![::info level -1]!$this!"
|
| 63 |
+
#puts stderr "createhull!$widget_type!$path!$args!$this![::info command $this]!"
|
| 64 |
+
#puts stderr "ns1![uplevel 1 namespace current]!"
|
| 65 |
+
#puts stderr "ns2![uplevel 2 namespace current]!"
|
| 66 |
+
#puts stderr "ns3![uplevel 3 namespace current]!"
|
| 67 |
+
#puts stderr "level-1![::info level -1]!"
|
| 68 |
+
#puts stderr "level-2![::info level -2]!"
|
| 69 |
+
# set my_this [namespace tail $this]
|
| 70 |
+
set my_this $this
|
| 71 |
+
set tmp $my_this
|
| 72 |
+
#puts stderr "II![::info command $this]![::info command $tmp]!"
|
| 73 |
+
#puts stderr "rename1!rename $my_this ${tmp}_!"
|
| 74 |
+
rename ::$my_this ${tmp}_
|
| 75 |
+
set options [list]
|
| 76 |
+
foreach {option_name value} $args {
|
| 77 |
+
switch -glob -- $option_name {
|
| 78 |
+
-class {
|
| 79 |
+
lappend options $option_name [namespace tail $value]
|
| 80 |
+
}
|
| 81 |
+
-* {
|
| 82 |
+
lappend options $option_name $value
|
| 83 |
+
}
|
| 84 |
+
default {
|
| 85 |
+
return -code error "bad option name\"$option_name\" options must start with a \"-\""
|
| 86 |
+
}
|
| 87 |
+
}
|
| 88 |
+
}
|
| 89 |
+
set my_win [namespace tail $path]
|
| 90 |
+
set cmd [list $widget_type $my_win]
|
| 91 |
+
#puts stderr "my_win!$my_win!cmd!$cmd!$path!"
|
| 92 |
+
if {[llength $options] > 0} {
|
| 93 |
+
lappend cmd {*}$options
|
| 94 |
+
}
|
| 95 |
+
set widget [uplevel 1 $cmd]
|
| 96 |
+
#puts stderr "widget!$widget!"
|
| 97 |
+
trace add command $widget delete ::itcl::internal::commands::widgetDeleted
|
| 98 |
+
set opts [uplevel 1 info delegated options]
|
| 99 |
+
foreach entry $opts {
|
| 100 |
+
foreach {optName compName} $entry break
|
| 101 |
+
if {$compName eq "itcl_hull"} {
|
| 102 |
+
set optInfos [uplevel 1 info delegated option $optName]
|
| 103 |
+
set realOptName [lindex $optInfos 4]
|
| 104 |
+
# strip off the "-" at the beginning
|
| 105 |
+
set myOptName [string range $realOptName 1 end]
|
| 106 |
+
set my_opt_val [option get $my_win $myOptName *]
|
| 107 |
+
if {$my_opt_val ne ""} {
|
| 108 |
+
$my_win configure -$myOptName $my_opt_val
|
| 109 |
+
}
|
| 110 |
+
}
|
| 111 |
+
}
|
| 112 |
+
set idx 1
|
| 113 |
+
while {1} {
|
| 114 |
+
set widgetName ::itcl::internal::widgets::hull${idx}$my_win
|
| 115 |
+
#puts stderr "widgetName!$widgetName!"
|
| 116 |
+
if {[string length [::info command $widgetName]] == 0} {
|
| 117 |
+
break
|
| 118 |
+
}
|
| 119 |
+
incr idx
|
| 120 |
+
}
|
| 121 |
+
#puts stderr "rename2!rename $widget $widgetName!"
|
| 122 |
+
set dorename 0
|
| 123 |
+
rename $widget $widgetName
|
| 124 |
+
#puts stderr "rename3!rename ${tmp}_ $tmp![::info command ${tmp}_]!my_this!$my_this!"
|
| 125 |
+
rename ${tmp}_ ::$tmp
|
| 126 |
+
set exists [uplevel 1 ::info exists itcl_hull]
|
| 127 |
+
if {!$exists} {
|
| 128 |
+
# that does not yet work, beacause of problems with resolving
|
| 129 |
+
::itcl::addcomponent $my_this itcl_hull
|
| 130 |
+
}
|
| 131 |
+
upvar itcl_hull itcl_hull
|
| 132 |
+
::itcl::setcomponent $my_this itcl_hull $widgetName
|
| 133 |
+
#puts stderr "IC![::info command $my_win]!"
|
| 134 |
+
set exists [uplevel 1 ::info exists itcl_interior]
|
| 135 |
+
if {!$exists} {
|
| 136 |
+
# that does not yet work, beacause of problems with resolving
|
| 137 |
+
::itcl::addcomponent $this itcl_interior
|
| 138 |
+
}
|
| 139 |
+
upvar itcl_interior itcl_interior
|
| 140 |
+
set itcl_interior $my_win
|
| 141 |
+
#puts stderr "hull end!win!$win!itcl_hull!$itcl_hull!itcl_interior!$itcl_interior!"
|
| 142 |
+
return $my_win
|
| 143 |
+
}
|
| 144 |
+
|
| 145 |
+
# ======================= addToItclOptions ===========================
|
| 146 |
+
|
| 147 |
+
proc addToItclOptions {my_class my_win myOptions argsDict} {
|
| 148 |
+
upvar win win
|
| 149 |
+
upvar itcl_hull itcl_hull
|
| 150 |
+
|
| 151 |
+
set opt_lst [list configure]
|
| 152 |
+
foreach opt [lsort $myOptions] {
|
| 153 |
+
#puts stderr "IOPT!$opt!$my_class!$my_win![::itcl::is class $my_class]!"
|
| 154 |
+
set isClass [::itcl::is class $my_class]
|
| 155 |
+
set found 0
|
| 156 |
+
if {$isClass} {
|
| 157 |
+
if {[catch {
|
| 158 |
+
set resource [namespace eval $my_class info option $opt -resource]
|
| 159 |
+
set class [namespace eval $my_class info option $opt -class]
|
| 160 |
+
set default_val [uplevel 2 info option $opt -default]
|
| 161 |
+
set found 1
|
| 162 |
+
} msg]} {
|
| 163 |
+
# puts stderr "MSG!$opt!$my_class!$msg!"
|
| 164 |
+
}
|
| 165 |
+
} else {
|
| 166 |
+
set tmp_win [uplevel #0 $my_class .___xx]
|
| 167 |
+
|
| 168 |
+
set my_info [$tmp_win configure $opt]
|
| 169 |
+
set resource [lindex $my_info 1]
|
| 170 |
+
set class [lindex $my_info 2]
|
| 171 |
+
set default_val [lindex $my_info 3]
|
| 172 |
+
uplevel #0 destroy $tmp_win
|
| 173 |
+
set found 1
|
| 174 |
+
}
|
| 175 |
+
if {$found} {
|
| 176 |
+
if {[catch {
|
| 177 |
+
set val [uplevel #0 ::option get $win $resource $class]
|
| 178 |
+
} msg]} {
|
| 179 |
+
set val ""
|
| 180 |
+
}
|
| 181 |
+
if {[::dict exists $argsDict $opt]} {
|
| 182 |
+
# we have an explicitly set option
|
| 183 |
+
set val [::dict get $argsDict $opt]
|
| 184 |
+
} else {
|
| 185 |
+
if {[string length $val] == 0} {
|
| 186 |
+
set val $default_val
|
| 187 |
+
}
|
| 188 |
+
}
|
| 189 |
+
set ::itcl::internal::variables::${my_win}::itcl_options($opt) $val
|
| 190 |
+
set ::itcl::internal::variables::${my_win}::__itcl_option_infos($opt) [list $resource $class $default_val]
|
| 191 |
+
#puts stderr "OPT1!$opt!$val!"
|
| 192 |
+
# uplevel 1 [list set itcl_options($opt) [list $val]]
|
| 193 |
+
if {[catch {uplevel 1 $win configure $opt [list $val]} msg]} {
|
| 194 |
+
#puts stderr "addToItclOptions ERR!$msg!$my_class!$win!configure!$opt!$val!"
|
| 195 |
+
}
|
| 196 |
+
}
|
| 197 |
+
}
|
| 198 |
+
}
|
| 199 |
+
|
| 200 |
+
# ======================= setupcomponent ===========================
|
| 201 |
+
|
| 202 |
+
proc setupcomponent {comp using widget_type path args} {
|
| 203 |
+
upvar this this
|
| 204 |
+
upvar win win
|
| 205 |
+
upvar itcl_hull itcl_hull
|
| 206 |
+
|
| 207 |
+
#puts stderr "setupcomponent!$comp!$widget_type!$path!$args!$this!$win!$itcl_hull!"
|
| 208 |
+
#puts stderr "CONT![uplevel 1 info context]!"
|
| 209 |
+
#puts stderr "ns1![uplevel 1 namespace current]!"
|
| 210 |
+
#puts stderr "ns2![uplevel 2 namespace current]!"
|
| 211 |
+
#puts stderr "ns3![uplevel 3 namespace current]!"
|
| 212 |
+
set my_comp_object [lindex [uplevel 1 info context] 1]
|
| 213 |
+
if {[::info exists ::itcl::internal::component_objects($my_comp_object)]} {
|
| 214 |
+
set my_comp_object [set ::itcl::internal::component_objects($my_comp_object)]
|
| 215 |
+
} else {
|
| 216 |
+
set ::itcl::internal::component_objects($path) $my_comp_object
|
| 217 |
+
}
|
| 218 |
+
set options [list]
|
| 219 |
+
foreach {option_name value} $args {
|
| 220 |
+
switch -glob -- $option_name {
|
| 221 |
+
-* {
|
| 222 |
+
lappend options $option_name $value
|
| 223 |
+
}
|
| 224 |
+
default {
|
| 225 |
+
return -code error "bad option name\"$option_name\" options must start with a \"-\""
|
| 226 |
+
}
|
| 227 |
+
}
|
| 228 |
+
}
|
| 229 |
+
if {[llength $args]} {
|
| 230 |
+
set argsDict [dict create {*}$args]
|
| 231 |
+
} else {
|
| 232 |
+
set argsDict [dict create]
|
| 233 |
+
}
|
| 234 |
+
set cmd [list $widget_type $path]
|
| 235 |
+
if {[llength $options] > 0} {
|
| 236 |
+
lappend cmd {*}$options
|
| 237 |
+
}
|
| 238 |
+
#puts stderr "cmd0![::info command $widget_type]!$path![::info command $path]!"
|
| 239 |
+
#puts stderr "cmd1!$cmd!"
|
| 240 |
+
# set my_comp [uplevel 3 $cmd]
|
| 241 |
+
set my_comp [uplevel #0 $cmd]
|
| 242 |
+
#puts stderr 111![::info command $path]!
|
| 243 |
+
::itcl::setcomponent $this $comp $my_comp
|
| 244 |
+
set opts [uplevel 1 info delegated options]
|
| 245 |
+
foreach entry $opts {
|
| 246 |
+
foreach {optName compName} $entry break
|
| 247 |
+
if {$compName eq $my_comp} {
|
| 248 |
+
set optInfos [uplevel 1 info delegated option $optName]
|
| 249 |
+
set realOptName [lindex $optInfos 4]
|
| 250 |
+
# strip off the "-" at the beginning
|
| 251 |
+
set myOptName [string range $realOptName 1 end]
|
| 252 |
+
set my_opt_val [option get $my_win $myOptName *]
|
| 253 |
+
if {$my_opt_val ne ""} {
|
| 254 |
+
$my_comp configure -$myOptName $my_opt_val
|
| 255 |
+
}
|
| 256 |
+
}
|
| 257 |
+
}
|
| 258 |
+
set my_class $widget_type
|
| 259 |
+
set my_parent_class [uplevel 1 namespace current]
|
| 260 |
+
if {[catch {
|
| 261 |
+
set myOptions [namespace eval $my_class {info classoptions}]
|
| 262 |
+
} msg]} {
|
| 263 |
+
set myOptions [list]
|
| 264 |
+
}
|
| 265 |
+
foreach entry [$path configure] {
|
| 266 |
+
foreach {opt dummy1 dummy2 dummy3} $entry break
|
| 267 |
+
lappend myOptions $opt
|
| 268 |
+
}
|
| 269 |
+
#puts stderr "OPTS!$myOptions!"
|
| 270 |
+
addToItclOptions $widget_type $my_comp_object $myOptions $argsDict
|
| 271 |
+
#puts stderr END!$path![::info command $path]!
|
| 272 |
+
}
|
| 273 |
+
|
| 274 |
+
proc itcl_initoptions {args} {
|
| 275 |
+
puts stderr "ITCL_INITOPT!$args!"
|
| 276 |
+
}
|
| 277 |
+
|
| 278 |
+
# ======================= initoptions ===========================
|
| 279 |
+
|
| 280 |
+
proc initoptions {args} {
|
| 281 |
+
upvar win win
|
| 282 |
+
upvar itcl_hull itcl_hull
|
| 283 |
+
upvar itcl_option_components itcl_option_components
|
| 284 |
+
|
| 285 |
+
#puts stderr "INITOPT!!$win!"
|
| 286 |
+
if {[llength $args]} {
|
| 287 |
+
set argsDict [dict create {*}$args]
|
| 288 |
+
} else {
|
| 289 |
+
set argsDict [dict create]
|
| 290 |
+
}
|
| 291 |
+
set my_class [uplevel 1 namespace current]
|
| 292 |
+
set myOptions [namespace eval $my_class {info classoptions}]
|
| 293 |
+
if {[dict exists $::itcl::internal::dicts::classComponents $my_class]} {
|
| 294 |
+
set class_info_dict [dict get $::itcl::internal::dicts::classComponents $my_class]
|
| 295 |
+
# set myOptions [lsort -unique [namespace eval $my_class {info options}]]
|
| 296 |
+
foreach comp [uplevel 1 info components] {
|
| 297 |
+
if {[dict exists $class_info_dict $comp -keptoptions]} {
|
| 298 |
+
foreach my_opt [dict get $class_info_dict $comp -keptoptions] {
|
| 299 |
+
if {[lsearch $myOptions $my_opt] < 0} {
|
| 300 |
+
#puts stderr "KEOPT!$my_opt!"
|
| 301 |
+
lappend myOptions $my_opt
|
| 302 |
+
}
|
| 303 |
+
}
|
| 304 |
+
}
|
| 305 |
+
}
|
| 306 |
+
} else {
|
| 307 |
+
set class_info_dict [list]
|
| 308 |
+
}
|
| 309 |
+
#puts stderr "OPTS!$win!$my_class![join [lsort $myOptions]] \n]!"
|
| 310 |
+
set opt_lst [list configure]
|
| 311 |
+
set my_win $win
|
| 312 |
+
foreach opt [lsort $myOptions] {
|
| 313 |
+
set found 0
|
| 314 |
+
if {[catch {
|
| 315 |
+
set resource [uplevel 1 info option $opt -resource]
|
| 316 |
+
set class [uplevel 1 info option $opt -class]
|
| 317 |
+
set default_val [uplevel 1 info option $opt -default]
|
| 318 |
+
set found 1
|
| 319 |
+
} msg]} {
|
| 320 |
+
# puts stderr "MSG!$opt!$msg!"
|
| 321 |
+
}
|
| 322 |
+
#puts stderr "OPT!$opt!$found!"
|
| 323 |
+
if {$found} {
|
| 324 |
+
if {[catch {
|
| 325 |
+
set val [uplevel #0 ::option get $my_win $resource $class]
|
| 326 |
+
} msg]} {
|
| 327 |
+
set val ""
|
| 328 |
+
}
|
| 329 |
+
if {[::dict exists $argsDict $opt]} {
|
| 330 |
+
# we have an explicitly set option
|
| 331 |
+
set val [::dict get $argsDict $opt]
|
| 332 |
+
} else {
|
| 333 |
+
if {[string length $val] == 0} {
|
| 334 |
+
set val $default_val
|
| 335 |
+
}
|
| 336 |
+
}
|
| 337 |
+
set ::itcl::internal::variables::${win}::itcl_options($opt) $val
|
| 338 |
+
set ::itcl::internal::variables::${win}::__itcl_option_infos($opt) [list $resource $class $default_val]
|
| 339 |
+
#puts stderr "OPT1!$opt!$val!"
|
| 340 |
+
# uplevel 1 [list set itcl_options($opt) [list $val]]
|
| 341 |
+
if {[catch {uplevel 1 $my_win configure $opt [list $val]} msg]} {
|
| 342 |
+
puts stderr "initoptions ERR!$msg!$my_class!$my_win!configure!$opt!$val!"
|
| 343 |
+
}
|
| 344 |
+
}
|
| 345 |
+
foreach comp [dict keys $class_info_dict] {
|
| 346 |
+
#puts stderr "OPT1!$opt!$comp![dict get $class_info_dict $comp]!"
|
| 347 |
+
if {[dict exists $class_info_dict $comp -keptoptions]} {
|
| 348 |
+
if {[lsearch [dict get $class_info_dict $comp -keptoptions] $opt] >= 0} {
|
| 349 |
+
if {$found == 0} {
|
| 350 |
+
# we use the option value of the first component for setting
|
| 351 |
+
# the option, as the components are traversed in the dict
|
| 352 |
+
# depending on the ordering of the component creation!!
|
| 353 |
+
set my_info [uplevel 1 \[set $comp\] configure $opt]
|
| 354 |
+
set resource [lindex $my_info 1]
|
| 355 |
+
set class [lindex $my_info 2]
|
| 356 |
+
set default_val [lindex $my_info 3]
|
| 357 |
+
set found 2
|
| 358 |
+
set val [uplevel #0 ::option get $my_win $resource $class]
|
| 359 |
+
if {[::dict exists $argsDict $opt]} {
|
| 360 |
+
# we have an explicitly set option
|
| 361 |
+
set val [::dict get $argsDict $opt]
|
| 362 |
+
} else {
|
| 363 |
+
if {[string length $val] == 0} {
|
| 364 |
+
set val $default_val
|
| 365 |
+
}
|
| 366 |
+
}
|
| 367 |
+
#puts stderr "OPT2!$opt!$val!"
|
| 368 |
+
set ::itcl::internal::variables::${win}::itcl_options($opt) $val
|
| 369 |
+
set ::itcl::internal::variables::${win}::__itcl_option_infos($opt) [list $resource $class $default_val]
|
| 370 |
+
# uplevel 1 [list set itcl_options($opt) [list $val]]
|
| 371 |
+
}
|
| 372 |
+
if {[catch {uplevel 1 \[set $comp\] configure $opt [list $val]} msg]} {
|
| 373 |
+
puts stderr "initoptions ERR2!$msg!$my_class!$comp!configure!$opt!$val!"
|
| 374 |
+
}
|
| 375 |
+
if {![uplevel 1 info exists itcl_option_components($opt)]} {
|
| 376 |
+
set itcl_option_components($opt) [list]
|
| 377 |
+
}
|
| 378 |
+
if {[lsearch [set itcl_option_components($opt)] $comp] < 0} {
|
| 379 |
+
if {![catch {
|
| 380 |
+
set optval [uplevel 1 [list set itcl_options($opt)]]
|
| 381 |
+
} msg3]} {
|
| 382 |
+
uplevel 1 \[set $comp\] configure $opt $optval
|
| 383 |
+
}
|
| 384 |
+
lappend itcl_option_components($opt) $comp
|
| 385 |
+
}
|
| 386 |
+
}
|
| 387 |
+
}
|
| 388 |
+
}
|
| 389 |
+
}
|
| 390 |
+
# uplevel 1 $opt_lst
|
| 391 |
+
}
|
| 392 |
+
|
| 393 |
+
# ======================= setoptions ===========================
|
| 394 |
+
|
| 395 |
+
proc setoptions {args} {
|
| 396 |
+
|
| 397 |
+
#puts stderr "setOPT!!$args!"
|
| 398 |
+
if {[llength $args]} {
|
| 399 |
+
set argsDict [dict create {*}$args]
|
| 400 |
+
} else {
|
| 401 |
+
set argsDict [dict create]
|
| 402 |
+
}
|
| 403 |
+
set my_class [uplevel 1 namespace current]
|
| 404 |
+
set myOptions [namespace eval $my_class {info options}]
|
| 405 |
+
#puts stderr "OPTS!$win!$my_class![join [lsort $myOptions]] \n]!"
|
| 406 |
+
set opt_lst [list configure]
|
| 407 |
+
foreach opt [lsort $myOptions] {
|
| 408 |
+
set found 0
|
| 409 |
+
if {[catch {
|
| 410 |
+
set resource [uplevel 1 info option $opt -resource]
|
| 411 |
+
set class [uplevel 1 info option $opt -class]
|
| 412 |
+
set default_val [uplevel 1 info option $opt -default]
|
| 413 |
+
set found 1
|
| 414 |
+
} msg]} {
|
| 415 |
+
# puts stderr "MSG!$opt!$msg!"
|
| 416 |
+
}
|
| 417 |
+
#puts stderr "OPT!$opt!$found!"
|
| 418 |
+
if {$found} {
|
| 419 |
+
set val ""
|
| 420 |
+
if {[::dict exists $argsDict $opt]} {
|
| 421 |
+
# we have an explicitly set option
|
| 422 |
+
set val [::dict get $argsDict $opt]
|
| 423 |
+
} else {
|
| 424 |
+
if {[string length $val] == 0} {
|
| 425 |
+
set val $default_val
|
| 426 |
+
}
|
| 427 |
+
}
|
| 428 |
+
set myObj [uplevel 1 set this]
|
| 429 |
+
#puts stderr "myObj!$myObj!"
|
| 430 |
+
set ::itcl::internal::variables::${myObj}::itcl_options($opt) $val
|
| 431 |
+
set ::itcl::internal::variables::${myObj}::__itcl_option_infos($opt) [list $resource $class $default_val]
|
| 432 |
+
#puts stderr "OPT1!$opt!$val!"
|
| 433 |
+
uplevel 1 [list set itcl_options($opt) [list $val]]
|
| 434 |
+
# if {[catch {uplevel 1 $myObj configure $opt [list $val]} msg]} {
|
| 435 |
+
#puts stderr "initoptions ERR!$msg!$my_class!$my_win!configure!$opt!$val!"
|
| 436 |
+
# }
|
| 437 |
+
}
|
| 438 |
+
}
|
| 439 |
+
# uplevel 1 $opt_lst
|
| 440 |
+
}
|
| 441 |
+
|
| 442 |
+
# ========================= keepcomponentoption ======================
|
| 443 |
+
# Invoked by Tcl during evaluating constructor whenever
|
| 444 |
+
# the "keepcomponentoption" command is invoked to list the options
|
| 445 |
+
# to be kept when an ::itcl::extendedclass component has been setup
|
| 446 |
+
# for an object.
|
| 447 |
+
#
|
| 448 |
+
# It checks, for all arguments, if the opt is an option of that class
|
| 449 |
+
# and of that component. If that is the case it adds the component name
|
| 450 |
+
# to the list of components for that option.
|
| 451 |
+
# The variable is the object variable: itcl_option_components($opt)
|
| 452 |
+
#
|
| 453 |
+
# Handles the following syntax:
|
| 454 |
+
#
|
| 455 |
+
# keepcomponentoption <componentName> <optionName> ?<optionName> ...?
|
| 456 |
+
#
|
| 457 |
+
# ======================================================================
|
| 458 |
+
|
| 459 |
+
|
| 460 |
+
proc keepcomponentoption {args} {
|
| 461 |
+
upvar win win
|
| 462 |
+
upvar itcl_hull itcl_hull
|
| 463 |
+
|
| 464 |
+
set usage "wrong # args, should be: keepcomponentoption componentName optionName ?optionName ...?"
|
| 465 |
+
|
| 466 |
+
#puts stderr "KEEP!$args![uplevel 1 namespace current]!"
|
| 467 |
+
if {[llength $args] < 2} {
|
| 468 |
+
puts stderr $usage
|
| 469 |
+
return -code error
|
| 470 |
+
}
|
| 471 |
+
set my_hull [uplevel 1 set itcl_hull]
|
| 472 |
+
set my_class [uplevel 1 namespace current]
|
| 473 |
+
set comp [lindex $args 0]
|
| 474 |
+
set args [lrange $args 1 end]
|
| 475 |
+
set class_info_dict [dict get $::itcl::internal::dicts::classComponents $my_class]
|
| 476 |
+
if {![dict exists $class_info_dict $comp]} {
|
| 477 |
+
puts stderr "keepcomponentoption cannot find component \"$comp\""
|
| 478 |
+
return -code error
|
| 479 |
+
}
|
| 480 |
+
set class_comp_dict [dict get $class_info_dict $comp]
|
| 481 |
+
if {![dict exists $class_comp_dict -keptoptions]} {
|
| 482 |
+
dict set class_comp_dict -keptoptions [list]
|
| 483 |
+
}
|
| 484 |
+
foreach opt $args {
|
| 485 |
+
#puts stderr "KEEP!$opt!"
|
| 486 |
+
if {[string range $opt 0 0] ne "-"} {
|
| 487 |
+
puts stderr "keepcomponentoption: option must begin with a \"-\"!"
|
| 488 |
+
return -code error
|
| 489 |
+
}
|
| 490 |
+
if {[lsearch [dict get $class_comp_dict -keptoptions] $opt] < 0} {
|
| 491 |
+
dict lappend class_comp_dict -keptoptions $opt
|
| 492 |
+
}
|
| 493 |
+
}
|
| 494 |
+
if {![info exists ::itcl::internal::component_objects([lindex [uplevel 1 info context] 1])]} {
|
| 495 |
+
set comp_object $::itcl::internal::component_objects([lindex [uplevel 1 info context] 1])
|
| 496 |
+
} else {
|
| 497 |
+
set comp_object "unknown_comp_obj_$comp!"
|
| 498 |
+
}
|
| 499 |
+
dict set class_info_dict $comp $class_comp_dict
|
| 500 |
+
dict set ::itcl::internal::dicts::classComponents $my_class $class_info_dict
|
| 501 |
+
puts stderr "CLDI!$class_comp_dict!"
|
| 502 |
+
addToItclOptions $my_class $comp_object $args [list]
|
| 503 |
+
}
|
| 504 |
+
|
| 505 |
+
proc ignorecomponentoption {args} {
|
| 506 |
+
puts stderr "IGNORE_COMPONENT_OPTION!$args!"
|
| 507 |
+
}
|
| 508 |
+
|
| 509 |
+
proc renamecomponentoption {args} {
|
| 510 |
+
puts stderr "rename_COMPONENT_OPTION!$args!"
|
| 511 |
+
}
|
| 512 |
+
|
| 513 |
+
proc addoptioncomponent {args} {
|
| 514 |
+
puts stderr "ADD_OPTION_COMPONENT!$args!"
|
| 515 |
+
}
|
| 516 |
+
|
| 517 |
+
proc ignoreoptioncomponent {args} {
|
| 518 |
+
puts stderr "IGNORE_OPTION_COMPONENT!$args!"
|
| 519 |
+
}
|
| 520 |
+
|
| 521 |
+
proc renameoptioncomponent {args} {
|
| 522 |
+
puts stderr "RENAME_OPTION_COMPONENT!$args!"
|
| 523 |
+
}
|
| 524 |
+
|
| 525 |
+
proc getEclassOptions {args} {
|
| 526 |
+
upvar win win
|
| 527 |
+
|
| 528 |
+
#puts stderr "getEclassOptions!$args!$win![uplevel 1 namespace current]!"
|
| 529 |
+
#parray ::itcl::internal::variables::${win}::itcl_options
|
| 530 |
+
set result [list]
|
| 531 |
+
foreach opt [array names ::itcl::internal::variables::${win}::itcl_options] {
|
| 532 |
+
if {[catch {
|
| 533 |
+
foreach {res cls def} [set ::itcl::internal::variables::${win}::__itcl_option_infos($opt)] break
|
| 534 |
+
lappend result [list $opt $res $cls $def [set ::itcl::internal::variables::${win}::itcl_options($opt)]]
|
| 535 |
+
} msg]} {
|
| 536 |
+
}
|
| 537 |
+
}
|
| 538 |
+
return $result
|
| 539 |
+
}
|
| 540 |
+
|
| 541 |
+
proc eclassConfigure {args} {
|
| 542 |
+
upvar win win
|
| 543 |
+
|
| 544 |
+
#puts stderr "+++ eclassConfigure!$args!"
|
| 545 |
+
if {[llength $args] > 1} {
|
| 546 |
+
foreach {opt val} $args break
|
| 547 |
+
if {[::info exists ::itcl::internal::variables::${win}::itcl_options($opt)]} {
|
| 548 |
+
set ::itcl::internal::variables::${win}::itcl_options($opt) $val
|
| 549 |
+
return
|
| 550 |
+
}
|
| 551 |
+
} else {
|
| 552 |
+
foreach {opt} $args break
|
| 553 |
+
if {[::info exists ::itcl::internal::variables::${win}::itcl_options($opt)]} {
|
| 554 |
+
#puts stderr "OP![set ::itcl::internal::variables::${win}::itcl_options($opt)]!"
|
| 555 |
+
foreach {res cls def} [set ::itcl::internal::variables::${win}::__itcl_option_infos($opt)] break
|
| 556 |
+
return [list $opt $res $cls $def [set ::itcl::internal::variables::${win}::itcl_options($opt)]]
|
| 557 |
+
}
|
| 558 |
+
}
|
| 559 |
+
return -code error
|
| 560 |
+
}
|
| 561 |
+
|
| 562 |
+
}
|
lib/itcl4.2.4/itclWidget.tcl
ADDED
|
@@ -0,0 +1,447 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#
|
| 2 |
+
# itclWidget.tcl
|
| 3 |
+
# ----------------------------------------------------------------------
|
| 4 |
+
# Invoked automatically upon startup to customize the interpreter
|
| 5 |
+
# for [incr Tcl] when one of ::itcl::widget or ::itcl::widgetadaptor is called.
|
| 6 |
+
# ----------------------------------------------------------------------
|
| 7 |
+
# AUTHOR: Arnulf P. Wiedemann
|
| 8 |
+
#
|
| 9 |
+
# ----------------------------------------------------------------------
|
| 10 |
+
# Copyright (c) 2008 Arnulf P. Wiedemann
|
| 11 |
+
# ======================================================================
|
| 12 |
+
# See the file "license.terms" for information on usage and
|
| 13 |
+
# redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
| 14 |
+
|
| 15 |
+
package require Tk 8.6 9
|
| 16 |
+
# package require itclwidget [set ::itcl::version]
|
| 17 |
+
|
| 18 |
+
namespace eval ::itcl {
|
| 19 |
+
|
| 20 |
+
proc widget {name args} {
|
| 21 |
+
set result [uplevel 1 ::itcl::internal::commands::genericclass widget $name $args]
|
| 22 |
+
# we handle create by owerselfs !! allow classunknown to handle that
|
| 23 |
+
oo::objdefine $result unexport create
|
| 24 |
+
return $result
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
proc widgetadaptor {name args} {
|
| 28 |
+
set result [uplevel 1 ::itcl::internal::commands::genericclass widgetadaptor $name $args]
|
| 29 |
+
# we handle create by owerselfs !! allow classunknown to handle that
|
| 30 |
+
oo::objdefine $result unexport create
|
| 31 |
+
return $result
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
} ; # end ::itcl
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
namespace eval ::itcl::internal::commands {
|
| 38 |
+
|
| 39 |
+
proc initWidgetOptions {varNsName widgetName className} {
|
| 40 |
+
set myDict [set ::itcl::internal::dicts::classOptions]
|
| 41 |
+
if {$myDict eq ""} {
|
| 42 |
+
return
|
| 43 |
+
}
|
| 44 |
+
if {![dict exists $myDict $className]} {
|
| 45 |
+
return
|
| 46 |
+
}
|
| 47 |
+
set myDict [dict get $myDict $className]
|
| 48 |
+
foreach option [dict keys $myDict] {
|
| 49 |
+
set infos [dict get $myDict $option]
|
| 50 |
+
set resource [dict get $infos -resource]
|
| 51 |
+
set class [dict get $infos -class]
|
| 52 |
+
set value [::option get $widgetName $resource $class]
|
| 53 |
+
if {$value eq ""} {
|
| 54 |
+
if {[dict exists $infos -default]} {
|
| 55 |
+
set defaultValue [dict get $infos -default]
|
| 56 |
+
uplevel 1 set ${varNsName}::itcl_options($option) $defaultValue
|
| 57 |
+
}
|
| 58 |
+
} else {
|
| 59 |
+
uplevel 1 set ${varNsName}::itcl_options($option) $value
|
| 60 |
+
}
|
| 61 |
+
}
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
proc initWidgetDelegatedOptions {varNsName widgetName className args} {
|
| 65 |
+
set myDict [set ::itcl::internal::dicts::classDelegatedOptions]
|
| 66 |
+
if {$myDict eq ""} {
|
| 67 |
+
return
|
| 68 |
+
}
|
| 69 |
+
if {![dict exists $myDict $className]} {
|
| 70 |
+
return
|
| 71 |
+
}
|
| 72 |
+
set myDict [dict get $myDict $className]
|
| 73 |
+
foreach option [dict keys $myDict] {
|
| 74 |
+
set infos [dict get $myDict $option]
|
| 75 |
+
if {![dict exists $infos -resource]} {
|
| 76 |
+
# this is the case when delegating "*"
|
| 77 |
+
continue
|
| 78 |
+
}
|
| 79 |
+
if {![dict exists $infos -component]} {
|
| 80 |
+
# nothing to do
|
| 81 |
+
continue
|
| 82 |
+
}
|
| 83 |
+
# check if not in the command line options
|
| 84 |
+
# these have higher priority
|
| 85 |
+
set myOption $option
|
| 86 |
+
if {[dict exists $infos -as]} {
|
| 87 |
+
set myOption [dict get $infos -as]
|
| 88 |
+
}
|
| 89 |
+
set noOptionSet 0
|
| 90 |
+
foreach {optName optVal} $args {
|
| 91 |
+
if {$optName eq $myOption} {
|
| 92 |
+
set noOptionSet 1
|
| 93 |
+
break
|
| 94 |
+
}
|
| 95 |
+
}
|
| 96 |
+
if {$noOptionSet} {
|
| 97 |
+
continue
|
| 98 |
+
}
|
| 99 |
+
set resource [dict get $infos -resource]
|
| 100 |
+
set class [dict get $infos -class]
|
| 101 |
+
set component [dict get $infos -component]
|
| 102 |
+
set value [::option get $widgetName $resource $class]
|
| 103 |
+
if {$component ne ""} {
|
| 104 |
+
if {$value ne ""} {
|
| 105 |
+
set compVar [namespace eval ${varNsName}${className} "set $component"]
|
| 106 |
+
if {$compVar ne ""} {
|
| 107 |
+
uplevel 1 $compVar configure $myOption $value
|
| 108 |
+
}
|
| 109 |
+
}
|
| 110 |
+
}
|
| 111 |
+
}
|
| 112 |
+
}
|
| 113 |
+
|
| 114 |
+
proc widgetinitobjectoptions {varNsName widgetName className} {
|
| 115 |
+
#puts stderr "initWidgetObjectOptions!$varNsName!$widgetName!$className!"
|
| 116 |
+
}
|
| 117 |
+
|
| 118 |
+
proc deletehull {newName oldName what} {
|
| 119 |
+
if {$what eq "delete"} {
|
| 120 |
+
set name [namespace tail $newName]
|
| 121 |
+
regsub {hull[0-9]+} $name {} name
|
| 122 |
+
rename $name {}
|
| 123 |
+
}
|
| 124 |
+
if {$what eq "rename"} {
|
| 125 |
+
set name [namespace tail $newName]
|
| 126 |
+
regsub {hull[0-9]+} $name {} name
|
| 127 |
+
rename $name {}
|
| 128 |
+
}
|
| 129 |
+
}
|
| 130 |
+
|
| 131 |
+
proc hullandoptionsinstall {objectName className widgetClass hulltype args} {
|
| 132 |
+
if {$hulltype eq ""} {
|
| 133 |
+
set hulltype frame
|
| 134 |
+
}
|
| 135 |
+
set idx 0
|
| 136 |
+
set found 0
|
| 137 |
+
foreach {optName optValue} $args {
|
| 138 |
+
if {$optName eq "-class"} {
|
| 139 |
+
set found 1
|
| 140 |
+
set widgetClass $optValue
|
| 141 |
+
break
|
| 142 |
+
}
|
| 143 |
+
incr idx
|
| 144 |
+
}
|
| 145 |
+
if {$found} {
|
| 146 |
+
set args [lreplace $args $idx [expr {$idx + 1}]]
|
| 147 |
+
}
|
| 148 |
+
if {$widgetClass eq ""} {
|
| 149 |
+
set widgetClass $className
|
| 150 |
+
set widgetClass [string totitle $widgetClass]
|
| 151 |
+
}
|
| 152 |
+
set cmd "set win $objectName; ::itcl::builtin::installhull using $hulltype -class $widgetClass $args"
|
| 153 |
+
uplevel 2 $cmd
|
| 154 |
+
}
|
| 155 |
+
|
| 156 |
+
} ; # end ::itcl::internal::commands
|
| 157 |
+
|
| 158 |
+
namespace eval ::itcl::builtin {
|
| 159 |
+
|
| 160 |
+
proc installhull {args} {
|
| 161 |
+
set cmdPath ::itcl::internal::commands
|
| 162 |
+
set className [uplevel 1 info class]
|
| 163 |
+
|
| 164 |
+
set replace 0
|
| 165 |
+
switch -- [llength $args] {
|
| 166 |
+
0 {
|
| 167 |
+
return -code error\
|
| 168 |
+
"wrong # args: should be \"[lindex [info level 0] 0]\
|
| 169 |
+
name|using <widgetType> ?arg ...?\""
|
| 170 |
+
}
|
| 171 |
+
1 {
|
| 172 |
+
set widgetName [lindex $args 0]
|
| 173 |
+
set varNsName $::itcl::internal::varNsName($widgetName)
|
| 174 |
+
}
|
| 175 |
+
default {
|
| 176 |
+
upvar win win
|
| 177 |
+
set widgetName $win
|
| 178 |
+
|
| 179 |
+
set varNsName $::itcl::internal::varNsName($widgetName)
|
| 180 |
+
set widgetType [lindex $args 1]
|
| 181 |
+
incr replace
|
| 182 |
+
if {[llength $args] > 3 && [lindex $args 2] eq "-class"} {
|
| 183 |
+
set classNam [lindex $args 3]
|
| 184 |
+
incr replace 2
|
| 185 |
+
} else {
|
| 186 |
+
set classNam [string totitle $widgetType]
|
| 187 |
+
}
|
| 188 |
+
uplevel 1 [lreplace $args 0 $replace $widgetType $widgetName -class $classNam]
|
| 189 |
+
uplevel 1 [list ${cmdPath}::initWidgetOptions $varNsName $widgetName $className]
|
| 190 |
+
}
|
| 191 |
+
}
|
| 192 |
+
|
| 193 |
+
# initialize the itcl_hull variable
|
| 194 |
+
set i 0
|
| 195 |
+
set nam ::itcl::internal::widgets::hull
|
| 196 |
+
while {1} {
|
| 197 |
+
incr i
|
| 198 |
+
set hullNam ${nam}${i}$widgetName
|
| 199 |
+
if {[::info command $hullNam] eq ""} {
|
| 200 |
+
break
|
| 201 |
+
}
|
| 202 |
+
}
|
| 203 |
+
uplevel 1 [list ${cmdPath}::sethullwindowname $widgetName]
|
| 204 |
+
uplevel 1 [list ::rename $widgetName $hullNam]
|
| 205 |
+
uplevel 1 [list ::trace add command $hullNam {delete rename} ::itcl::internal::commands::deletehull]
|
| 206 |
+
catch {${cmdPath}::checksetitclhull [list] 0}
|
| 207 |
+
namespace eval ${varNsName}${className} "set itcl_hull $hullNam"
|
| 208 |
+
catch {${cmdPath}::checksetitclhull [list] 2}
|
| 209 |
+
uplevel 1 [lreplace $args 0 $replace ${cmdPath}::initWidgetDelegatedOptions $varNsName $widgetName $className]
|
| 210 |
+
}
|
| 211 |
+
|
| 212 |
+
proc installcomponent {args} {
|
| 213 |
+
upvar win win
|
| 214 |
+
|
| 215 |
+
set className [uplevel 1 info class]
|
| 216 |
+
set myType [${className}::info types [namespace tail $className]]
|
| 217 |
+
set isType 0
|
| 218 |
+
if {$myType ne ""} {
|
| 219 |
+
set isType 1
|
| 220 |
+
}
|
| 221 |
+
set numArgs [llength $args]
|
| 222 |
+
set usage "usage: installcomponent <componentName> using <widgetType> <widgetPath> ?-option value ...?"
|
| 223 |
+
if {$numArgs < 4} {
|
| 224 |
+
error $usage
|
| 225 |
+
}
|
| 226 |
+
foreach {componentName using widgetType widgetPath} $args break
|
| 227 |
+
set opts [lrange $args 4 end]
|
| 228 |
+
if {$using ne "using"} {
|
| 229 |
+
error $usage
|
| 230 |
+
}
|
| 231 |
+
if {!$isType} {
|
| 232 |
+
set hullExists [uplevel 1 ::info exists itcl_hull]
|
| 233 |
+
if {!$hullExists} {
|
| 234 |
+
error "cannot install \"$componentName\" before \"itcl_hull\" exists"
|
| 235 |
+
}
|
| 236 |
+
set hullVal [uplevel 1 set itcl_hull]
|
| 237 |
+
if {$hullVal eq ""} {
|
| 238 |
+
error "cannot install \"$componentName\" before \"itcl_hull\" exists"
|
| 239 |
+
}
|
| 240 |
+
}
|
| 241 |
+
# check for delegated option and ask the option database for the values
|
| 242 |
+
# first check for number of delegated options
|
| 243 |
+
set numOpts 0
|
| 244 |
+
set starOption 0
|
| 245 |
+
set myDict [set ::itcl::internal::dicts::classDelegatedOptions]
|
| 246 |
+
if {[dict exists $myDict $className]} {
|
| 247 |
+
set myDict [dict get $myDict $className]
|
| 248 |
+
foreach option [dict keys $myDict] {
|
| 249 |
+
if {$option eq "*"} {
|
| 250 |
+
set starOption 1
|
| 251 |
+
}
|
| 252 |
+
incr numOpts
|
| 253 |
+
}
|
| 254 |
+
}
|
| 255 |
+
set myOptionDict [set ::itcl::internal::dicts::classOptions]
|
| 256 |
+
if {[dict exists $myOptionDict $className]} {
|
| 257 |
+
set myOptionDict [dict get $myOptionDict $className]
|
| 258 |
+
}
|
| 259 |
+
set cmd [list $widgetPath configure]
|
| 260 |
+
set cmd1 "set $componentName \[$widgetType $widgetPath\]"
|
| 261 |
+
uplevel 1 $cmd1
|
| 262 |
+
if {$starOption} {
|
| 263 |
+
upvar $componentName compName
|
| 264 |
+
set cmd1 [list $compName configure]
|
| 265 |
+
set configInfos [uplevel 1 $cmd1]
|
| 266 |
+
foreach entry $configInfos {
|
| 267 |
+
if {[llength $entry] > 2} {
|
| 268 |
+
foreach {optName resource class defaultValue} $entry break
|
| 269 |
+
set val ""
|
| 270 |
+
catch {
|
| 271 |
+
set val [::option get $win $resource $class]
|
| 272 |
+
}
|
| 273 |
+
if {$val ne ""} {
|
| 274 |
+
set addOpt 1
|
| 275 |
+
if {[dict exists $myDict $$optName]} {
|
| 276 |
+
set addOpt 0
|
| 277 |
+
} else {
|
| 278 |
+
set starDict [dict get $myDict "*"]
|
| 279 |
+
if {[dict exists $starDict -except]} {
|
| 280 |
+
set exceptions [dict get $starDict -except]
|
| 281 |
+
if {[lsearch $exceptions $optName] >= 0} {
|
| 282 |
+
set addOpt 0
|
| 283 |
+
}
|
| 284 |
+
|
| 285 |
+
}
|
| 286 |
+
if {[dict exists $myOptionDict $optName]} {
|
| 287 |
+
set addOpt 0
|
| 288 |
+
}
|
| 289 |
+
}
|
| 290 |
+
if {$addOpt} {
|
| 291 |
+
lappend cmd $optName $val
|
| 292 |
+
}
|
| 293 |
+
|
| 294 |
+
}
|
| 295 |
+
|
| 296 |
+
}
|
| 297 |
+
}
|
| 298 |
+
} else {
|
| 299 |
+
foreach optName [dict keys $myDict] {
|
| 300 |
+
set optInfos [dict get $myDict $optName]
|
| 301 |
+
set resource [dict get $optInfos -resource]
|
| 302 |
+
set class [namespace tail $className]
|
| 303 |
+
set class [string totitle $class]
|
| 304 |
+
set val ""
|
| 305 |
+
catch {
|
| 306 |
+
set val [::option get $win $resource $class]
|
| 307 |
+
}
|
| 308 |
+
if {$val ne ""} {
|
| 309 |
+
if {[dict exists $optInfos -as] } {
|
| 310 |
+
set optName [dict get $optInfos -as]
|
| 311 |
+
}
|
| 312 |
+
lappend cmd $optName $val
|
| 313 |
+
}
|
| 314 |
+
}
|
| 315 |
+
}
|
| 316 |
+
lappend cmd {*}$opts
|
| 317 |
+
uplevel 1 $cmd
|
| 318 |
+
}
|
| 319 |
+
|
| 320 |
+
} ; # end ::itcl::builtin
|
| 321 |
+
|
| 322 |
+
set ::itcl::internal::dicts::hullTypes [list \
|
| 323 |
+
frame \
|
| 324 |
+
toplevel \
|
| 325 |
+
labelframe \
|
| 326 |
+
ttk:frame \
|
| 327 |
+
ttk:toplevel \
|
| 328 |
+
ttk:labelframe \
|
| 329 |
+
]
|
| 330 |
+
|
| 331 |
+
namespace eval ::itcl::builtin::Info {
|
| 332 |
+
|
| 333 |
+
proc hulltypes {args} {
|
| 334 |
+
namespace upvar ::itcl::internal::dicts hullTypes hullTypes
|
| 335 |
+
|
| 336 |
+
set numArgs [llength $args]
|
| 337 |
+
if {$numArgs > 1} {
|
| 338 |
+
error "wrong # args should be: info hulltypes ?<pattern>?"
|
| 339 |
+
}
|
| 340 |
+
set pattern ""
|
| 341 |
+
if {$numArgs > 0} {
|
| 342 |
+
set pattern [lindex $args 0]
|
| 343 |
+
}
|
| 344 |
+
if {$pattern ne ""} {
|
| 345 |
+
return [lsearch -all -inline -glob $hullTypes $pattern]
|
| 346 |
+
}
|
| 347 |
+
return $hullTypes
|
| 348 |
+
|
| 349 |
+
}
|
| 350 |
+
|
| 351 |
+
proc widgetclasses {args} {
|
| 352 |
+
set numArgs [llength $args]
|
| 353 |
+
if {$numArgs > 1} {
|
| 354 |
+
error "wrong # args should be: info widgetclasses ?<pattern>?"
|
| 355 |
+
}
|
| 356 |
+
set pattern ""
|
| 357 |
+
if {$numArgs > 0} {
|
| 358 |
+
set pattern [lindex $args 0]
|
| 359 |
+
}
|
| 360 |
+
set myDict [set ::itcl::internal::dicts::classes]
|
| 361 |
+
if {![dict exists $myDict widget]} {
|
| 362 |
+
return [list]
|
| 363 |
+
}
|
| 364 |
+
set myDict [dict get $myDict widget]
|
| 365 |
+
set result [list]
|
| 366 |
+
if {$pattern ne ""} {
|
| 367 |
+
foreach key [dict keys $myDict] {
|
| 368 |
+
set myInfo [dict get $myDict $key]
|
| 369 |
+
set value [dict get $myInfo -widget]
|
| 370 |
+
if {[string match $pattern $value]} {
|
| 371 |
+
lappend result $value
|
| 372 |
+
}
|
| 373 |
+
}
|
| 374 |
+
} else {
|
| 375 |
+
foreach key [dict keys $myDict] {
|
| 376 |
+
set myInfo [dict get $myDict $key]
|
| 377 |
+
lappend result [dict get $myInfo -widget]
|
| 378 |
+
}
|
| 379 |
+
}
|
| 380 |
+
return $result
|
| 381 |
+
}
|
| 382 |
+
|
| 383 |
+
proc widgets {args} {
|
| 384 |
+
set numArgs [llength $args]
|
| 385 |
+
if {$numArgs > 1} {
|
| 386 |
+
error "wrong # args should be: info widgets ?<pattern>?"
|
| 387 |
+
}
|
| 388 |
+
set pattern ""
|
| 389 |
+
if {$numArgs > 0} {
|
| 390 |
+
set pattern [lindex $args 0]
|
| 391 |
+
}
|
| 392 |
+
set myDict [set ::itcl::internal::dicts::classes]
|
| 393 |
+
if {![dict exists $myDict widget]} {
|
| 394 |
+
return [list]
|
| 395 |
+
}
|
| 396 |
+
set myDict [dict get $myDict widget]
|
| 397 |
+
set result [list]
|
| 398 |
+
if {$pattern ne ""} {
|
| 399 |
+
foreach key [dict keys $myDict] {
|
| 400 |
+
set myInfo [dict get $myDict $key]
|
| 401 |
+
set value [dict get $myInfo -name]
|
| 402 |
+
if {[string match $pattern $value]} {
|
| 403 |
+
lappend result $value
|
| 404 |
+
}
|
| 405 |
+
}
|
| 406 |
+
} else {
|
| 407 |
+
foreach key [dict keys $myDict] {
|
| 408 |
+
set myInfo [dict get $myDict $key]
|
| 409 |
+
lappend result [dict get $myInfo -name]
|
| 410 |
+
}
|
| 411 |
+
}
|
| 412 |
+
return $result
|
| 413 |
+
}
|
| 414 |
+
|
| 415 |
+
proc widgetadaptors {args} {
|
| 416 |
+
set numArgs [llength $args]
|
| 417 |
+
if {$numArgs > 1} {
|
| 418 |
+
error "wrong # args should be: info widgetadaptors ?<pattern>?"
|
| 419 |
+
}
|
| 420 |
+
set pattern ""
|
| 421 |
+
if {$numArgs > 0} {
|
| 422 |
+
set pattern [lindex $args 0]
|
| 423 |
+
}
|
| 424 |
+
set myDict [set ::itcl::internal::dicts::classes]
|
| 425 |
+
if {![dict exists $myDict widgetadaptor]} {
|
| 426 |
+
return [list]
|
| 427 |
+
}
|
| 428 |
+
set myDict [dict get $myDict widgetadaptor]
|
| 429 |
+
set result [list]
|
| 430 |
+
if {$pattern ne ""} {
|
| 431 |
+
foreach key [dict keys $myDict] {
|
| 432 |
+
set myInfo [dict get $myDict $key]
|
| 433 |
+
set value [dict get $myInfo -name]
|
| 434 |
+
if {[string match $pattern $value]} {
|
| 435 |
+
lappend result $value
|
| 436 |
+
}
|
| 437 |
+
}
|
| 438 |
+
} else {
|
| 439 |
+
foreach key [dict keys $myDict] {
|
| 440 |
+
set myInfo [dict get $myDict $key]
|
| 441 |
+
lappend result [dict get $myInfo -name]
|
| 442 |
+
}
|
| 443 |
+
}
|
| 444 |
+
return $result
|
| 445 |
+
}
|
| 446 |
+
|
| 447 |
+
} ; # end ::itcl::builtin::Info
|
lib/itcl4.2.4/libitclstub4.2.4.a
ADDED
|
Binary file (2.75 kB). View file
|
|
|
lib/itcl4.2.4/pkgIndex.tcl
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- tcl -*-
|
| 2 |
+
# Tcl package index file, version 1.1
|
| 3 |
+
#
|
| 4 |
+
|
| 5 |
+
if {![package vsatisfies [package provide Tcl] 8.6-]} {return}
|
| 6 |
+
|
| 7 |
+
if {[package vsatisfies [package provide Tcl] 9.0-]} {
|
| 8 |
+
package ifneeded itcl 4.2.4 \
|
| 9 |
+
[list load [file join $dir libtcl9itcl4.2.4.so] Itcl]
|
| 10 |
+
} else {
|
| 11 |
+
package ifneeded itcl 4.2.4 \
|
| 12 |
+
[list load [file join $dir libitcl4.2.4.so] Itcl]
|
| 13 |
+
}
|
| 14 |
+
package ifneeded Itcl 4.2.4 [list package require -exact itcl 4.2.4]
|
lib/python3.10/py_compile.py
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Routine to "compile" a .py file to a .pyc file.
|
| 2 |
+
|
| 3 |
+
This module has intimate knowledge of the format of .pyc files.
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
import enum
|
| 7 |
+
import importlib._bootstrap_external
|
| 8 |
+
import importlib.machinery
|
| 9 |
+
import importlib.util
|
| 10 |
+
import os
|
| 11 |
+
import os.path
|
| 12 |
+
import sys
|
| 13 |
+
import traceback
|
| 14 |
+
|
| 15 |
+
__all__ = ["compile", "main", "PyCompileError", "PycInvalidationMode"]
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
class PyCompileError(Exception):
|
| 19 |
+
"""Exception raised when an error occurs while attempting to
|
| 20 |
+
compile the file.
|
| 21 |
+
|
| 22 |
+
To raise this exception, use
|
| 23 |
+
|
| 24 |
+
raise PyCompileError(exc_type,exc_value,file[,msg])
|
| 25 |
+
|
| 26 |
+
where
|
| 27 |
+
|
| 28 |
+
exc_type: exception type to be used in error message
|
| 29 |
+
type name can be accesses as class variable
|
| 30 |
+
'exc_type_name'
|
| 31 |
+
|
| 32 |
+
exc_value: exception value to be used in error message
|
| 33 |
+
can be accesses as class variable 'exc_value'
|
| 34 |
+
|
| 35 |
+
file: name of file being compiled to be used in error message
|
| 36 |
+
can be accesses as class variable 'file'
|
| 37 |
+
|
| 38 |
+
msg: string message to be written as error message
|
| 39 |
+
If no value is given, a default exception message will be
|
| 40 |
+
given, consistent with 'standard' py_compile output.
|
| 41 |
+
message (or default) can be accesses as class variable
|
| 42 |
+
'msg'
|
| 43 |
+
|
| 44 |
+
"""
|
| 45 |
+
|
| 46 |
+
def __init__(self, exc_type, exc_value, file, msg=''):
|
| 47 |
+
exc_type_name = exc_type.__name__
|
| 48 |
+
if exc_type is SyntaxError:
|
| 49 |
+
tbtext = ''.join(traceback.format_exception_only(
|
| 50 |
+
exc_type, exc_value))
|
| 51 |
+
errmsg = tbtext.replace('File "<string>"', 'File "%s"' % file)
|
| 52 |
+
else:
|
| 53 |
+
errmsg = "Sorry: %s: %s" % (exc_type_name,exc_value)
|
| 54 |
+
|
| 55 |
+
Exception.__init__(self,msg or errmsg,exc_type_name,exc_value,file)
|
| 56 |
+
|
| 57 |
+
self.exc_type_name = exc_type_name
|
| 58 |
+
self.exc_value = exc_value
|
| 59 |
+
self.file = file
|
| 60 |
+
self.msg = msg or errmsg
|
| 61 |
+
|
| 62 |
+
def __str__(self):
|
| 63 |
+
return self.msg
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
class PycInvalidationMode(enum.Enum):
|
| 67 |
+
TIMESTAMP = 1
|
| 68 |
+
CHECKED_HASH = 2
|
| 69 |
+
UNCHECKED_HASH = 3
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
def _get_default_invalidation_mode():
|
| 73 |
+
if os.environ.get('SOURCE_DATE_EPOCH'):
|
| 74 |
+
return PycInvalidationMode.CHECKED_HASH
|
| 75 |
+
else:
|
| 76 |
+
return PycInvalidationMode.TIMESTAMP
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
def compile(file, cfile=None, dfile=None, doraise=False, optimize=-1,
|
| 80 |
+
invalidation_mode=None, quiet=0):
|
| 81 |
+
"""Byte-compile one Python source file to Python bytecode.
|
| 82 |
+
|
| 83 |
+
:param file: The source file name.
|
| 84 |
+
:param cfile: The target byte compiled file name. When not given, this
|
| 85 |
+
defaults to the PEP 3147/PEP 488 location.
|
| 86 |
+
:param dfile: Purported file name, i.e. the file name that shows up in
|
| 87 |
+
error messages. Defaults to the source file name.
|
| 88 |
+
:param doraise: Flag indicating whether or not an exception should be
|
| 89 |
+
raised when a compile error is found. If an exception occurs and this
|
| 90 |
+
flag is set to False, a string indicating the nature of the exception
|
| 91 |
+
will be printed, and the function will return to the caller. If an
|
| 92 |
+
exception occurs and this flag is set to True, a PyCompileError
|
| 93 |
+
exception will be raised.
|
| 94 |
+
:param optimize: The optimization level for the compiler. Valid values
|
| 95 |
+
are -1, 0, 1 and 2. A value of -1 means to use the optimization
|
| 96 |
+
level of the current interpreter, as given by -O command line options.
|
| 97 |
+
:param invalidation_mode:
|
| 98 |
+
:param quiet: Return full output with False or 0, errors only with 1,
|
| 99 |
+
and no output with 2.
|
| 100 |
+
|
| 101 |
+
:return: Path to the resulting byte compiled file.
|
| 102 |
+
|
| 103 |
+
Note that it isn't necessary to byte-compile Python modules for
|
| 104 |
+
execution efficiency -- Python itself byte-compiles a module when
|
| 105 |
+
it is loaded, and if it can, writes out the bytecode to the
|
| 106 |
+
corresponding .pyc file.
|
| 107 |
+
|
| 108 |
+
However, if a Python installation is shared between users, it is a
|
| 109 |
+
good idea to byte-compile all modules upon installation, since
|
| 110 |
+
other users may not be able to write in the source directories,
|
| 111 |
+
and thus they won't be able to write the .pyc file, and then
|
| 112 |
+
they would be byte-compiling every module each time it is loaded.
|
| 113 |
+
This can slow down program start-up considerably.
|
| 114 |
+
|
| 115 |
+
See compileall.py for a script/module that uses this module to
|
| 116 |
+
byte-compile all installed files (or all files in selected
|
| 117 |
+
directories).
|
| 118 |
+
|
| 119 |
+
Do note that FileExistsError is raised if cfile ends up pointing at a
|
| 120 |
+
non-regular file or symlink. Because the compilation uses a file renaming,
|
| 121 |
+
the resulting file would be regular and thus not the same type of file as
|
| 122 |
+
it was previously.
|
| 123 |
+
"""
|
| 124 |
+
if invalidation_mode is None:
|
| 125 |
+
invalidation_mode = _get_default_invalidation_mode()
|
| 126 |
+
if cfile is None:
|
| 127 |
+
if optimize >= 0:
|
| 128 |
+
optimization = optimize if optimize >= 1 else ''
|
| 129 |
+
cfile = importlib.util.cache_from_source(file,
|
| 130 |
+
optimization=optimization)
|
| 131 |
+
else:
|
| 132 |
+
cfile = importlib.util.cache_from_source(file)
|
| 133 |
+
if os.path.islink(cfile):
|
| 134 |
+
msg = ('{} is a symlink and will be changed into a regular file if '
|
| 135 |
+
'import writes a byte-compiled file to it')
|
| 136 |
+
raise FileExistsError(msg.format(cfile))
|
| 137 |
+
elif os.path.exists(cfile) and not os.path.isfile(cfile):
|
| 138 |
+
msg = ('{} is a non-regular file and will be changed into a regular '
|
| 139 |
+
'one if import writes a byte-compiled file to it')
|
| 140 |
+
raise FileExistsError(msg.format(cfile))
|
| 141 |
+
loader = importlib.machinery.SourceFileLoader('<py_compile>', file)
|
| 142 |
+
source_bytes = loader.get_data(file)
|
| 143 |
+
try:
|
| 144 |
+
code = loader.source_to_code(source_bytes, dfile or file,
|
| 145 |
+
_optimize=optimize)
|
| 146 |
+
except Exception as err:
|
| 147 |
+
py_exc = PyCompileError(err.__class__, err, dfile or file)
|
| 148 |
+
if quiet < 2:
|
| 149 |
+
if doraise:
|
| 150 |
+
raise py_exc
|
| 151 |
+
else:
|
| 152 |
+
sys.stderr.write(py_exc.msg + '\n')
|
| 153 |
+
return
|
| 154 |
+
try:
|
| 155 |
+
dirname = os.path.dirname(cfile)
|
| 156 |
+
if dirname:
|
| 157 |
+
os.makedirs(dirname)
|
| 158 |
+
except FileExistsError:
|
| 159 |
+
pass
|
| 160 |
+
if invalidation_mode == PycInvalidationMode.TIMESTAMP:
|
| 161 |
+
source_stats = loader.path_stats(file)
|
| 162 |
+
bytecode = importlib._bootstrap_external._code_to_timestamp_pyc(
|
| 163 |
+
code, source_stats['mtime'], source_stats['size'])
|
| 164 |
+
else:
|
| 165 |
+
source_hash = importlib.util.source_hash(source_bytes)
|
| 166 |
+
bytecode = importlib._bootstrap_external._code_to_hash_pyc(
|
| 167 |
+
code,
|
| 168 |
+
source_hash,
|
| 169 |
+
(invalidation_mode == PycInvalidationMode.CHECKED_HASH),
|
| 170 |
+
)
|
| 171 |
+
mode = importlib._bootstrap_external._calc_mode(file)
|
| 172 |
+
importlib._bootstrap_external._write_atomic(cfile, bytecode, mode)
|
| 173 |
+
return cfile
|
| 174 |
+
|
| 175 |
+
|
| 176 |
+
def main():
|
| 177 |
+
import argparse
|
| 178 |
+
|
| 179 |
+
description = 'A simple command-line interface for py_compile module.'
|
| 180 |
+
parser = argparse.ArgumentParser(description=description)
|
| 181 |
+
parser.add_argument(
|
| 182 |
+
'-q', '--quiet',
|
| 183 |
+
action='store_true',
|
| 184 |
+
help='Suppress error output',
|
| 185 |
+
)
|
| 186 |
+
parser.add_argument(
|
| 187 |
+
'filenames',
|
| 188 |
+
nargs='+',
|
| 189 |
+
help='Files to compile',
|
| 190 |
+
)
|
| 191 |
+
args = parser.parse_args()
|
| 192 |
+
if args.filenames == ['-']:
|
| 193 |
+
filenames = [filename.rstrip('\n') for filename in sys.stdin.readlines()]
|
| 194 |
+
else:
|
| 195 |
+
filenames = args.filenames
|
| 196 |
+
for filename in filenames:
|
| 197 |
+
try:
|
| 198 |
+
compile(filename, doraise=True)
|
| 199 |
+
except PyCompileError as error:
|
| 200 |
+
if args.quiet:
|
| 201 |
+
parser.exit(1)
|
| 202 |
+
else:
|
| 203 |
+
parser.exit(1, error.msg)
|
| 204 |
+
except OSError as error:
|
| 205 |
+
if args.quiet:
|
| 206 |
+
parser.exit(1)
|
| 207 |
+
else:
|
| 208 |
+
parser.exit(1, str(error))
|
| 209 |
+
|
| 210 |
+
|
| 211 |
+
if __name__ == "__main__":
|
| 212 |
+
main()
|
lib/python3.10/pyclbr.py
ADDED
|
@@ -0,0 +1,314 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Parse a Python module and describe its classes and functions.
|
| 2 |
+
|
| 3 |
+
Parse enough of a Python file to recognize imports and class and
|
| 4 |
+
function definitions, and to find out the superclasses of a class.
|
| 5 |
+
|
| 6 |
+
The interface consists of a single function:
|
| 7 |
+
readmodule_ex(module, path=None)
|
| 8 |
+
where module is the name of a Python module, and path is an optional
|
| 9 |
+
list of directories where the module is to be searched. If present,
|
| 10 |
+
path is prepended to the system search path sys.path. The return value
|
| 11 |
+
is a dictionary. The keys of the dictionary are the names of the
|
| 12 |
+
classes and functions defined in the module (including classes that are
|
| 13 |
+
defined via the from XXX import YYY construct). The values are
|
| 14 |
+
instances of classes Class and Function. One special key/value pair is
|
| 15 |
+
present for packages: the key '__path__' has a list as its value which
|
| 16 |
+
contains the package search path.
|
| 17 |
+
|
| 18 |
+
Classes and Functions have a common superclass: _Object. Every instance
|
| 19 |
+
has the following attributes:
|
| 20 |
+
module -- name of the module;
|
| 21 |
+
name -- name of the object;
|
| 22 |
+
file -- file in which the object is defined;
|
| 23 |
+
lineno -- line in the file where the object's definition starts;
|
| 24 |
+
end_lineno -- line in the file where the object's definition ends;
|
| 25 |
+
parent -- parent of this object, if any;
|
| 26 |
+
children -- nested objects contained in this object.
|
| 27 |
+
The 'children' attribute is a dictionary mapping names to objects.
|
| 28 |
+
|
| 29 |
+
Instances of Function describe functions with the attributes from _Object,
|
| 30 |
+
plus the following:
|
| 31 |
+
is_async -- if a function is defined with an 'async' prefix
|
| 32 |
+
|
| 33 |
+
Instances of Class describe classes with the attributes from _Object,
|
| 34 |
+
plus the following:
|
| 35 |
+
super -- list of super classes (Class instances if possible);
|
| 36 |
+
methods -- mapping of method names to beginning line numbers.
|
| 37 |
+
If the name of a super class is not recognized, the corresponding
|
| 38 |
+
entry in the list of super classes is not a class instance but a
|
| 39 |
+
string giving the name of the super class. Since import statements
|
| 40 |
+
are recognized and imported modules are scanned as well, this
|
| 41 |
+
shouldn't happen often.
|
| 42 |
+
"""
|
| 43 |
+
|
| 44 |
+
import ast
|
| 45 |
+
import sys
|
| 46 |
+
import importlib.util
|
| 47 |
+
|
| 48 |
+
__all__ = ["readmodule", "readmodule_ex", "Class", "Function"]
|
| 49 |
+
|
| 50 |
+
_modules = {} # Initialize cache of modules we've seen.
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
class _Object:
|
| 54 |
+
"Information about Python class or function."
|
| 55 |
+
def __init__(self, module, name, file, lineno, end_lineno, parent):
|
| 56 |
+
self.module = module
|
| 57 |
+
self.name = name
|
| 58 |
+
self.file = file
|
| 59 |
+
self.lineno = lineno
|
| 60 |
+
self.end_lineno = end_lineno
|
| 61 |
+
self.parent = parent
|
| 62 |
+
self.children = {}
|
| 63 |
+
if parent is not None:
|
| 64 |
+
parent.children[name] = self
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
# Odd Function and Class signatures are for back-compatibility.
|
| 68 |
+
class Function(_Object):
|
| 69 |
+
"Information about a Python function, including methods."
|
| 70 |
+
def __init__(self, module, name, file, lineno,
|
| 71 |
+
parent=None, is_async=False, *, end_lineno=None):
|
| 72 |
+
super().__init__(module, name, file, lineno, end_lineno, parent)
|
| 73 |
+
self.is_async = is_async
|
| 74 |
+
if isinstance(parent, Class):
|
| 75 |
+
parent.methods[name] = lineno
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
class Class(_Object):
|
| 79 |
+
"Information about a Python class."
|
| 80 |
+
def __init__(self, module, name, super_, file, lineno,
|
| 81 |
+
parent=None, *, end_lineno=None):
|
| 82 |
+
super().__init__(module, name, file, lineno, end_lineno, parent)
|
| 83 |
+
self.super = super_ or []
|
| 84 |
+
self.methods = {}
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
# These 2 functions are used in these tests
|
| 88 |
+
# Lib/test/test_pyclbr, Lib/idlelib/idle_test/test_browser.py
|
| 89 |
+
def _nest_function(ob, func_name, lineno, end_lineno, is_async=False):
|
| 90 |
+
"Return a Function after nesting within ob."
|
| 91 |
+
return Function(ob.module, func_name, ob.file, lineno,
|
| 92 |
+
parent=ob, is_async=is_async, end_lineno=end_lineno)
|
| 93 |
+
|
| 94 |
+
def _nest_class(ob, class_name, lineno, end_lineno, super=None):
|
| 95 |
+
"Return a Class after nesting within ob."
|
| 96 |
+
return Class(ob.module, class_name, super, ob.file, lineno,
|
| 97 |
+
parent=ob, end_lineno=end_lineno)
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
def readmodule(module, path=None):
|
| 101 |
+
"""Return Class objects for the top-level classes in module.
|
| 102 |
+
|
| 103 |
+
This is the original interface, before Functions were added.
|
| 104 |
+
"""
|
| 105 |
+
|
| 106 |
+
res = {}
|
| 107 |
+
for key, value in _readmodule(module, path or []).items():
|
| 108 |
+
if isinstance(value, Class):
|
| 109 |
+
res[key] = value
|
| 110 |
+
return res
|
| 111 |
+
|
| 112 |
+
def readmodule_ex(module, path=None):
|
| 113 |
+
"""Return a dictionary with all functions and classes in module.
|
| 114 |
+
|
| 115 |
+
Search for module in PATH + sys.path.
|
| 116 |
+
If possible, include imported superclasses.
|
| 117 |
+
Do this by reading source, without importing (and executing) it.
|
| 118 |
+
"""
|
| 119 |
+
return _readmodule(module, path or [])
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
def _readmodule(module, path, inpackage=None):
|
| 123 |
+
"""Do the hard work for readmodule[_ex].
|
| 124 |
+
|
| 125 |
+
If inpackage is given, it must be the dotted name of the package in
|
| 126 |
+
which we are searching for a submodule, and then PATH must be the
|
| 127 |
+
package search path; otherwise, we are searching for a top-level
|
| 128 |
+
module, and path is combined with sys.path.
|
| 129 |
+
"""
|
| 130 |
+
# Compute the full module name (prepending inpackage if set).
|
| 131 |
+
if inpackage is not None:
|
| 132 |
+
fullmodule = "%s.%s" % (inpackage, module)
|
| 133 |
+
else:
|
| 134 |
+
fullmodule = module
|
| 135 |
+
|
| 136 |
+
# Check in the cache.
|
| 137 |
+
if fullmodule in _modules:
|
| 138 |
+
return _modules[fullmodule]
|
| 139 |
+
|
| 140 |
+
# Initialize the dict for this module's contents.
|
| 141 |
+
tree = {}
|
| 142 |
+
|
| 143 |
+
# Check if it is a built-in module; we don't do much for these.
|
| 144 |
+
if module in sys.builtin_module_names and inpackage is None:
|
| 145 |
+
_modules[module] = tree
|
| 146 |
+
return tree
|
| 147 |
+
|
| 148 |
+
# Check for a dotted module name.
|
| 149 |
+
i = module.rfind('.')
|
| 150 |
+
if i >= 0:
|
| 151 |
+
package = module[:i]
|
| 152 |
+
submodule = module[i+1:]
|
| 153 |
+
parent = _readmodule(package, path, inpackage)
|
| 154 |
+
if inpackage is not None:
|
| 155 |
+
package = "%s.%s" % (inpackage, package)
|
| 156 |
+
if not '__path__' in parent:
|
| 157 |
+
raise ImportError('No package named {}'.format(package))
|
| 158 |
+
return _readmodule(submodule, parent['__path__'], package)
|
| 159 |
+
|
| 160 |
+
# Search the path for the module.
|
| 161 |
+
f = None
|
| 162 |
+
if inpackage is not None:
|
| 163 |
+
search_path = path
|
| 164 |
+
else:
|
| 165 |
+
search_path = path + sys.path
|
| 166 |
+
spec = importlib.util._find_spec_from_path(fullmodule, search_path)
|
| 167 |
+
if spec is None:
|
| 168 |
+
raise ModuleNotFoundError(f"no module named {fullmodule!r}", name=fullmodule)
|
| 169 |
+
_modules[fullmodule] = tree
|
| 170 |
+
# Is module a package?
|
| 171 |
+
if spec.submodule_search_locations is not None:
|
| 172 |
+
tree['__path__'] = spec.submodule_search_locations
|
| 173 |
+
try:
|
| 174 |
+
source = spec.loader.get_source(fullmodule)
|
| 175 |
+
except (AttributeError, ImportError):
|
| 176 |
+
# If module is not Python source, we cannot do anything.
|
| 177 |
+
return tree
|
| 178 |
+
else:
|
| 179 |
+
if source is None:
|
| 180 |
+
return tree
|
| 181 |
+
|
| 182 |
+
fname = spec.loader.get_filename(fullmodule)
|
| 183 |
+
return _create_tree(fullmodule, path, fname, source, tree, inpackage)
|
| 184 |
+
|
| 185 |
+
|
| 186 |
+
class _ModuleBrowser(ast.NodeVisitor):
|
| 187 |
+
def __init__(self, module, path, file, tree, inpackage):
|
| 188 |
+
self.path = path
|
| 189 |
+
self.tree = tree
|
| 190 |
+
self.file = file
|
| 191 |
+
self.module = module
|
| 192 |
+
self.inpackage = inpackage
|
| 193 |
+
self.stack = []
|
| 194 |
+
|
| 195 |
+
def visit_ClassDef(self, node):
|
| 196 |
+
bases = []
|
| 197 |
+
for base in node.bases:
|
| 198 |
+
name = ast.unparse(base)
|
| 199 |
+
if name in self.tree:
|
| 200 |
+
# We know this super class.
|
| 201 |
+
bases.append(self.tree[name])
|
| 202 |
+
elif len(names := name.split(".")) > 1:
|
| 203 |
+
# Super class form is module.class:
|
| 204 |
+
# look in module for class.
|
| 205 |
+
*_, module, class_ = names
|
| 206 |
+
if module in _modules:
|
| 207 |
+
bases.append(_modules[module].get(class_, name))
|
| 208 |
+
else:
|
| 209 |
+
bases.append(name)
|
| 210 |
+
|
| 211 |
+
parent = self.stack[-1] if self.stack else None
|
| 212 |
+
class_ = Class(self.module, node.name, bases, self.file, node.lineno,
|
| 213 |
+
parent=parent, end_lineno=node.end_lineno)
|
| 214 |
+
if parent is None:
|
| 215 |
+
self.tree[node.name] = class_
|
| 216 |
+
self.stack.append(class_)
|
| 217 |
+
self.generic_visit(node)
|
| 218 |
+
self.stack.pop()
|
| 219 |
+
|
| 220 |
+
def visit_FunctionDef(self, node, *, is_async=False):
|
| 221 |
+
parent = self.stack[-1] if self.stack else None
|
| 222 |
+
function = Function(self.module, node.name, self.file, node.lineno,
|
| 223 |
+
parent, is_async, end_lineno=node.end_lineno)
|
| 224 |
+
if parent is None:
|
| 225 |
+
self.tree[node.name] = function
|
| 226 |
+
self.stack.append(function)
|
| 227 |
+
self.generic_visit(node)
|
| 228 |
+
self.stack.pop()
|
| 229 |
+
|
| 230 |
+
def visit_AsyncFunctionDef(self, node):
|
| 231 |
+
self.visit_FunctionDef(node, is_async=True)
|
| 232 |
+
|
| 233 |
+
def visit_Import(self, node):
|
| 234 |
+
if node.col_offset != 0:
|
| 235 |
+
return
|
| 236 |
+
|
| 237 |
+
for module in node.names:
|
| 238 |
+
try:
|
| 239 |
+
try:
|
| 240 |
+
_readmodule(module.name, self.path, self.inpackage)
|
| 241 |
+
except ImportError:
|
| 242 |
+
_readmodule(module.name, [])
|
| 243 |
+
except (ImportError, SyntaxError):
|
| 244 |
+
# If we can't find or parse the imported module,
|
| 245 |
+
# too bad -- don't die here.
|
| 246 |
+
continue
|
| 247 |
+
|
| 248 |
+
def visit_ImportFrom(self, node):
|
| 249 |
+
if node.col_offset != 0:
|
| 250 |
+
return
|
| 251 |
+
try:
|
| 252 |
+
module = "." * node.level
|
| 253 |
+
if node.module:
|
| 254 |
+
module += node.module
|
| 255 |
+
module = _readmodule(module, self.path, self.inpackage)
|
| 256 |
+
except (ImportError, SyntaxError):
|
| 257 |
+
return
|
| 258 |
+
|
| 259 |
+
for name in node.names:
|
| 260 |
+
if name.name in module:
|
| 261 |
+
self.tree[name.asname or name.name] = module[name.name]
|
| 262 |
+
elif name.name == "*":
|
| 263 |
+
for import_name, import_value in module.items():
|
| 264 |
+
if import_name.startswith("_"):
|
| 265 |
+
continue
|
| 266 |
+
self.tree[import_name] = import_value
|
| 267 |
+
|
| 268 |
+
|
| 269 |
+
def _create_tree(fullmodule, path, fname, source, tree, inpackage):
|
| 270 |
+
mbrowser = _ModuleBrowser(fullmodule, path, fname, tree, inpackage)
|
| 271 |
+
mbrowser.visit(ast.parse(source))
|
| 272 |
+
return mbrowser.tree
|
| 273 |
+
|
| 274 |
+
|
| 275 |
+
def _main():
|
| 276 |
+
"Print module output (default this file) for quick visual check."
|
| 277 |
+
import os
|
| 278 |
+
try:
|
| 279 |
+
mod = sys.argv[1]
|
| 280 |
+
except:
|
| 281 |
+
mod = __file__
|
| 282 |
+
if os.path.exists(mod):
|
| 283 |
+
path = [os.path.dirname(mod)]
|
| 284 |
+
mod = os.path.basename(mod)
|
| 285 |
+
if mod.lower().endswith(".py"):
|
| 286 |
+
mod = mod[:-3]
|
| 287 |
+
else:
|
| 288 |
+
path = []
|
| 289 |
+
tree = readmodule_ex(mod, path)
|
| 290 |
+
lineno_key = lambda a: getattr(a, 'lineno', 0)
|
| 291 |
+
objs = sorted(tree.values(), key=lineno_key, reverse=True)
|
| 292 |
+
indent_level = 2
|
| 293 |
+
while objs:
|
| 294 |
+
obj = objs.pop()
|
| 295 |
+
if isinstance(obj, list):
|
| 296 |
+
# Value is a __path__ key.
|
| 297 |
+
continue
|
| 298 |
+
if not hasattr(obj, 'indent'):
|
| 299 |
+
obj.indent = 0
|
| 300 |
+
|
| 301 |
+
if isinstance(obj, _Object):
|
| 302 |
+
new_objs = sorted(obj.children.values(),
|
| 303 |
+
key=lineno_key, reverse=True)
|
| 304 |
+
for ob in new_objs:
|
| 305 |
+
ob.indent = obj.indent + indent_level
|
| 306 |
+
objs.extend(new_objs)
|
| 307 |
+
if isinstance(obj, Class):
|
| 308 |
+
print("{}class {} {} {}"
|
| 309 |
+
.format(' ' * obj.indent, obj.name, obj.super, obj.lineno))
|
| 310 |
+
elif isinstance(obj, Function):
|
| 311 |
+
print("{}def {} {}".format(' ' * obj.indent, obj.name, obj.lineno))
|
| 312 |
+
|
| 313 |
+
if __name__ == "__main__":
|
| 314 |
+
_main()
|
lib/python3.10/pydoc.py
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
lib/python3.10/queue.py
ADDED
|
@@ -0,0 +1,326 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
'''A multi-producer, multi-consumer queue.'''
|
| 2 |
+
|
| 3 |
+
import threading
|
| 4 |
+
import types
|
| 5 |
+
from collections import deque
|
| 6 |
+
from heapq import heappush, heappop
|
| 7 |
+
from time import monotonic as time
|
| 8 |
+
try:
|
| 9 |
+
from _queue import SimpleQueue
|
| 10 |
+
except ImportError:
|
| 11 |
+
SimpleQueue = None
|
| 12 |
+
|
| 13 |
+
__all__ = ['Empty', 'Full', 'Queue', 'PriorityQueue', 'LifoQueue', 'SimpleQueue']
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
try:
|
| 17 |
+
from _queue import Empty
|
| 18 |
+
except ImportError:
|
| 19 |
+
class Empty(Exception):
|
| 20 |
+
'Exception raised by Queue.get(block=0)/get_nowait().'
|
| 21 |
+
pass
|
| 22 |
+
|
| 23 |
+
class Full(Exception):
|
| 24 |
+
'Exception raised by Queue.put(block=0)/put_nowait().'
|
| 25 |
+
pass
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
class Queue:
|
| 29 |
+
'''Create a queue object with a given maximum size.
|
| 30 |
+
|
| 31 |
+
If maxsize is <= 0, the queue size is infinite.
|
| 32 |
+
'''
|
| 33 |
+
|
| 34 |
+
def __init__(self, maxsize=0):
|
| 35 |
+
self.maxsize = maxsize
|
| 36 |
+
self._init(maxsize)
|
| 37 |
+
|
| 38 |
+
# mutex must be held whenever the queue is mutating. All methods
|
| 39 |
+
# that acquire mutex must release it before returning. mutex
|
| 40 |
+
# is shared between the three conditions, so acquiring and
|
| 41 |
+
# releasing the conditions also acquires and releases mutex.
|
| 42 |
+
self.mutex = threading.Lock()
|
| 43 |
+
|
| 44 |
+
# Notify not_empty whenever an item is added to the queue; a
|
| 45 |
+
# thread waiting to get is notified then.
|
| 46 |
+
self.not_empty = threading.Condition(self.mutex)
|
| 47 |
+
|
| 48 |
+
# Notify not_full whenever an item is removed from the queue;
|
| 49 |
+
# a thread waiting to put is notified then.
|
| 50 |
+
self.not_full = threading.Condition(self.mutex)
|
| 51 |
+
|
| 52 |
+
# Notify all_tasks_done whenever the number of unfinished tasks
|
| 53 |
+
# drops to zero; thread waiting to join() is notified to resume
|
| 54 |
+
self.all_tasks_done = threading.Condition(self.mutex)
|
| 55 |
+
self.unfinished_tasks = 0
|
| 56 |
+
|
| 57 |
+
def task_done(self):
|
| 58 |
+
'''Indicate that a formerly enqueued task is complete.
|
| 59 |
+
|
| 60 |
+
Used by Queue consumer threads. For each get() used to fetch a task,
|
| 61 |
+
a subsequent call to task_done() tells the queue that the processing
|
| 62 |
+
on the task is complete.
|
| 63 |
+
|
| 64 |
+
If a join() is currently blocking, it will resume when all items
|
| 65 |
+
have been processed (meaning that a task_done() call was received
|
| 66 |
+
for every item that had been put() into the queue).
|
| 67 |
+
|
| 68 |
+
Raises a ValueError if called more times than there were items
|
| 69 |
+
placed in the queue.
|
| 70 |
+
'''
|
| 71 |
+
with self.all_tasks_done:
|
| 72 |
+
unfinished = self.unfinished_tasks - 1
|
| 73 |
+
if unfinished <= 0:
|
| 74 |
+
if unfinished < 0:
|
| 75 |
+
raise ValueError('task_done() called too many times')
|
| 76 |
+
self.all_tasks_done.notify_all()
|
| 77 |
+
self.unfinished_tasks = unfinished
|
| 78 |
+
|
| 79 |
+
def join(self):
|
| 80 |
+
'''Blocks until all items in the Queue have been gotten and processed.
|
| 81 |
+
|
| 82 |
+
The count of unfinished tasks goes up whenever an item is added to the
|
| 83 |
+
queue. The count goes down whenever a consumer thread calls task_done()
|
| 84 |
+
to indicate the item was retrieved and all work on it is complete.
|
| 85 |
+
|
| 86 |
+
When the count of unfinished tasks drops to zero, join() unblocks.
|
| 87 |
+
'''
|
| 88 |
+
with self.all_tasks_done:
|
| 89 |
+
while self.unfinished_tasks:
|
| 90 |
+
self.all_tasks_done.wait()
|
| 91 |
+
|
| 92 |
+
def qsize(self):
|
| 93 |
+
'''Return the approximate size of the queue (not reliable!).'''
|
| 94 |
+
with self.mutex:
|
| 95 |
+
return self._qsize()
|
| 96 |
+
|
| 97 |
+
def empty(self):
|
| 98 |
+
'''Return True if the queue is empty, False otherwise (not reliable!).
|
| 99 |
+
|
| 100 |
+
This method is likely to be removed at some point. Use qsize() == 0
|
| 101 |
+
as a direct substitute, but be aware that either approach risks a race
|
| 102 |
+
condition where a queue can grow before the result of empty() or
|
| 103 |
+
qsize() can be used.
|
| 104 |
+
|
| 105 |
+
To create code that needs to wait for all queued tasks to be
|
| 106 |
+
completed, the preferred technique is to use the join() method.
|
| 107 |
+
'''
|
| 108 |
+
with self.mutex:
|
| 109 |
+
return not self._qsize()
|
| 110 |
+
|
| 111 |
+
def full(self):
|
| 112 |
+
'''Return True if the queue is full, False otherwise (not reliable!).
|
| 113 |
+
|
| 114 |
+
This method is likely to be removed at some point. Use qsize() >= n
|
| 115 |
+
as a direct substitute, but be aware that either approach risks a race
|
| 116 |
+
condition where a queue can shrink before the result of full() or
|
| 117 |
+
qsize() can be used.
|
| 118 |
+
'''
|
| 119 |
+
with self.mutex:
|
| 120 |
+
return 0 < self.maxsize <= self._qsize()
|
| 121 |
+
|
| 122 |
+
def put(self, item, block=True, timeout=None):
|
| 123 |
+
'''Put an item into the queue.
|
| 124 |
+
|
| 125 |
+
If optional args 'block' is true and 'timeout' is None (the default),
|
| 126 |
+
block if necessary until a free slot is available. If 'timeout' is
|
| 127 |
+
a non-negative number, it blocks at most 'timeout' seconds and raises
|
| 128 |
+
the Full exception if no free slot was available within that time.
|
| 129 |
+
Otherwise ('block' is false), put an item on the queue if a free slot
|
| 130 |
+
is immediately available, else raise the Full exception ('timeout'
|
| 131 |
+
is ignored in that case).
|
| 132 |
+
'''
|
| 133 |
+
with self.not_full:
|
| 134 |
+
if self.maxsize > 0:
|
| 135 |
+
if not block:
|
| 136 |
+
if self._qsize() >= self.maxsize:
|
| 137 |
+
raise Full
|
| 138 |
+
elif timeout is None:
|
| 139 |
+
while self._qsize() >= self.maxsize:
|
| 140 |
+
self.not_full.wait()
|
| 141 |
+
elif timeout < 0:
|
| 142 |
+
raise ValueError("'timeout' must be a non-negative number")
|
| 143 |
+
else:
|
| 144 |
+
endtime = time() + timeout
|
| 145 |
+
while self._qsize() >= self.maxsize:
|
| 146 |
+
remaining = endtime - time()
|
| 147 |
+
if remaining <= 0.0:
|
| 148 |
+
raise Full
|
| 149 |
+
self.not_full.wait(remaining)
|
| 150 |
+
self._put(item)
|
| 151 |
+
self.unfinished_tasks += 1
|
| 152 |
+
self.not_empty.notify()
|
| 153 |
+
|
| 154 |
+
def get(self, block=True, timeout=None):
|
| 155 |
+
'''Remove and return an item from the queue.
|
| 156 |
+
|
| 157 |
+
If optional args 'block' is true and 'timeout' is None (the default),
|
| 158 |
+
block if necessary until an item is available. If 'timeout' is
|
| 159 |
+
a non-negative number, it blocks at most 'timeout' seconds and raises
|
| 160 |
+
the Empty exception if no item was available within that time.
|
| 161 |
+
Otherwise ('block' is false), return an item if one is immediately
|
| 162 |
+
available, else raise the Empty exception ('timeout' is ignored
|
| 163 |
+
in that case).
|
| 164 |
+
'''
|
| 165 |
+
with self.not_empty:
|
| 166 |
+
if not block:
|
| 167 |
+
if not self._qsize():
|
| 168 |
+
raise Empty
|
| 169 |
+
elif timeout is None:
|
| 170 |
+
while not self._qsize():
|
| 171 |
+
self.not_empty.wait()
|
| 172 |
+
elif timeout < 0:
|
| 173 |
+
raise ValueError("'timeout' must be a non-negative number")
|
| 174 |
+
else:
|
| 175 |
+
endtime = time() + timeout
|
| 176 |
+
while not self._qsize():
|
| 177 |
+
remaining = endtime - time()
|
| 178 |
+
if remaining <= 0.0:
|
| 179 |
+
raise Empty
|
| 180 |
+
self.not_empty.wait(remaining)
|
| 181 |
+
item = self._get()
|
| 182 |
+
self.not_full.notify()
|
| 183 |
+
return item
|
| 184 |
+
|
| 185 |
+
def put_nowait(self, item):
|
| 186 |
+
'''Put an item into the queue without blocking.
|
| 187 |
+
|
| 188 |
+
Only enqueue the item if a free slot is immediately available.
|
| 189 |
+
Otherwise raise the Full exception.
|
| 190 |
+
'''
|
| 191 |
+
return self.put(item, block=False)
|
| 192 |
+
|
| 193 |
+
def get_nowait(self):
|
| 194 |
+
'''Remove and return an item from the queue without blocking.
|
| 195 |
+
|
| 196 |
+
Only get an item if one is immediately available. Otherwise
|
| 197 |
+
raise the Empty exception.
|
| 198 |
+
'''
|
| 199 |
+
return self.get(block=False)
|
| 200 |
+
|
| 201 |
+
# Override these methods to implement other queue organizations
|
| 202 |
+
# (e.g. stack or priority queue).
|
| 203 |
+
# These will only be called with appropriate locks held
|
| 204 |
+
|
| 205 |
+
# Initialize the queue representation
|
| 206 |
+
def _init(self, maxsize):
|
| 207 |
+
self.queue = deque()
|
| 208 |
+
|
| 209 |
+
def _qsize(self):
|
| 210 |
+
return len(self.queue)
|
| 211 |
+
|
| 212 |
+
# Put a new item in the queue
|
| 213 |
+
def _put(self, item):
|
| 214 |
+
self.queue.append(item)
|
| 215 |
+
|
| 216 |
+
# Get an item from the queue
|
| 217 |
+
def _get(self):
|
| 218 |
+
return self.queue.popleft()
|
| 219 |
+
|
| 220 |
+
__class_getitem__ = classmethod(types.GenericAlias)
|
| 221 |
+
|
| 222 |
+
|
| 223 |
+
class PriorityQueue(Queue):
|
| 224 |
+
'''Variant of Queue that retrieves open entries in priority order (lowest first).
|
| 225 |
+
|
| 226 |
+
Entries are typically tuples of the form: (priority number, data).
|
| 227 |
+
'''
|
| 228 |
+
|
| 229 |
+
def _init(self, maxsize):
|
| 230 |
+
self.queue = []
|
| 231 |
+
|
| 232 |
+
def _qsize(self):
|
| 233 |
+
return len(self.queue)
|
| 234 |
+
|
| 235 |
+
def _put(self, item):
|
| 236 |
+
heappush(self.queue, item)
|
| 237 |
+
|
| 238 |
+
def _get(self):
|
| 239 |
+
return heappop(self.queue)
|
| 240 |
+
|
| 241 |
+
|
| 242 |
+
class LifoQueue(Queue):
|
| 243 |
+
'''Variant of Queue that retrieves most recently added entries first.'''
|
| 244 |
+
|
| 245 |
+
def _init(self, maxsize):
|
| 246 |
+
self.queue = []
|
| 247 |
+
|
| 248 |
+
def _qsize(self):
|
| 249 |
+
return len(self.queue)
|
| 250 |
+
|
| 251 |
+
def _put(self, item):
|
| 252 |
+
self.queue.append(item)
|
| 253 |
+
|
| 254 |
+
def _get(self):
|
| 255 |
+
return self.queue.pop()
|
| 256 |
+
|
| 257 |
+
|
| 258 |
+
class _PySimpleQueue:
|
| 259 |
+
'''Simple, unbounded FIFO queue.
|
| 260 |
+
|
| 261 |
+
This pure Python implementation is not reentrant.
|
| 262 |
+
'''
|
| 263 |
+
# Note: while this pure Python version provides fairness
|
| 264 |
+
# (by using a threading.Semaphore which is itself fair, being based
|
| 265 |
+
# on threading.Condition), fairness is not part of the API contract.
|
| 266 |
+
# This allows the C version to use a different implementation.
|
| 267 |
+
|
| 268 |
+
def __init__(self):
|
| 269 |
+
self._queue = deque()
|
| 270 |
+
self._count = threading.Semaphore(0)
|
| 271 |
+
|
| 272 |
+
def put(self, item, block=True, timeout=None):
|
| 273 |
+
'''Put the item on the queue.
|
| 274 |
+
|
| 275 |
+
The optional 'block' and 'timeout' arguments are ignored, as this method
|
| 276 |
+
never blocks. They are provided for compatibility with the Queue class.
|
| 277 |
+
'''
|
| 278 |
+
self._queue.append(item)
|
| 279 |
+
self._count.release()
|
| 280 |
+
|
| 281 |
+
def get(self, block=True, timeout=None):
|
| 282 |
+
'''Remove and return an item from the queue.
|
| 283 |
+
|
| 284 |
+
If optional args 'block' is true and 'timeout' is None (the default),
|
| 285 |
+
block if necessary until an item is available. If 'timeout' is
|
| 286 |
+
a non-negative number, it blocks at most 'timeout' seconds and raises
|
| 287 |
+
the Empty exception if no item was available within that time.
|
| 288 |
+
Otherwise ('block' is false), return an item if one is immediately
|
| 289 |
+
available, else raise the Empty exception ('timeout' is ignored
|
| 290 |
+
in that case).
|
| 291 |
+
'''
|
| 292 |
+
if timeout is not None and timeout < 0:
|
| 293 |
+
raise ValueError("'timeout' must be a non-negative number")
|
| 294 |
+
if not self._count.acquire(block, timeout):
|
| 295 |
+
raise Empty
|
| 296 |
+
return self._queue.popleft()
|
| 297 |
+
|
| 298 |
+
def put_nowait(self, item):
|
| 299 |
+
'''Put an item into the queue without blocking.
|
| 300 |
+
|
| 301 |
+
This is exactly equivalent to `put(item, block=False)` and is only provided
|
| 302 |
+
for compatibility with the Queue class.
|
| 303 |
+
'''
|
| 304 |
+
return self.put(item, block=False)
|
| 305 |
+
|
| 306 |
+
def get_nowait(self):
|
| 307 |
+
'''Remove and return an item from the queue without blocking.
|
| 308 |
+
|
| 309 |
+
Only get an item if one is immediately available. Otherwise
|
| 310 |
+
raise the Empty exception.
|
| 311 |
+
'''
|
| 312 |
+
return self.get(block=False)
|
| 313 |
+
|
| 314 |
+
def empty(self):
|
| 315 |
+
'''Return True if the queue is empty, False otherwise (not reliable!).'''
|
| 316 |
+
return len(self._queue) == 0
|
| 317 |
+
|
| 318 |
+
def qsize(self):
|
| 319 |
+
'''Return the approximate size of the queue (not reliable!).'''
|
| 320 |
+
return len(self._queue)
|
| 321 |
+
|
| 322 |
+
__class_getitem__ = classmethod(types.GenericAlias)
|
| 323 |
+
|
| 324 |
+
|
| 325 |
+
if SimpleQueue is None:
|
| 326 |
+
SimpleQueue = _PySimpleQueue
|
lib/python3.10/quopri.py
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#! /usr/bin/env python3
|
| 2 |
+
|
| 3 |
+
"""Conversions to/from quoted-printable transport encoding as per RFC 1521."""
|
| 4 |
+
|
| 5 |
+
# (Dec 1991 version).
|
| 6 |
+
|
| 7 |
+
__all__ = ["encode", "decode", "encodestring", "decodestring"]
|
| 8 |
+
|
| 9 |
+
ESCAPE = b'='
|
| 10 |
+
MAXLINESIZE = 76
|
| 11 |
+
HEX = b'0123456789ABCDEF'
|
| 12 |
+
EMPTYSTRING = b''
|
| 13 |
+
|
| 14 |
+
try:
|
| 15 |
+
from binascii import a2b_qp, b2a_qp
|
| 16 |
+
except ImportError:
|
| 17 |
+
a2b_qp = None
|
| 18 |
+
b2a_qp = None
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def needsquoting(c, quotetabs, header):
|
| 22 |
+
"""Decide whether a particular byte ordinal needs to be quoted.
|
| 23 |
+
|
| 24 |
+
The 'quotetabs' flag indicates whether embedded tabs and spaces should be
|
| 25 |
+
quoted. Note that line-ending tabs and spaces are always encoded, as per
|
| 26 |
+
RFC 1521.
|
| 27 |
+
"""
|
| 28 |
+
assert isinstance(c, bytes)
|
| 29 |
+
if c in b' \t':
|
| 30 |
+
return quotetabs
|
| 31 |
+
# if header, we have to escape _ because _ is used to escape space
|
| 32 |
+
if c == b'_':
|
| 33 |
+
return header
|
| 34 |
+
return c == ESCAPE or not (b' ' <= c <= b'~')
|
| 35 |
+
|
| 36 |
+
def quote(c):
|
| 37 |
+
"""Quote a single character."""
|
| 38 |
+
assert isinstance(c, bytes) and len(c)==1
|
| 39 |
+
c = ord(c)
|
| 40 |
+
return ESCAPE + bytes((HEX[c//16], HEX[c%16]))
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def encode(input, output, quotetabs, header=False):
|
| 45 |
+
"""Read 'input', apply quoted-printable encoding, and write to 'output'.
|
| 46 |
+
|
| 47 |
+
'input' and 'output' are binary file objects. The 'quotetabs' flag
|
| 48 |
+
indicates whether embedded tabs and spaces should be quoted. Note that
|
| 49 |
+
line-ending tabs and spaces are always encoded, as per RFC 1521.
|
| 50 |
+
The 'header' flag indicates whether we are encoding spaces as _ as per RFC
|
| 51 |
+
1522."""
|
| 52 |
+
|
| 53 |
+
if b2a_qp is not None:
|
| 54 |
+
data = input.read()
|
| 55 |
+
odata = b2a_qp(data, quotetabs=quotetabs, header=header)
|
| 56 |
+
output.write(odata)
|
| 57 |
+
return
|
| 58 |
+
|
| 59 |
+
def write(s, output=output, lineEnd=b'\n'):
|
| 60 |
+
# RFC 1521 requires that the line ending in a space or tab must have
|
| 61 |
+
# that trailing character encoded.
|
| 62 |
+
if s and s[-1:] in b' \t':
|
| 63 |
+
output.write(s[:-1] + quote(s[-1:]) + lineEnd)
|
| 64 |
+
elif s == b'.':
|
| 65 |
+
output.write(quote(s) + lineEnd)
|
| 66 |
+
else:
|
| 67 |
+
output.write(s + lineEnd)
|
| 68 |
+
|
| 69 |
+
prevline = None
|
| 70 |
+
while 1:
|
| 71 |
+
line = input.readline()
|
| 72 |
+
if not line:
|
| 73 |
+
break
|
| 74 |
+
outline = []
|
| 75 |
+
# Strip off any readline induced trailing newline
|
| 76 |
+
stripped = b''
|
| 77 |
+
if line[-1:] == b'\n':
|
| 78 |
+
line = line[:-1]
|
| 79 |
+
stripped = b'\n'
|
| 80 |
+
# Calculate the un-length-limited encoded line
|
| 81 |
+
for c in line:
|
| 82 |
+
c = bytes((c,))
|
| 83 |
+
if needsquoting(c, quotetabs, header):
|
| 84 |
+
c = quote(c)
|
| 85 |
+
if header and c == b' ':
|
| 86 |
+
outline.append(b'_')
|
| 87 |
+
else:
|
| 88 |
+
outline.append(c)
|
| 89 |
+
# First, write out the previous line
|
| 90 |
+
if prevline is not None:
|
| 91 |
+
write(prevline)
|
| 92 |
+
# Now see if we need any soft line breaks because of RFC-imposed
|
| 93 |
+
# length limitations. Then do the thisline->prevline dance.
|
| 94 |
+
thisline = EMPTYSTRING.join(outline)
|
| 95 |
+
while len(thisline) > MAXLINESIZE:
|
| 96 |
+
# Don't forget to include the soft line break `=' sign in the
|
| 97 |
+
# length calculation!
|
| 98 |
+
write(thisline[:MAXLINESIZE-1], lineEnd=b'=\n')
|
| 99 |
+
thisline = thisline[MAXLINESIZE-1:]
|
| 100 |
+
# Write out the current line
|
| 101 |
+
prevline = thisline
|
| 102 |
+
# Write out the last line, without a trailing newline
|
| 103 |
+
if prevline is not None:
|
| 104 |
+
write(prevline, lineEnd=stripped)
|
| 105 |
+
|
| 106 |
+
def encodestring(s, quotetabs=False, header=False):
|
| 107 |
+
if b2a_qp is not None:
|
| 108 |
+
return b2a_qp(s, quotetabs=quotetabs, header=header)
|
| 109 |
+
from io import BytesIO
|
| 110 |
+
infp = BytesIO(s)
|
| 111 |
+
outfp = BytesIO()
|
| 112 |
+
encode(infp, outfp, quotetabs, header)
|
| 113 |
+
return outfp.getvalue()
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
def decode(input, output, header=False):
|
| 118 |
+
"""Read 'input', apply quoted-printable decoding, and write to 'output'.
|
| 119 |
+
'input' and 'output' are binary file objects.
|
| 120 |
+
If 'header' is true, decode underscore as space (per RFC 1522)."""
|
| 121 |
+
|
| 122 |
+
if a2b_qp is not None:
|
| 123 |
+
data = input.read()
|
| 124 |
+
odata = a2b_qp(data, header=header)
|
| 125 |
+
output.write(odata)
|
| 126 |
+
return
|
| 127 |
+
|
| 128 |
+
new = b''
|
| 129 |
+
while 1:
|
| 130 |
+
line = input.readline()
|
| 131 |
+
if not line: break
|
| 132 |
+
i, n = 0, len(line)
|
| 133 |
+
if n > 0 and line[n-1:n] == b'\n':
|
| 134 |
+
partial = 0; n = n-1
|
| 135 |
+
# Strip trailing whitespace
|
| 136 |
+
while n > 0 and line[n-1:n] in b" \t\r":
|
| 137 |
+
n = n-1
|
| 138 |
+
else:
|
| 139 |
+
partial = 1
|
| 140 |
+
while i < n:
|
| 141 |
+
c = line[i:i+1]
|
| 142 |
+
if c == b'_' and header:
|
| 143 |
+
new = new + b' '; i = i+1
|
| 144 |
+
elif c != ESCAPE:
|
| 145 |
+
new = new + c; i = i+1
|
| 146 |
+
elif i+1 == n and not partial:
|
| 147 |
+
partial = 1; break
|
| 148 |
+
elif i+1 < n and line[i+1:i+2] == ESCAPE:
|
| 149 |
+
new = new + ESCAPE; i = i+2
|
| 150 |
+
elif i+2 < n and ishex(line[i+1:i+2]) and ishex(line[i+2:i+3]):
|
| 151 |
+
new = new + bytes((unhex(line[i+1:i+3]),)); i = i+3
|
| 152 |
+
else: # Bad escape sequence -- leave it in
|
| 153 |
+
new = new + c; i = i+1
|
| 154 |
+
if not partial:
|
| 155 |
+
output.write(new + b'\n')
|
| 156 |
+
new = b''
|
| 157 |
+
if new:
|
| 158 |
+
output.write(new)
|
| 159 |
+
|
| 160 |
+
def decodestring(s, header=False):
|
| 161 |
+
if a2b_qp is not None:
|
| 162 |
+
return a2b_qp(s, header=header)
|
| 163 |
+
from io import BytesIO
|
| 164 |
+
infp = BytesIO(s)
|
| 165 |
+
outfp = BytesIO()
|
| 166 |
+
decode(infp, outfp, header=header)
|
| 167 |
+
return outfp.getvalue()
|
| 168 |
+
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
# Other helper functions
|
| 172 |
+
def ishex(c):
|
| 173 |
+
"""Return true if the byte ordinal 'c' is a hexadecimal digit in ASCII."""
|
| 174 |
+
assert isinstance(c, bytes)
|
| 175 |
+
return b'0' <= c <= b'9' or b'a' <= c <= b'f' or b'A' <= c <= b'F'
|
| 176 |
+
|
| 177 |
+
def unhex(s):
|
| 178 |
+
"""Get the integer value of a hexadecimal number."""
|
| 179 |
+
bits = 0
|
| 180 |
+
for c in s:
|
| 181 |
+
c = bytes((c,))
|
| 182 |
+
if b'0' <= c <= b'9':
|
| 183 |
+
i = ord('0')
|
| 184 |
+
elif b'a' <= c <= b'f':
|
| 185 |
+
i = ord('a')-10
|
| 186 |
+
elif b'A' <= c <= b'F':
|
| 187 |
+
i = ord(b'A')-10
|
| 188 |
+
else:
|
| 189 |
+
assert False, "non-hex digit "+repr(c)
|
| 190 |
+
bits = bits*16 + (ord(c) - i)
|
| 191 |
+
return bits
|
| 192 |
+
|
| 193 |
+
|
| 194 |
+
|
| 195 |
+
def main():
|
| 196 |
+
import sys
|
| 197 |
+
import getopt
|
| 198 |
+
try:
|
| 199 |
+
opts, args = getopt.getopt(sys.argv[1:], 'td')
|
| 200 |
+
except getopt.error as msg:
|
| 201 |
+
sys.stdout = sys.stderr
|
| 202 |
+
print(msg)
|
| 203 |
+
print("usage: quopri [-t | -d] [file] ...")
|
| 204 |
+
print("-t: quote tabs")
|
| 205 |
+
print("-d: decode; default encode")
|
| 206 |
+
sys.exit(2)
|
| 207 |
+
deco = False
|
| 208 |
+
tabs = False
|
| 209 |
+
for o, a in opts:
|
| 210 |
+
if o == '-t': tabs = True
|
| 211 |
+
if o == '-d': deco = True
|
| 212 |
+
if tabs and deco:
|
| 213 |
+
sys.stdout = sys.stderr
|
| 214 |
+
print("-t and -d are mutually exclusive")
|
| 215 |
+
sys.exit(2)
|
| 216 |
+
if not args: args = ['-']
|
| 217 |
+
sts = 0
|
| 218 |
+
for file in args:
|
| 219 |
+
if file == '-':
|
| 220 |
+
fp = sys.stdin.buffer
|
| 221 |
+
else:
|
| 222 |
+
try:
|
| 223 |
+
fp = open(file, "rb")
|
| 224 |
+
except OSError as msg:
|
| 225 |
+
sys.stderr.write("%s: can't open (%s)\n" % (file, msg))
|
| 226 |
+
sts = 1
|
| 227 |
+
continue
|
| 228 |
+
try:
|
| 229 |
+
if deco:
|
| 230 |
+
decode(fp, sys.stdout.buffer)
|
| 231 |
+
else:
|
| 232 |
+
encode(fp, sys.stdout.buffer, tabs)
|
| 233 |
+
finally:
|
| 234 |
+
if file != '-':
|
| 235 |
+
fp.close()
|
| 236 |
+
if sts:
|
| 237 |
+
sys.exit(sts)
|
| 238 |
+
|
| 239 |
+
|
| 240 |
+
|
| 241 |
+
if __name__ == '__main__':
|
| 242 |
+
main()
|
lib/python3.10/random.py
ADDED
|
@@ -0,0 +1,930 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Random variable generators.
|
| 2 |
+
|
| 3 |
+
bytes
|
| 4 |
+
-----
|
| 5 |
+
uniform bytes (values between 0 and 255)
|
| 6 |
+
|
| 7 |
+
integers
|
| 8 |
+
--------
|
| 9 |
+
uniform within range
|
| 10 |
+
|
| 11 |
+
sequences
|
| 12 |
+
---------
|
| 13 |
+
pick random element
|
| 14 |
+
pick random sample
|
| 15 |
+
pick weighted random sample
|
| 16 |
+
generate random permutation
|
| 17 |
+
|
| 18 |
+
distributions on the real line:
|
| 19 |
+
------------------------------
|
| 20 |
+
uniform
|
| 21 |
+
triangular
|
| 22 |
+
normal (Gaussian)
|
| 23 |
+
lognormal
|
| 24 |
+
negative exponential
|
| 25 |
+
gamma
|
| 26 |
+
beta
|
| 27 |
+
pareto
|
| 28 |
+
Weibull
|
| 29 |
+
|
| 30 |
+
distributions on the circle (angles 0 to 2pi)
|
| 31 |
+
---------------------------------------------
|
| 32 |
+
circular uniform
|
| 33 |
+
von Mises
|
| 34 |
+
|
| 35 |
+
General notes on the underlying Mersenne Twister core generator:
|
| 36 |
+
|
| 37 |
+
* The period is 2**19937-1.
|
| 38 |
+
* It is one of the most extensively tested generators in existence.
|
| 39 |
+
* The random() method is implemented in C, executes in a single Python step,
|
| 40 |
+
and is, therefore, threadsafe.
|
| 41 |
+
|
| 42 |
+
"""
|
| 43 |
+
|
| 44 |
+
# Translated by Guido van Rossum from C source provided by
|
| 45 |
+
# Adrian Baddeley. Adapted by Raymond Hettinger for use with
|
| 46 |
+
# the Mersenne Twister and os.urandom() core generators.
|
| 47 |
+
|
| 48 |
+
from warnings import warn as _warn
|
| 49 |
+
from math import log as _log, exp as _exp, pi as _pi, e as _e, ceil as _ceil
|
| 50 |
+
from math import sqrt as _sqrt, acos as _acos, cos as _cos, sin as _sin
|
| 51 |
+
from math import tau as TWOPI, floor as _floor, isfinite as _isfinite
|
| 52 |
+
from os import urandom as _urandom
|
| 53 |
+
from _collections_abc import Set as _Set, Sequence as _Sequence
|
| 54 |
+
from operator import index as _index
|
| 55 |
+
from itertools import accumulate as _accumulate, repeat as _repeat
|
| 56 |
+
from bisect import bisect as _bisect
|
| 57 |
+
import os as _os
|
| 58 |
+
import _random
|
| 59 |
+
|
| 60 |
+
try:
|
| 61 |
+
# hashlib is pretty heavy to load, try lean internal module first
|
| 62 |
+
from _sha512 import sha512 as _sha512
|
| 63 |
+
except ImportError:
|
| 64 |
+
# fallback to official implementation
|
| 65 |
+
from hashlib import sha512 as _sha512
|
| 66 |
+
|
| 67 |
+
__all__ = [
|
| 68 |
+
"Random",
|
| 69 |
+
"SystemRandom",
|
| 70 |
+
"betavariate",
|
| 71 |
+
"choice",
|
| 72 |
+
"choices",
|
| 73 |
+
"expovariate",
|
| 74 |
+
"gammavariate",
|
| 75 |
+
"gauss",
|
| 76 |
+
"getrandbits",
|
| 77 |
+
"getstate",
|
| 78 |
+
"lognormvariate",
|
| 79 |
+
"normalvariate",
|
| 80 |
+
"paretovariate",
|
| 81 |
+
"randbytes",
|
| 82 |
+
"randint",
|
| 83 |
+
"random",
|
| 84 |
+
"randrange",
|
| 85 |
+
"sample",
|
| 86 |
+
"seed",
|
| 87 |
+
"setstate",
|
| 88 |
+
"shuffle",
|
| 89 |
+
"triangular",
|
| 90 |
+
"uniform",
|
| 91 |
+
"vonmisesvariate",
|
| 92 |
+
"weibullvariate",
|
| 93 |
+
]
|
| 94 |
+
|
| 95 |
+
NV_MAGICCONST = 4 * _exp(-0.5) / _sqrt(2.0)
|
| 96 |
+
LOG4 = _log(4.0)
|
| 97 |
+
SG_MAGICCONST = 1.0 + _log(4.5)
|
| 98 |
+
BPF = 53 # Number of bits in a float
|
| 99 |
+
RECIP_BPF = 2 ** -BPF
|
| 100 |
+
_ONE = 1
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
class Random(_random.Random):
|
| 104 |
+
"""Random number generator base class used by bound module functions.
|
| 105 |
+
|
| 106 |
+
Used to instantiate instances of Random to get generators that don't
|
| 107 |
+
share state.
|
| 108 |
+
|
| 109 |
+
Class Random can also be subclassed if you want to use a different basic
|
| 110 |
+
generator of your own devising: in that case, override the following
|
| 111 |
+
methods: random(), seed(), getstate(), and setstate().
|
| 112 |
+
Optionally, implement a getrandbits() method so that randrange()
|
| 113 |
+
can cover arbitrarily large ranges.
|
| 114 |
+
|
| 115 |
+
"""
|
| 116 |
+
|
| 117 |
+
VERSION = 3 # used by getstate/setstate
|
| 118 |
+
|
| 119 |
+
def __init__(self, x=None):
|
| 120 |
+
"""Initialize an instance.
|
| 121 |
+
|
| 122 |
+
Optional argument x controls seeding, as for Random.seed().
|
| 123 |
+
"""
|
| 124 |
+
|
| 125 |
+
self.seed(x)
|
| 126 |
+
self.gauss_next = None
|
| 127 |
+
|
| 128 |
+
def seed(self, a=None, version=2):
|
| 129 |
+
"""Initialize internal state from a seed.
|
| 130 |
+
|
| 131 |
+
The only supported seed types are None, int, float,
|
| 132 |
+
str, bytes, and bytearray.
|
| 133 |
+
|
| 134 |
+
None or no argument seeds from current time or from an operating
|
| 135 |
+
system specific randomness source if available.
|
| 136 |
+
|
| 137 |
+
If *a* is an int, all bits are used.
|
| 138 |
+
|
| 139 |
+
For version 2 (the default), all of the bits are used if *a* is a str,
|
| 140 |
+
bytes, or bytearray. For version 1 (provided for reproducing random
|
| 141 |
+
sequences from older versions of Python), the algorithm for str and
|
| 142 |
+
bytes generates a narrower range of seeds.
|
| 143 |
+
|
| 144 |
+
"""
|
| 145 |
+
|
| 146 |
+
if version == 1 and isinstance(a, (str, bytes)):
|
| 147 |
+
a = a.decode('latin-1') if isinstance(a, bytes) else a
|
| 148 |
+
x = ord(a[0]) << 7 if a else 0
|
| 149 |
+
for c in map(ord, a):
|
| 150 |
+
x = ((1000003 * x) ^ c) & 0xFFFFFFFFFFFFFFFF
|
| 151 |
+
x ^= len(a)
|
| 152 |
+
a = -2 if x == -1 else x
|
| 153 |
+
|
| 154 |
+
elif version == 2 and isinstance(a, (str, bytes, bytearray)):
|
| 155 |
+
if isinstance(a, str):
|
| 156 |
+
a = a.encode()
|
| 157 |
+
a = int.from_bytes(a + _sha512(a).digest(), 'big')
|
| 158 |
+
|
| 159 |
+
elif not isinstance(a, (type(None), int, float, str, bytes, bytearray)):
|
| 160 |
+
_warn('Seeding based on hashing is deprecated\n'
|
| 161 |
+
'since Python 3.9 and will be removed in a subsequent '
|
| 162 |
+
'version. The only \n'
|
| 163 |
+
'supported seed types are: None, '
|
| 164 |
+
'int, float, str, bytes, and bytearray.',
|
| 165 |
+
DeprecationWarning, 2)
|
| 166 |
+
|
| 167 |
+
super().seed(a)
|
| 168 |
+
self.gauss_next = None
|
| 169 |
+
|
| 170 |
+
def getstate(self):
|
| 171 |
+
"""Return internal state; can be passed to setstate() later."""
|
| 172 |
+
return self.VERSION, super().getstate(), self.gauss_next
|
| 173 |
+
|
| 174 |
+
def setstate(self, state):
|
| 175 |
+
"""Restore internal state from object returned by getstate()."""
|
| 176 |
+
version = state[0]
|
| 177 |
+
if version == 3:
|
| 178 |
+
version, internalstate, self.gauss_next = state
|
| 179 |
+
super().setstate(internalstate)
|
| 180 |
+
elif version == 2:
|
| 181 |
+
version, internalstate, self.gauss_next = state
|
| 182 |
+
# In version 2, the state was saved as signed ints, which causes
|
| 183 |
+
# inconsistencies between 32/64-bit systems. The state is
|
| 184 |
+
# really unsigned 32-bit ints, so we convert negative ints from
|
| 185 |
+
# version 2 to positive longs for version 3.
|
| 186 |
+
try:
|
| 187 |
+
internalstate = tuple(x % (2 ** 32) for x in internalstate)
|
| 188 |
+
except ValueError as e:
|
| 189 |
+
raise TypeError from e
|
| 190 |
+
super().setstate(internalstate)
|
| 191 |
+
else:
|
| 192 |
+
raise ValueError("state with version %s passed to "
|
| 193 |
+
"Random.setstate() of version %s" %
|
| 194 |
+
(version, self.VERSION))
|
| 195 |
+
|
| 196 |
+
|
| 197 |
+
## -------------------------------------------------------
|
| 198 |
+
## ---- Methods below this point do not need to be overridden or extended
|
| 199 |
+
## ---- when subclassing for the purpose of using a different core generator.
|
| 200 |
+
|
| 201 |
+
|
| 202 |
+
## -------------------- pickle support -------------------
|
| 203 |
+
|
| 204 |
+
# Issue 17489: Since __reduce__ was defined to fix #759889 this is no
|
| 205 |
+
# longer called; we leave it here because it has been here since random was
|
| 206 |
+
# rewritten back in 2001 and why risk breaking something.
|
| 207 |
+
def __getstate__(self): # for pickle
|
| 208 |
+
return self.getstate()
|
| 209 |
+
|
| 210 |
+
def __setstate__(self, state): # for pickle
|
| 211 |
+
self.setstate(state)
|
| 212 |
+
|
| 213 |
+
def __reduce__(self):
|
| 214 |
+
return self.__class__, (), self.getstate()
|
| 215 |
+
|
| 216 |
+
|
| 217 |
+
## ---- internal support method for evenly distributed integers ----
|
| 218 |
+
|
| 219 |
+
def __init_subclass__(cls, /, **kwargs):
|
| 220 |
+
"""Control how subclasses generate random integers.
|
| 221 |
+
|
| 222 |
+
The algorithm a subclass can use depends on the random() and/or
|
| 223 |
+
getrandbits() implementation available to it and determines
|
| 224 |
+
whether it can generate random integers from arbitrarily large
|
| 225 |
+
ranges.
|
| 226 |
+
"""
|
| 227 |
+
|
| 228 |
+
for c in cls.__mro__:
|
| 229 |
+
if '_randbelow' in c.__dict__:
|
| 230 |
+
# just inherit it
|
| 231 |
+
break
|
| 232 |
+
if 'getrandbits' in c.__dict__:
|
| 233 |
+
cls._randbelow = cls._randbelow_with_getrandbits
|
| 234 |
+
break
|
| 235 |
+
if 'random' in c.__dict__:
|
| 236 |
+
cls._randbelow = cls._randbelow_without_getrandbits
|
| 237 |
+
break
|
| 238 |
+
|
| 239 |
+
def _randbelow_with_getrandbits(self, n):
|
| 240 |
+
"Return a random int in the range [0,n). Returns 0 if n==0."
|
| 241 |
+
|
| 242 |
+
if not n:
|
| 243 |
+
return 0
|
| 244 |
+
getrandbits = self.getrandbits
|
| 245 |
+
k = n.bit_length() # don't use (n-1) here because n can be 1
|
| 246 |
+
r = getrandbits(k) # 0 <= r < 2**k
|
| 247 |
+
while r >= n:
|
| 248 |
+
r = getrandbits(k)
|
| 249 |
+
return r
|
| 250 |
+
|
| 251 |
+
def _randbelow_without_getrandbits(self, n, maxsize=1<<BPF):
|
| 252 |
+
"""Return a random int in the range [0,n). Returns 0 if n==0.
|
| 253 |
+
|
| 254 |
+
The implementation does not use getrandbits, but only random.
|
| 255 |
+
"""
|
| 256 |
+
|
| 257 |
+
random = self.random
|
| 258 |
+
if n >= maxsize:
|
| 259 |
+
_warn("Underlying random() generator does not supply \n"
|
| 260 |
+
"enough bits to choose from a population range this large.\n"
|
| 261 |
+
"To remove the range limitation, add a getrandbits() method.")
|
| 262 |
+
return _floor(random() * n)
|
| 263 |
+
if n == 0:
|
| 264 |
+
return 0
|
| 265 |
+
rem = maxsize % n
|
| 266 |
+
limit = (maxsize - rem) / maxsize # int(limit * maxsize) % n == 0
|
| 267 |
+
r = random()
|
| 268 |
+
while r >= limit:
|
| 269 |
+
r = random()
|
| 270 |
+
return _floor(r * maxsize) % n
|
| 271 |
+
|
| 272 |
+
_randbelow = _randbelow_with_getrandbits
|
| 273 |
+
|
| 274 |
+
|
| 275 |
+
## --------------------------------------------------------
|
| 276 |
+
## ---- Methods below this point generate custom distributions
|
| 277 |
+
## ---- based on the methods defined above. They do not
|
| 278 |
+
## ---- directly touch the underlying generator and only
|
| 279 |
+
## ---- access randomness through the methods: random(),
|
| 280 |
+
## ---- getrandbits(), or _randbelow().
|
| 281 |
+
|
| 282 |
+
|
| 283 |
+
## -------------------- bytes methods ---------------------
|
| 284 |
+
|
| 285 |
+
def randbytes(self, n):
|
| 286 |
+
"""Generate n random bytes."""
|
| 287 |
+
return self.getrandbits(n * 8).to_bytes(n, 'little')
|
| 288 |
+
|
| 289 |
+
|
| 290 |
+
## -------------------- integer methods -------------------
|
| 291 |
+
|
| 292 |
+
def randrange(self, start, stop=None, step=_ONE):
|
| 293 |
+
"""Choose a random item from range(start, stop[, step]).
|
| 294 |
+
|
| 295 |
+
This fixes the problem with randint() which includes the
|
| 296 |
+
endpoint; in Python this is usually not what you want.
|
| 297 |
+
|
| 298 |
+
"""
|
| 299 |
+
|
| 300 |
+
# This code is a bit messy to make it fast for the
|
| 301 |
+
# common case while still doing adequate error checking.
|
| 302 |
+
try:
|
| 303 |
+
istart = _index(start)
|
| 304 |
+
except TypeError:
|
| 305 |
+
istart = int(start)
|
| 306 |
+
if istart != start:
|
| 307 |
+
_warn('randrange() will raise TypeError in the future',
|
| 308 |
+
DeprecationWarning, 2)
|
| 309 |
+
raise ValueError("non-integer arg 1 for randrange()")
|
| 310 |
+
_warn('non-integer arguments to randrange() have been deprecated '
|
| 311 |
+
'since Python 3.10 and will be removed in a subsequent '
|
| 312 |
+
'version',
|
| 313 |
+
DeprecationWarning, 2)
|
| 314 |
+
if stop is None:
|
| 315 |
+
# We don't check for "step != 1" because it hasn't been
|
| 316 |
+
# type checked and converted to an integer yet.
|
| 317 |
+
if step is not _ONE:
|
| 318 |
+
raise TypeError('Missing a non-None stop argument')
|
| 319 |
+
if istart > 0:
|
| 320 |
+
return self._randbelow(istart)
|
| 321 |
+
raise ValueError("empty range for randrange()")
|
| 322 |
+
|
| 323 |
+
# stop argument supplied.
|
| 324 |
+
try:
|
| 325 |
+
istop = _index(stop)
|
| 326 |
+
except TypeError:
|
| 327 |
+
istop = int(stop)
|
| 328 |
+
if istop != stop:
|
| 329 |
+
_warn('randrange() will raise TypeError in the future',
|
| 330 |
+
DeprecationWarning, 2)
|
| 331 |
+
raise ValueError("non-integer stop for randrange()")
|
| 332 |
+
_warn('non-integer arguments to randrange() have been deprecated '
|
| 333 |
+
'since Python 3.10 and will be removed in a subsequent '
|
| 334 |
+
'version',
|
| 335 |
+
DeprecationWarning, 2)
|
| 336 |
+
width = istop - istart
|
| 337 |
+
try:
|
| 338 |
+
istep = _index(step)
|
| 339 |
+
except TypeError:
|
| 340 |
+
istep = int(step)
|
| 341 |
+
if istep != step:
|
| 342 |
+
_warn('randrange() will raise TypeError in the future',
|
| 343 |
+
DeprecationWarning, 2)
|
| 344 |
+
raise ValueError("non-integer step for randrange()")
|
| 345 |
+
_warn('non-integer arguments to randrange() have been deprecated '
|
| 346 |
+
'since Python 3.10 and will be removed in a subsequent '
|
| 347 |
+
'version',
|
| 348 |
+
DeprecationWarning, 2)
|
| 349 |
+
# Fast path.
|
| 350 |
+
if istep == 1:
|
| 351 |
+
if width > 0:
|
| 352 |
+
return istart + self._randbelow(width)
|
| 353 |
+
raise ValueError("empty range for randrange() (%d, %d, %d)" % (istart, istop, width))
|
| 354 |
+
|
| 355 |
+
# Non-unit step argument supplied.
|
| 356 |
+
if istep > 0:
|
| 357 |
+
n = (width + istep - 1) // istep
|
| 358 |
+
elif istep < 0:
|
| 359 |
+
n = (width + istep + 1) // istep
|
| 360 |
+
else:
|
| 361 |
+
raise ValueError("zero step for randrange()")
|
| 362 |
+
if n <= 0:
|
| 363 |
+
raise ValueError("empty range for randrange()")
|
| 364 |
+
return istart + istep * self._randbelow(n)
|
| 365 |
+
|
| 366 |
+
def randint(self, a, b):
|
| 367 |
+
"""Return random integer in range [a, b], including both end points.
|
| 368 |
+
"""
|
| 369 |
+
|
| 370 |
+
return self.randrange(a, b+1)
|
| 371 |
+
|
| 372 |
+
|
| 373 |
+
## -------------------- sequence methods -------------------
|
| 374 |
+
|
| 375 |
+
def choice(self, seq):
|
| 376 |
+
"""Choose a random element from a non-empty sequence."""
|
| 377 |
+
# raises IndexError if seq is empty
|
| 378 |
+
return seq[self._randbelow(len(seq))]
|
| 379 |
+
|
| 380 |
+
def shuffle(self, x, random=None):
|
| 381 |
+
"""Shuffle list x in place, and return None.
|
| 382 |
+
|
| 383 |
+
Optional argument random is a 0-argument function returning a
|
| 384 |
+
random float in [0.0, 1.0); if it is the default None, the
|
| 385 |
+
standard random.random will be used.
|
| 386 |
+
|
| 387 |
+
"""
|
| 388 |
+
|
| 389 |
+
if random is None:
|
| 390 |
+
randbelow = self._randbelow
|
| 391 |
+
for i in reversed(range(1, len(x))):
|
| 392 |
+
# pick an element in x[:i+1] with which to exchange x[i]
|
| 393 |
+
j = randbelow(i + 1)
|
| 394 |
+
x[i], x[j] = x[j], x[i]
|
| 395 |
+
else:
|
| 396 |
+
_warn('The *random* parameter to shuffle() has been deprecated\n'
|
| 397 |
+
'since Python 3.9 and will be removed in a subsequent '
|
| 398 |
+
'version.',
|
| 399 |
+
DeprecationWarning, 2)
|
| 400 |
+
floor = _floor
|
| 401 |
+
for i in reversed(range(1, len(x))):
|
| 402 |
+
# pick an element in x[:i+1] with which to exchange x[i]
|
| 403 |
+
j = floor(random() * (i + 1))
|
| 404 |
+
x[i], x[j] = x[j], x[i]
|
| 405 |
+
|
| 406 |
+
def sample(self, population, k, *, counts=None):
|
| 407 |
+
"""Chooses k unique random elements from a population sequence or set.
|
| 408 |
+
|
| 409 |
+
Returns a new list containing elements from the population while
|
| 410 |
+
leaving the original population unchanged. The resulting list is
|
| 411 |
+
in selection order so that all sub-slices will also be valid random
|
| 412 |
+
samples. This allows raffle winners (the sample) to be partitioned
|
| 413 |
+
into grand prize and second place winners (the subslices).
|
| 414 |
+
|
| 415 |
+
Members of the population need not be hashable or unique. If the
|
| 416 |
+
population contains repeats, then each occurrence is a possible
|
| 417 |
+
selection in the sample.
|
| 418 |
+
|
| 419 |
+
Repeated elements can be specified one at a time or with the optional
|
| 420 |
+
counts parameter. For example:
|
| 421 |
+
|
| 422 |
+
sample(['red', 'blue'], counts=[4, 2], k=5)
|
| 423 |
+
|
| 424 |
+
is equivalent to:
|
| 425 |
+
|
| 426 |
+
sample(['red', 'red', 'red', 'red', 'blue', 'blue'], k=5)
|
| 427 |
+
|
| 428 |
+
To choose a sample from a range of integers, use range() for the
|
| 429 |
+
population argument. This is especially fast and space efficient
|
| 430 |
+
for sampling from a large population:
|
| 431 |
+
|
| 432 |
+
sample(range(10000000), 60)
|
| 433 |
+
|
| 434 |
+
"""
|
| 435 |
+
|
| 436 |
+
# Sampling without replacement entails tracking either potential
|
| 437 |
+
# selections (the pool) in a list or previous selections in a set.
|
| 438 |
+
|
| 439 |
+
# When the number of selections is small compared to the
|
| 440 |
+
# population, then tracking selections is efficient, requiring
|
| 441 |
+
# only a small set and an occasional reselection. For
|
| 442 |
+
# a larger number of selections, the pool tracking method is
|
| 443 |
+
# preferred since the list takes less space than the
|
| 444 |
+
# set and it doesn't suffer from frequent reselections.
|
| 445 |
+
|
| 446 |
+
# The number of calls to _randbelow() is kept at or near k, the
|
| 447 |
+
# theoretical minimum. This is important because running time
|
| 448 |
+
# is dominated by _randbelow() and because it extracts the
|
| 449 |
+
# least entropy from the underlying random number generators.
|
| 450 |
+
|
| 451 |
+
# Memory requirements are kept to the smaller of a k-length
|
| 452 |
+
# set or an n-length list.
|
| 453 |
+
|
| 454 |
+
# There are other sampling algorithms that do not require
|
| 455 |
+
# auxiliary memory, but they were rejected because they made
|
| 456 |
+
# too many calls to _randbelow(), making them slower and
|
| 457 |
+
# causing them to eat more entropy than necessary.
|
| 458 |
+
|
| 459 |
+
if not isinstance(population, _Sequence):
|
| 460 |
+
if isinstance(population, _Set):
|
| 461 |
+
_warn('Sampling from a set deprecated\n'
|
| 462 |
+
'since Python 3.9 and will be removed in a subsequent version.',
|
| 463 |
+
DeprecationWarning, 2)
|
| 464 |
+
population = tuple(population)
|
| 465 |
+
else:
|
| 466 |
+
raise TypeError("Population must be a sequence. For dicts or sets, use sorted(d).")
|
| 467 |
+
n = len(population)
|
| 468 |
+
if counts is not None:
|
| 469 |
+
cum_counts = list(_accumulate(counts))
|
| 470 |
+
if len(cum_counts) != n:
|
| 471 |
+
raise ValueError('The number of counts does not match the population')
|
| 472 |
+
total = cum_counts.pop()
|
| 473 |
+
if not isinstance(total, int):
|
| 474 |
+
raise TypeError('Counts must be integers')
|
| 475 |
+
if total <= 0:
|
| 476 |
+
raise ValueError('Total of counts must be greater than zero')
|
| 477 |
+
selections = self.sample(range(total), k=k)
|
| 478 |
+
bisect = _bisect
|
| 479 |
+
return [population[bisect(cum_counts, s)] for s in selections]
|
| 480 |
+
randbelow = self._randbelow
|
| 481 |
+
if not 0 <= k <= n:
|
| 482 |
+
raise ValueError("Sample larger than population or is negative")
|
| 483 |
+
result = [None] * k
|
| 484 |
+
setsize = 21 # size of a small set minus size of an empty list
|
| 485 |
+
if k > 5:
|
| 486 |
+
setsize += 4 ** _ceil(_log(k * 3, 4)) # table size for big sets
|
| 487 |
+
if n <= setsize:
|
| 488 |
+
# An n-length list is smaller than a k-length set.
|
| 489 |
+
# Invariant: non-selected at pool[0 : n-i]
|
| 490 |
+
pool = list(population)
|
| 491 |
+
for i in range(k):
|
| 492 |
+
j = randbelow(n - i)
|
| 493 |
+
result[i] = pool[j]
|
| 494 |
+
pool[j] = pool[n - i - 1] # move non-selected item into vacancy
|
| 495 |
+
else:
|
| 496 |
+
selected = set()
|
| 497 |
+
selected_add = selected.add
|
| 498 |
+
for i in range(k):
|
| 499 |
+
j = randbelow(n)
|
| 500 |
+
while j in selected:
|
| 501 |
+
j = randbelow(n)
|
| 502 |
+
selected_add(j)
|
| 503 |
+
result[i] = population[j]
|
| 504 |
+
return result
|
| 505 |
+
|
| 506 |
+
def choices(self, population, weights=None, *, cum_weights=None, k=1):
|
| 507 |
+
"""Return a k sized list of population elements chosen with replacement.
|
| 508 |
+
|
| 509 |
+
If the relative weights or cumulative weights are not specified,
|
| 510 |
+
the selections are made with equal probability.
|
| 511 |
+
|
| 512 |
+
"""
|
| 513 |
+
random = self.random
|
| 514 |
+
n = len(population)
|
| 515 |
+
if cum_weights is None:
|
| 516 |
+
if weights is None:
|
| 517 |
+
floor = _floor
|
| 518 |
+
n += 0.0 # convert to float for a small speed improvement
|
| 519 |
+
return [population[floor(random() * n)] for i in _repeat(None, k)]
|
| 520 |
+
try:
|
| 521 |
+
cum_weights = list(_accumulate(weights))
|
| 522 |
+
except TypeError:
|
| 523 |
+
if not isinstance(weights, int):
|
| 524 |
+
raise
|
| 525 |
+
k = weights
|
| 526 |
+
raise TypeError(
|
| 527 |
+
f'The number of choices must be a keyword argument: {k=}'
|
| 528 |
+
) from None
|
| 529 |
+
elif weights is not None:
|
| 530 |
+
raise TypeError('Cannot specify both weights and cumulative weights')
|
| 531 |
+
if len(cum_weights) != n:
|
| 532 |
+
raise ValueError('The number of weights does not match the population')
|
| 533 |
+
total = cum_weights[-1] + 0.0 # convert to float
|
| 534 |
+
if total <= 0.0:
|
| 535 |
+
raise ValueError('Total of weights must be greater than zero')
|
| 536 |
+
if not _isfinite(total):
|
| 537 |
+
raise ValueError('Total of weights must be finite')
|
| 538 |
+
bisect = _bisect
|
| 539 |
+
hi = n - 1
|
| 540 |
+
return [population[bisect(cum_weights, random() * total, 0, hi)]
|
| 541 |
+
for i in _repeat(None, k)]
|
| 542 |
+
|
| 543 |
+
|
| 544 |
+
## -------------------- real-valued distributions -------------------
|
| 545 |
+
|
| 546 |
+
def uniform(self, a, b):
|
| 547 |
+
"Get a random number in the range [a, b) or [a, b] depending on rounding."
|
| 548 |
+
return a + (b - a) * self.random()
|
| 549 |
+
|
| 550 |
+
def triangular(self, low=0.0, high=1.0, mode=None):
|
| 551 |
+
"""Triangular distribution.
|
| 552 |
+
|
| 553 |
+
Continuous distribution bounded by given lower and upper limits,
|
| 554 |
+
and having a given mode value in-between.
|
| 555 |
+
|
| 556 |
+
http://en.wikipedia.org/wiki/Triangular_distribution
|
| 557 |
+
|
| 558 |
+
"""
|
| 559 |
+
u = self.random()
|
| 560 |
+
try:
|
| 561 |
+
c = 0.5 if mode is None else (mode - low) / (high - low)
|
| 562 |
+
except ZeroDivisionError:
|
| 563 |
+
return low
|
| 564 |
+
if u > c:
|
| 565 |
+
u = 1.0 - u
|
| 566 |
+
c = 1.0 - c
|
| 567 |
+
low, high = high, low
|
| 568 |
+
return low + (high - low) * _sqrt(u * c)
|
| 569 |
+
|
| 570 |
+
def normalvariate(self, mu, sigma):
|
| 571 |
+
"""Normal distribution.
|
| 572 |
+
|
| 573 |
+
mu is the mean, and sigma is the standard deviation.
|
| 574 |
+
|
| 575 |
+
"""
|
| 576 |
+
# Uses Kinderman and Monahan method. Reference: Kinderman,
|
| 577 |
+
# A.J. and Monahan, J.F., "Computer generation of random
|
| 578 |
+
# variables using the ratio of uniform deviates", ACM Trans
|
| 579 |
+
# Math Software, 3, (1977), pp257-260.
|
| 580 |
+
|
| 581 |
+
random = self.random
|
| 582 |
+
while True:
|
| 583 |
+
u1 = random()
|
| 584 |
+
u2 = 1.0 - random()
|
| 585 |
+
z = NV_MAGICCONST * (u1 - 0.5) / u2
|
| 586 |
+
zz = z * z / 4.0
|
| 587 |
+
if zz <= -_log(u2):
|
| 588 |
+
break
|
| 589 |
+
return mu + z * sigma
|
| 590 |
+
|
| 591 |
+
def gauss(self, mu, sigma):
|
| 592 |
+
"""Gaussian distribution.
|
| 593 |
+
|
| 594 |
+
mu is the mean, and sigma is the standard deviation. This is
|
| 595 |
+
slightly faster than the normalvariate() function.
|
| 596 |
+
|
| 597 |
+
Not thread-safe without a lock around calls.
|
| 598 |
+
|
| 599 |
+
"""
|
| 600 |
+
# When x and y are two variables from [0, 1), uniformly
|
| 601 |
+
# distributed, then
|
| 602 |
+
#
|
| 603 |
+
# cos(2*pi*x)*sqrt(-2*log(1-y))
|
| 604 |
+
# sin(2*pi*x)*sqrt(-2*log(1-y))
|
| 605 |
+
#
|
| 606 |
+
# are two *independent* variables with normal distribution
|
| 607 |
+
# (mu = 0, sigma = 1).
|
| 608 |
+
# (Lambert Meertens)
|
| 609 |
+
# (corrected version; bug discovered by Mike Miller, fixed by LM)
|
| 610 |
+
|
| 611 |
+
# Multithreading note: When two threads call this function
|
| 612 |
+
# simultaneously, it is possible that they will receive the
|
| 613 |
+
# same return value. The window is very small though. To
|
| 614 |
+
# avoid this, you have to use a lock around all calls. (I
|
| 615 |
+
# didn't want to slow this down in the serial case by using a
|
| 616 |
+
# lock here.)
|
| 617 |
+
|
| 618 |
+
random = self.random
|
| 619 |
+
z = self.gauss_next
|
| 620 |
+
self.gauss_next = None
|
| 621 |
+
if z is None:
|
| 622 |
+
x2pi = random() * TWOPI
|
| 623 |
+
g2rad = _sqrt(-2.0 * _log(1.0 - random()))
|
| 624 |
+
z = _cos(x2pi) * g2rad
|
| 625 |
+
self.gauss_next = _sin(x2pi) * g2rad
|
| 626 |
+
|
| 627 |
+
return mu + z * sigma
|
| 628 |
+
|
| 629 |
+
def lognormvariate(self, mu, sigma):
|
| 630 |
+
"""Log normal distribution.
|
| 631 |
+
|
| 632 |
+
If you take the natural logarithm of this distribution, you'll get a
|
| 633 |
+
normal distribution with mean mu and standard deviation sigma.
|
| 634 |
+
mu can have any value, and sigma must be greater than zero.
|
| 635 |
+
|
| 636 |
+
"""
|
| 637 |
+
return _exp(self.normalvariate(mu, sigma))
|
| 638 |
+
|
| 639 |
+
def expovariate(self, lambd):
|
| 640 |
+
"""Exponential distribution.
|
| 641 |
+
|
| 642 |
+
lambd is 1.0 divided by the desired mean. It should be
|
| 643 |
+
nonzero. (The parameter would be called "lambda", but that is
|
| 644 |
+
a reserved word in Python.) Returned values range from 0 to
|
| 645 |
+
positive infinity if lambd is positive, and from negative
|
| 646 |
+
infinity to 0 if lambd is negative.
|
| 647 |
+
|
| 648 |
+
"""
|
| 649 |
+
# lambd: rate lambd = 1/mean
|
| 650 |
+
# ('lambda' is a Python reserved word)
|
| 651 |
+
|
| 652 |
+
# we use 1-random() instead of random() to preclude the
|
| 653 |
+
# possibility of taking the log of zero.
|
| 654 |
+
return -_log(1.0 - self.random()) / lambd
|
| 655 |
+
|
| 656 |
+
def vonmisesvariate(self, mu, kappa):
|
| 657 |
+
"""Circular data distribution.
|
| 658 |
+
|
| 659 |
+
mu is the mean angle, expressed in radians between 0 and 2*pi, and
|
| 660 |
+
kappa is the concentration parameter, which must be greater than or
|
| 661 |
+
equal to zero. If kappa is equal to zero, this distribution reduces
|
| 662 |
+
to a uniform random angle over the range 0 to 2*pi.
|
| 663 |
+
|
| 664 |
+
"""
|
| 665 |
+
# Based upon an algorithm published in: Fisher, N.I.,
|
| 666 |
+
# "Statistical Analysis of Circular Data", Cambridge
|
| 667 |
+
# University Press, 1993.
|
| 668 |
+
|
| 669 |
+
# Thanks to Magnus Kessler for a correction to the
|
| 670 |
+
# implementation of step 4.
|
| 671 |
+
|
| 672 |
+
random = self.random
|
| 673 |
+
if kappa <= 1e-6:
|
| 674 |
+
return TWOPI * random()
|
| 675 |
+
|
| 676 |
+
s = 0.5 / kappa
|
| 677 |
+
r = s + _sqrt(1.0 + s * s)
|
| 678 |
+
|
| 679 |
+
while True:
|
| 680 |
+
u1 = random()
|
| 681 |
+
z = _cos(_pi * u1)
|
| 682 |
+
|
| 683 |
+
d = z / (r + z)
|
| 684 |
+
u2 = random()
|
| 685 |
+
if u2 < 1.0 - d * d or u2 <= (1.0 - d) * _exp(d):
|
| 686 |
+
break
|
| 687 |
+
|
| 688 |
+
q = 1.0 / r
|
| 689 |
+
f = (q + z) / (1.0 + q * z)
|
| 690 |
+
u3 = random()
|
| 691 |
+
if u3 > 0.5:
|
| 692 |
+
theta = (mu + _acos(f)) % TWOPI
|
| 693 |
+
else:
|
| 694 |
+
theta = (mu - _acos(f)) % TWOPI
|
| 695 |
+
|
| 696 |
+
return theta
|
| 697 |
+
|
| 698 |
+
def gammavariate(self, alpha, beta):
|
| 699 |
+
"""Gamma distribution. Not the gamma function!
|
| 700 |
+
|
| 701 |
+
Conditions on the parameters are alpha > 0 and beta > 0.
|
| 702 |
+
|
| 703 |
+
The probability distribution function is:
|
| 704 |
+
|
| 705 |
+
x ** (alpha - 1) * math.exp(-x / beta)
|
| 706 |
+
pdf(x) = --------------------------------------
|
| 707 |
+
math.gamma(alpha) * beta ** alpha
|
| 708 |
+
|
| 709 |
+
"""
|
| 710 |
+
# alpha > 0, beta > 0, mean is alpha*beta, variance is alpha*beta**2
|
| 711 |
+
|
| 712 |
+
# Warning: a few older sources define the gamma distribution in terms
|
| 713 |
+
# of alpha > -1.0
|
| 714 |
+
if alpha <= 0.0 or beta <= 0.0:
|
| 715 |
+
raise ValueError('gammavariate: alpha and beta must be > 0.0')
|
| 716 |
+
|
| 717 |
+
random = self.random
|
| 718 |
+
if alpha > 1.0:
|
| 719 |
+
|
| 720 |
+
# Uses R.C.H. Cheng, "The generation of Gamma
|
| 721 |
+
# variables with non-integral shape parameters",
|
| 722 |
+
# Applied Statistics, (1977), 26, No. 1, p71-74
|
| 723 |
+
|
| 724 |
+
ainv = _sqrt(2.0 * alpha - 1.0)
|
| 725 |
+
bbb = alpha - LOG4
|
| 726 |
+
ccc = alpha + ainv
|
| 727 |
+
|
| 728 |
+
while True:
|
| 729 |
+
u1 = random()
|
| 730 |
+
if not 1e-7 < u1 < 0.9999999:
|
| 731 |
+
continue
|
| 732 |
+
u2 = 1.0 - random()
|
| 733 |
+
v = _log(u1 / (1.0 - u1)) / ainv
|
| 734 |
+
x = alpha * _exp(v)
|
| 735 |
+
z = u1 * u1 * u2
|
| 736 |
+
r = bbb + ccc * v - x
|
| 737 |
+
if r + SG_MAGICCONST - 4.5 * z >= 0.0 or r >= _log(z):
|
| 738 |
+
return x * beta
|
| 739 |
+
|
| 740 |
+
elif alpha == 1.0:
|
| 741 |
+
# expovariate(1/beta)
|
| 742 |
+
return -_log(1.0 - random()) * beta
|
| 743 |
+
|
| 744 |
+
else:
|
| 745 |
+
# alpha is between 0 and 1 (exclusive)
|
| 746 |
+
# Uses ALGORITHM GS of Statistical Computing - Kennedy & Gentle
|
| 747 |
+
while True:
|
| 748 |
+
u = random()
|
| 749 |
+
b = (_e + alpha) / _e
|
| 750 |
+
p = b * u
|
| 751 |
+
if p <= 1.0:
|
| 752 |
+
x = p ** (1.0 / alpha)
|
| 753 |
+
else:
|
| 754 |
+
x = -_log((b - p) / alpha)
|
| 755 |
+
u1 = random()
|
| 756 |
+
if p > 1.0:
|
| 757 |
+
if u1 <= x ** (alpha - 1.0):
|
| 758 |
+
break
|
| 759 |
+
elif u1 <= _exp(-x):
|
| 760 |
+
break
|
| 761 |
+
return x * beta
|
| 762 |
+
|
| 763 |
+
def betavariate(self, alpha, beta):
|
| 764 |
+
"""Beta distribution.
|
| 765 |
+
|
| 766 |
+
Conditions on the parameters are alpha > 0 and beta > 0.
|
| 767 |
+
Returned values range between 0 and 1.
|
| 768 |
+
|
| 769 |
+
"""
|
| 770 |
+
## See
|
| 771 |
+
## http://mail.python.org/pipermail/python-bugs-list/2001-January/003752.html
|
| 772 |
+
## for Ivan Frohne's insightful analysis of why the original implementation:
|
| 773 |
+
##
|
| 774 |
+
## def betavariate(self, alpha, beta):
|
| 775 |
+
## # Discrete Event Simulation in C, pp 87-88.
|
| 776 |
+
##
|
| 777 |
+
## y = self.expovariate(alpha)
|
| 778 |
+
## z = self.expovariate(1.0/beta)
|
| 779 |
+
## return z/(y+z)
|
| 780 |
+
##
|
| 781 |
+
## was dead wrong, and how it probably got that way.
|
| 782 |
+
|
| 783 |
+
# This version due to Janne Sinkkonen, and matches all the std
|
| 784 |
+
# texts (e.g., Knuth Vol 2 Ed 3 pg 134 "the beta distribution").
|
| 785 |
+
y = self.gammavariate(alpha, 1.0)
|
| 786 |
+
if y:
|
| 787 |
+
return y / (y + self.gammavariate(beta, 1.0))
|
| 788 |
+
return 0.0
|
| 789 |
+
|
| 790 |
+
def paretovariate(self, alpha):
|
| 791 |
+
"""Pareto distribution. alpha is the shape parameter."""
|
| 792 |
+
# Jain, pg. 495
|
| 793 |
+
|
| 794 |
+
u = 1.0 - self.random()
|
| 795 |
+
return u ** (-1.0 / alpha)
|
| 796 |
+
|
| 797 |
+
def weibullvariate(self, alpha, beta):
|
| 798 |
+
"""Weibull distribution.
|
| 799 |
+
|
| 800 |
+
alpha is the scale parameter and beta is the shape parameter.
|
| 801 |
+
|
| 802 |
+
"""
|
| 803 |
+
# Jain, pg. 499; bug fix courtesy Bill Arms
|
| 804 |
+
|
| 805 |
+
u = 1.0 - self.random()
|
| 806 |
+
return alpha * (-_log(u)) ** (1.0 / beta)
|
| 807 |
+
|
| 808 |
+
|
| 809 |
+
## ------------------------------------------------------------------
|
| 810 |
+
## --------------- Operating System Random Source ------------------
|
| 811 |
+
|
| 812 |
+
|
| 813 |
+
class SystemRandom(Random):
|
| 814 |
+
"""Alternate random number generator using sources provided
|
| 815 |
+
by the operating system (such as /dev/urandom on Unix or
|
| 816 |
+
CryptGenRandom on Windows).
|
| 817 |
+
|
| 818 |
+
Not available on all systems (see os.urandom() for details).
|
| 819 |
+
|
| 820 |
+
"""
|
| 821 |
+
|
| 822 |
+
def random(self):
|
| 823 |
+
"""Get the next random number in the range [0.0, 1.0)."""
|
| 824 |
+
return (int.from_bytes(_urandom(7), 'big') >> 3) * RECIP_BPF
|
| 825 |
+
|
| 826 |
+
def getrandbits(self, k):
|
| 827 |
+
"""getrandbits(k) -> x. Generates an int with k random bits."""
|
| 828 |
+
if k < 0:
|
| 829 |
+
raise ValueError('number of bits must be non-negative')
|
| 830 |
+
numbytes = (k + 7) // 8 # bits / 8 and rounded up
|
| 831 |
+
x = int.from_bytes(_urandom(numbytes), 'big')
|
| 832 |
+
return x >> (numbytes * 8 - k) # trim excess bits
|
| 833 |
+
|
| 834 |
+
def randbytes(self, n):
|
| 835 |
+
"""Generate n random bytes."""
|
| 836 |
+
# os.urandom(n) fails with ValueError for n < 0
|
| 837 |
+
# and returns an empty bytes string for n == 0.
|
| 838 |
+
return _urandom(n)
|
| 839 |
+
|
| 840 |
+
def seed(self, *args, **kwds):
|
| 841 |
+
"Stub method. Not used for a system random number generator."
|
| 842 |
+
return None
|
| 843 |
+
|
| 844 |
+
def _notimplemented(self, *args, **kwds):
|
| 845 |
+
"Method should not be called for a system random number generator."
|
| 846 |
+
raise NotImplementedError('System entropy source does not have state.')
|
| 847 |
+
getstate = setstate = _notimplemented
|
| 848 |
+
|
| 849 |
+
|
| 850 |
+
# ----------------------------------------------------------------------
|
| 851 |
+
# Create one instance, seeded from current time, and export its methods
|
| 852 |
+
# as module-level functions. The functions share state across all uses
|
| 853 |
+
# (both in the user's code and in the Python libraries), but that's fine
|
| 854 |
+
# for most programs and is easier for the casual user than making them
|
| 855 |
+
# instantiate their own Random() instance.
|
| 856 |
+
|
| 857 |
+
_inst = Random()
|
| 858 |
+
seed = _inst.seed
|
| 859 |
+
random = _inst.random
|
| 860 |
+
uniform = _inst.uniform
|
| 861 |
+
triangular = _inst.triangular
|
| 862 |
+
randint = _inst.randint
|
| 863 |
+
choice = _inst.choice
|
| 864 |
+
randrange = _inst.randrange
|
| 865 |
+
sample = _inst.sample
|
| 866 |
+
shuffle = _inst.shuffle
|
| 867 |
+
choices = _inst.choices
|
| 868 |
+
normalvariate = _inst.normalvariate
|
| 869 |
+
lognormvariate = _inst.lognormvariate
|
| 870 |
+
expovariate = _inst.expovariate
|
| 871 |
+
vonmisesvariate = _inst.vonmisesvariate
|
| 872 |
+
gammavariate = _inst.gammavariate
|
| 873 |
+
gauss = _inst.gauss
|
| 874 |
+
betavariate = _inst.betavariate
|
| 875 |
+
paretovariate = _inst.paretovariate
|
| 876 |
+
weibullvariate = _inst.weibullvariate
|
| 877 |
+
getstate = _inst.getstate
|
| 878 |
+
setstate = _inst.setstate
|
| 879 |
+
getrandbits = _inst.getrandbits
|
| 880 |
+
randbytes = _inst.randbytes
|
| 881 |
+
|
| 882 |
+
|
| 883 |
+
## ------------------------------------------------------
|
| 884 |
+
## ----------------- test program -----------------------
|
| 885 |
+
|
| 886 |
+
def _test_generator(n, func, args):
|
| 887 |
+
from statistics import stdev, fmean as mean
|
| 888 |
+
from time import perf_counter
|
| 889 |
+
|
| 890 |
+
t0 = perf_counter()
|
| 891 |
+
data = [func(*args) for i in _repeat(None, n)]
|
| 892 |
+
t1 = perf_counter()
|
| 893 |
+
|
| 894 |
+
xbar = mean(data)
|
| 895 |
+
sigma = stdev(data, xbar)
|
| 896 |
+
low = min(data)
|
| 897 |
+
high = max(data)
|
| 898 |
+
|
| 899 |
+
print(f'{t1 - t0:.3f} sec, {n} times {func.__name__}')
|
| 900 |
+
print('avg %g, stddev %g, min %g, max %g\n' % (xbar, sigma, low, high))
|
| 901 |
+
|
| 902 |
+
|
| 903 |
+
def _test(N=2000):
|
| 904 |
+
_test_generator(N, random, ())
|
| 905 |
+
_test_generator(N, normalvariate, (0.0, 1.0))
|
| 906 |
+
_test_generator(N, lognormvariate, (0.0, 1.0))
|
| 907 |
+
_test_generator(N, vonmisesvariate, (0.0, 1.0))
|
| 908 |
+
_test_generator(N, gammavariate, (0.01, 1.0))
|
| 909 |
+
_test_generator(N, gammavariate, (0.1, 1.0))
|
| 910 |
+
_test_generator(N, gammavariate, (0.1, 2.0))
|
| 911 |
+
_test_generator(N, gammavariate, (0.5, 1.0))
|
| 912 |
+
_test_generator(N, gammavariate, (0.9, 1.0))
|
| 913 |
+
_test_generator(N, gammavariate, (1.0, 1.0))
|
| 914 |
+
_test_generator(N, gammavariate, (2.0, 1.0))
|
| 915 |
+
_test_generator(N, gammavariate, (20.0, 1.0))
|
| 916 |
+
_test_generator(N, gammavariate, (200.0, 1.0))
|
| 917 |
+
_test_generator(N, gauss, (0.0, 1.0))
|
| 918 |
+
_test_generator(N, betavariate, (3.0, 3.0))
|
| 919 |
+
_test_generator(N, triangular, (0.0, 1.0, 1.0 / 3.0))
|
| 920 |
+
|
| 921 |
+
|
| 922 |
+
## ------------------------------------------------------
|
| 923 |
+
## ------------------ fork support ---------------------
|
| 924 |
+
|
| 925 |
+
if hasattr(_os, "fork"):
|
| 926 |
+
_os.register_at_fork(after_in_child=_inst.seed)
|
| 927 |
+
|
| 928 |
+
|
| 929 |
+
if __name__ == '__main__':
|
| 930 |
+
_test()
|
lib/python3.10/re.py
ADDED
|
@@ -0,0 +1,383 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#
|
| 2 |
+
# Secret Labs' Regular Expression Engine
|
| 3 |
+
#
|
| 4 |
+
# re-compatible interface for the sre matching engine
|
| 5 |
+
#
|
| 6 |
+
# Copyright (c) 1998-2001 by Secret Labs AB. All rights reserved.
|
| 7 |
+
#
|
| 8 |
+
# This version of the SRE library can be redistributed under CNRI's
|
| 9 |
+
# Python 1.6 license. For any other use, please contact Secret Labs
|
| 10 |
+
# AB (info@pythonware.com).
|
| 11 |
+
#
|
| 12 |
+
# Portions of this engine have been developed in cooperation with
|
| 13 |
+
# CNRI. Hewlett-Packard provided funding for 1.6 integration and
|
| 14 |
+
# other compatibility work.
|
| 15 |
+
#
|
| 16 |
+
|
| 17 |
+
r"""Support for regular expressions (RE).
|
| 18 |
+
|
| 19 |
+
This module provides regular expression matching operations similar to
|
| 20 |
+
those found in Perl. It supports both 8-bit and Unicode strings; both
|
| 21 |
+
the pattern and the strings being processed can contain null bytes and
|
| 22 |
+
characters outside the US ASCII range.
|
| 23 |
+
|
| 24 |
+
Regular expressions can contain both special and ordinary characters.
|
| 25 |
+
Most ordinary characters, like "A", "a", or "0", are the simplest
|
| 26 |
+
regular expressions; they simply match themselves. You can
|
| 27 |
+
concatenate ordinary characters, so last matches the string 'last'.
|
| 28 |
+
|
| 29 |
+
The special characters are:
|
| 30 |
+
"." Matches any character except a newline.
|
| 31 |
+
"^" Matches the start of the string.
|
| 32 |
+
"$" Matches the end of the string or just before the newline at
|
| 33 |
+
the end of the string.
|
| 34 |
+
"*" Matches 0 or more (greedy) repetitions of the preceding RE.
|
| 35 |
+
Greedy means that it will match as many repetitions as possible.
|
| 36 |
+
"+" Matches 1 or more (greedy) repetitions of the preceding RE.
|
| 37 |
+
"?" Matches 0 or 1 (greedy) of the preceding RE.
|
| 38 |
+
*?,+?,?? Non-greedy versions of the previous three special characters.
|
| 39 |
+
{m,n} Matches from m to n repetitions of the preceding RE.
|
| 40 |
+
{m,n}? Non-greedy version of the above.
|
| 41 |
+
"\\" Either escapes special characters or signals a special sequence.
|
| 42 |
+
[] Indicates a set of characters.
|
| 43 |
+
A "^" as the first character indicates a complementing set.
|
| 44 |
+
"|" A|B, creates an RE that will match either A or B.
|
| 45 |
+
(...) Matches the RE inside the parentheses.
|
| 46 |
+
The contents can be retrieved or matched later in the string.
|
| 47 |
+
(?aiLmsux) The letters set the corresponding flags defined below.
|
| 48 |
+
(?:...) Non-grouping version of regular parentheses.
|
| 49 |
+
(?P<name>...) The substring matched by the group is accessible by name.
|
| 50 |
+
(?P=name) Matches the text matched earlier by the group named name.
|
| 51 |
+
(?#...) A comment; ignored.
|
| 52 |
+
(?=...) Matches if ... matches next, but doesn't consume the string.
|
| 53 |
+
(?!...) Matches if ... doesn't match next.
|
| 54 |
+
(?<=...) Matches if preceded by ... (must be fixed length).
|
| 55 |
+
(?<!...) Matches if not preceded by ... (must be fixed length).
|
| 56 |
+
(?(id/name)yes|no) Matches yes pattern if the group with id/name matched,
|
| 57 |
+
the (optional) no pattern otherwise.
|
| 58 |
+
|
| 59 |
+
The special sequences consist of "\\" and a character from the list
|
| 60 |
+
below. If the ordinary character is not on the list, then the
|
| 61 |
+
resulting RE will match the second character.
|
| 62 |
+
\number Matches the contents of the group of the same number.
|
| 63 |
+
\A Matches only at the start of the string.
|
| 64 |
+
\Z Matches only at the end of the string.
|
| 65 |
+
\b Matches the empty string, but only at the start or end of a word.
|
| 66 |
+
\B Matches the empty string, but not at the start or end of a word.
|
| 67 |
+
\d Matches any decimal digit; equivalent to the set [0-9] in
|
| 68 |
+
bytes patterns or string patterns with the ASCII flag.
|
| 69 |
+
In string patterns without the ASCII flag, it will match the whole
|
| 70 |
+
range of Unicode digits.
|
| 71 |
+
\D Matches any non-digit character; equivalent to [^\d].
|
| 72 |
+
\s Matches any whitespace character; equivalent to [ \t\n\r\f\v] in
|
| 73 |
+
bytes patterns or string patterns with the ASCII flag.
|
| 74 |
+
In string patterns without the ASCII flag, it will match the whole
|
| 75 |
+
range of Unicode whitespace characters.
|
| 76 |
+
\S Matches any non-whitespace character; equivalent to [^\s].
|
| 77 |
+
\w Matches any alphanumeric character; equivalent to [a-zA-Z0-9_]
|
| 78 |
+
in bytes patterns or string patterns with the ASCII flag.
|
| 79 |
+
In string patterns without the ASCII flag, it will match the
|
| 80 |
+
range of Unicode alphanumeric characters (letters plus digits
|
| 81 |
+
plus underscore).
|
| 82 |
+
With LOCALE, it will match the set [0-9_] plus characters defined
|
| 83 |
+
as letters for the current locale.
|
| 84 |
+
\W Matches the complement of \w.
|
| 85 |
+
\\ Matches a literal backslash.
|
| 86 |
+
|
| 87 |
+
This module exports the following functions:
|
| 88 |
+
match Match a regular expression pattern to the beginning of a string.
|
| 89 |
+
fullmatch Match a regular expression pattern to all of a string.
|
| 90 |
+
search Search a string for the presence of a pattern.
|
| 91 |
+
sub Substitute occurrences of a pattern found in a string.
|
| 92 |
+
subn Same as sub, but also return the number of substitutions made.
|
| 93 |
+
split Split a string by the occurrences of a pattern.
|
| 94 |
+
findall Find all occurrences of a pattern in a string.
|
| 95 |
+
finditer Return an iterator yielding a Match object for each match.
|
| 96 |
+
compile Compile a pattern into a Pattern object.
|
| 97 |
+
purge Clear the regular expression cache.
|
| 98 |
+
escape Backslash all non-alphanumerics in a string.
|
| 99 |
+
|
| 100 |
+
Each function other than purge and escape can take an optional 'flags' argument
|
| 101 |
+
consisting of one or more of the following module constants, joined by "|".
|
| 102 |
+
A, L, and U are mutually exclusive.
|
| 103 |
+
A ASCII For string patterns, make \w, \W, \b, \B, \d, \D
|
| 104 |
+
match the corresponding ASCII character categories
|
| 105 |
+
(rather than the whole Unicode categories, which is the
|
| 106 |
+
default).
|
| 107 |
+
For bytes patterns, this flag is the only available
|
| 108 |
+
behaviour and needn't be specified.
|
| 109 |
+
I IGNORECASE Perform case-insensitive matching.
|
| 110 |
+
L LOCALE Make \w, \W, \b, \B, dependent on the current locale.
|
| 111 |
+
M MULTILINE "^" matches the beginning of lines (after a newline)
|
| 112 |
+
as well as the string.
|
| 113 |
+
"$" matches the end of lines (before a newline) as well
|
| 114 |
+
as the end of the string.
|
| 115 |
+
S DOTALL "." matches any character at all, including the newline.
|
| 116 |
+
X VERBOSE Ignore whitespace and comments for nicer looking RE's.
|
| 117 |
+
U UNICODE For compatibility only. Ignored for string patterns (it
|
| 118 |
+
is the default), and forbidden for bytes patterns.
|
| 119 |
+
|
| 120 |
+
This module also defines an exception 'error'.
|
| 121 |
+
|
| 122 |
+
"""
|
| 123 |
+
|
| 124 |
+
import enum
|
| 125 |
+
import sre_compile
|
| 126 |
+
import sre_parse
|
| 127 |
+
import functools
|
| 128 |
+
try:
|
| 129 |
+
import _locale
|
| 130 |
+
except ImportError:
|
| 131 |
+
_locale = None
|
| 132 |
+
|
| 133 |
+
|
| 134 |
+
# public symbols
|
| 135 |
+
__all__ = [
|
| 136 |
+
"match", "fullmatch", "search", "sub", "subn", "split",
|
| 137 |
+
"findall", "finditer", "compile", "purge", "template", "escape",
|
| 138 |
+
"error", "Pattern", "Match", "A", "I", "L", "M", "S", "X", "U",
|
| 139 |
+
"ASCII", "IGNORECASE", "LOCALE", "MULTILINE", "DOTALL", "VERBOSE",
|
| 140 |
+
"UNICODE",
|
| 141 |
+
]
|
| 142 |
+
|
| 143 |
+
__version__ = "2.2.1"
|
| 144 |
+
|
| 145 |
+
class RegexFlag(enum.IntFlag):
|
| 146 |
+
ASCII = A = sre_compile.SRE_FLAG_ASCII # assume ascii "locale"
|
| 147 |
+
IGNORECASE = I = sre_compile.SRE_FLAG_IGNORECASE # ignore case
|
| 148 |
+
LOCALE = L = sre_compile.SRE_FLAG_LOCALE # assume current 8-bit locale
|
| 149 |
+
UNICODE = U = sre_compile.SRE_FLAG_UNICODE # assume unicode "locale"
|
| 150 |
+
MULTILINE = M = sre_compile.SRE_FLAG_MULTILINE # make anchors look for newline
|
| 151 |
+
DOTALL = S = sre_compile.SRE_FLAG_DOTALL # make dot match newline
|
| 152 |
+
VERBOSE = X = sre_compile.SRE_FLAG_VERBOSE # ignore whitespace and comments
|
| 153 |
+
# sre extensions (experimental, don't rely on these)
|
| 154 |
+
TEMPLATE = T = sre_compile.SRE_FLAG_TEMPLATE # disable backtracking
|
| 155 |
+
DEBUG = sre_compile.SRE_FLAG_DEBUG # dump pattern after compilation
|
| 156 |
+
|
| 157 |
+
def __repr__(self):
|
| 158 |
+
if self._name_ is not None:
|
| 159 |
+
return f're.{self._name_}'
|
| 160 |
+
value = self._value_
|
| 161 |
+
members = []
|
| 162 |
+
negative = value < 0
|
| 163 |
+
if negative:
|
| 164 |
+
value = ~value
|
| 165 |
+
for m in self.__class__:
|
| 166 |
+
if value & m._value_:
|
| 167 |
+
value &= ~m._value_
|
| 168 |
+
members.append(f're.{m._name_}')
|
| 169 |
+
if value:
|
| 170 |
+
members.append(hex(value))
|
| 171 |
+
res = '|'.join(members)
|
| 172 |
+
if negative:
|
| 173 |
+
if len(members) > 1:
|
| 174 |
+
res = f'~({res})'
|
| 175 |
+
else:
|
| 176 |
+
res = f'~{res}'
|
| 177 |
+
return res
|
| 178 |
+
__str__ = object.__str__
|
| 179 |
+
globals().update(RegexFlag.__members__)
|
| 180 |
+
|
| 181 |
+
# sre exception
|
| 182 |
+
error = sre_compile.error
|
| 183 |
+
|
| 184 |
+
# --------------------------------------------------------------------
|
| 185 |
+
# public interface
|
| 186 |
+
|
| 187 |
+
def match(pattern, string, flags=0):
|
| 188 |
+
"""Try to apply the pattern at the start of the string, returning
|
| 189 |
+
a Match object, or None if no match was found."""
|
| 190 |
+
return _compile(pattern, flags).match(string)
|
| 191 |
+
|
| 192 |
+
def fullmatch(pattern, string, flags=0):
|
| 193 |
+
"""Try to apply the pattern to all of the string, returning
|
| 194 |
+
a Match object, or None if no match was found."""
|
| 195 |
+
return _compile(pattern, flags).fullmatch(string)
|
| 196 |
+
|
| 197 |
+
def search(pattern, string, flags=0):
|
| 198 |
+
"""Scan through string looking for a match to the pattern, returning
|
| 199 |
+
a Match object, or None if no match was found."""
|
| 200 |
+
return _compile(pattern, flags).search(string)
|
| 201 |
+
|
| 202 |
+
def sub(pattern, repl, string, count=0, flags=0):
|
| 203 |
+
"""Return the string obtained by replacing the leftmost
|
| 204 |
+
non-overlapping occurrences of the pattern in string by the
|
| 205 |
+
replacement repl. repl can be either a string or a callable;
|
| 206 |
+
if a string, backslash escapes in it are processed. If it is
|
| 207 |
+
a callable, it's passed the Match object and must return
|
| 208 |
+
a replacement string to be used."""
|
| 209 |
+
return _compile(pattern, flags).sub(repl, string, count)
|
| 210 |
+
|
| 211 |
+
def subn(pattern, repl, string, count=0, flags=0):
|
| 212 |
+
"""Return a 2-tuple containing (new_string, number).
|
| 213 |
+
new_string is the string obtained by replacing the leftmost
|
| 214 |
+
non-overlapping occurrences of the pattern in the source
|
| 215 |
+
string by the replacement repl. number is the number of
|
| 216 |
+
substitutions that were made. repl can be either a string or a
|
| 217 |
+
callable; if a string, backslash escapes in it are processed.
|
| 218 |
+
If it is a callable, it's passed the Match object and must
|
| 219 |
+
return a replacement string to be used."""
|
| 220 |
+
return _compile(pattern, flags).subn(repl, string, count)
|
| 221 |
+
|
| 222 |
+
def split(pattern, string, maxsplit=0, flags=0):
|
| 223 |
+
"""Split the source string by the occurrences of the pattern,
|
| 224 |
+
returning a list containing the resulting substrings. If
|
| 225 |
+
capturing parentheses are used in pattern, then the text of all
|
| 226 |
+
groups in the pattern are also returned as part of the resulting
|
| 227 |
+
list. If maxsplit is nonzero, at most maxsplit splits occur,
|
| 228 |
+
and the remainder of the string is returned as the final element
|
| 229 |
+
of the list."""
|
| 230 |
+
return _compile(pattern, flags).split(string, maxsplit)
|
| 231 |
+
|
| 232 |
+
def findall(pattern, string, flags=0):
|
| 233 |
+
"""Return a list of all non-overlapping matches in the string.
|
| 234 |
+
|
| 235 |
+
If one or more capturing groups are present in the pattern, return
|
| 236 |
+
a list of groups; this will be a list of tuples if the pattern
|
| 237 |
+
has more than one group.
|
| 238 |
+
|
| 239 |
+
Empty matches are included in the result."""
|
| 240 |
+
return _compile(pattern, flags).findall(string)
|
| 241 |
+
|
| 242 |
+
def finditer(pattern, string, flags=0):
|
| 243 |
+
"""Return an iterator over all non-overlapping matches in the
|
| 244 |
+
string. For each match, the iterator returns a Match object.
|
| 245 |
+
|
| 246 |
+
Empty matches are included in the result."""
|
| 247 |
+
return _compile(pattern, flags).finditer(string)
|
| 248 |
+
|
| 249 |
+
def compile(pattern, flags=0):
|
| 250 |
+
"Compile a regular expression pattern, returning a Pattern object."
|
| 251 |
+
return _compile(pattern, flags)
|
| 252 |
+
|
| 253 |
+
def purge():
|
| 254 |
+
"Clear the regular expression caches"
|
| 255 |
+
_cache.clear()
|
| 256 |
+
_compile_repl.cache_clear()
|
| 257 |
+
|
| 258 |
+
def template(pattern, flags=0):
|
| 259 |
+
"Compile a template pattern, returning a Pattern object"
|
| 260 |
+
return _compile(pattern, flags|T)
|
| 261 |
+
|
| 262 |
+
# SPECIAL_CHARS
|
| 263 |
+
# closing ')', '}' and ']'
|
| 264 |
+
# '-' (a range in character set)
|
| 265 |
+
# '&', '~', (extended character set operations)
|
| 266 |
+
# '#' (comment) and WHITESPACE (ignored) in verbose mode
|
| 267 |
+
_special_chars_map = {i: '\\' + chr(i) for i in b'()[]{}?*+-|^$\\.&~# \t\n\r\v\f'}
|
| 268 |
+
|
| 269 |
+
def escape(pattern):
|
| 270 |
+
"""
|
| 271 |
+
Escape special characters in a string.
|
| 272 |
+
"""
|
| 273 |
+
if isinstance(pattern, str):
|
| 274 |
+
return pattern.translate(_special_chars_map)
|
| 275 |
+
else:
|
| 276 |
+
pattern = str(pattern, 'latin1')
|
| 277 |
+
return pattern.translate(_special_chars_map).encode('latin1')
|
| 278 |
+
|
| 279 |
+
Pattern = type(sre_compile.compile('', 0))
|
| 280 |
+
Match = type(sre_compile.compile('', 0).match(''))
|
| 281 |
+
|
| 282 |
+
# --------------------------------------------------------------------
|
| 283 |
+
# internals
|
| 284 |
+
|
| 285 |
+
_cache = {} # ordered!
|
| 286 |
+
|
| 287 |
+
_MAXCACHE = 512
|
| 288 |
+
def _compile(pattern, flags):
|
| 289 |
+
# internal: compile pattern
|
| 290 |
+
if isinstance(flags, RegexFlag):
|
| 291 |
+
flags = flags.value
|
| 292 |
+
try:
|
| 293 |
+
return _cache[type(pattern), pattern, flags]
|
| 294 |
+
except KeyError:
|
| 295 |
+
pass
|
| 296 |
+
if isinstance(pattern, Pattern):
|
| 297 |
+
if flags:
|
| 298 |
+
raise ValueError(
|
| 299 |
+
"cannot process flags argument with a compiled pattern")
|
| 300 |
+
return pattern
|
| 301 |
+
if not sre_compile.isstring(pattern):
|
| 302 |
+
raise TypeError("first argument must be string or compiled pattern")
|
| 303 |
+
p = sre_compile.compile(pattern, flags)
|
| 304 |
+
if not (flags & DEBUG):
|
| 305 |
+
if len(_cache) >= _MAXCACHE:
|
| 306 |
+
# Drop the oldest item
|
| 307 |
+
try:
|
| 308 |
+
del _cache[next(iter(_cache))]
|
| 309 |
+
except (StopIteration, RuntimeError, KeyError):
|
| 310 |
+
pass
|
| 311 |
+
_cache[type(pattern), pattern, flags] = p
|
| 312 |
+
return p
|
| 313 |
+
|
| 314 |
+
@functools.lru_cache(_MAXCACHE)
|
| 315 |
+
def _compile_repl(repl, pattern):
|
| 316 |
+
# internal: compile replacement pattern
|
| 317 |
+
return sre_parse.parse_template(repl, pattern)
|
| 318 |
+
|
| 319 |
+
def _expand(pattern, match, template):
|
| 320 |
+
# internal: Match.expand implementation hook
|
| 321 |
+
template = sre_parse.parse_template(template, pattern)
|
| 322 |
+
return sre_parse.expand_template(template, match)
|
| 323 |
+
|
| 324 |
+
def _subx(pattern, template):
|
| 325 |
+
# internal: Pattern.sub/subn implementation helper
|
| 326 |
+
template = _compile_repl(template, pattern)
|
| 327 |
+
if not template[0] and len(template[1]) == 1:
|
| 328 |
+
# literal replacement
|
| 329 |
+
return template[1][0]
|
| 330 |
+
def filter(match, template=template):
|
| 331 |
+
return sre_parse.expand_template(template, match)
|
| 332 |
+
return filter
|
| 333 |
+
|
| 334 |
+
# register myself for pickling
|
| 335 |
+
|
| 336 |
+
import copyreg
|
| 337 |
+
|
| 338 |
+
def _pickle(p):
|
| 339 |
+
return _compile, (p.pattern, p.flags)
|
| 340 |
+
|
| 341 |
+
copyreg.pickle(Pattern, _pickle, _compile)
|
| 342 |
+
|
| 343 |
+
# --------------------------------------------------------------------
|
| 344 |
+
# experimental stuff (see python-dev discussions for details)
|
| 345 |
+
|
| 346 |
+
class Scanner:
|
| 347 |
+
def __init__(self, lexicon, flags=0):
|
| 348 |
+
from sre_constants import BRANCH, SUBPATTERN
|
| 349 |
+
if isinstance(flags, RegexFlag):
|
| 350 |
+
flags = flags.value
|
| 351 |
+
self.lexicon = lexicon
|
| 352 |
+
# combine phrases into a compound pattern
|
| 353 |
+
p = []
|
| 354 |
+
s = sre_parse.State()
|
| 355 |
+
s.flags = flags
|
| 356 |
+
for phrase, action in lexicon:
|
| 357 |
+
gid = s.opengroup()
|
| 358 |
+
p.append(sre_parse.SubPattern(s, [
|
| 359 |
+
(SUBPATTERN, (gid, 0, 0, sre_parse.parse(phrase, flags))),
|
| 360 |
+
]))
|
| 361 |
+
s.closegroup(gid, p[-1])
|
| 362 |
+
p = sre_parse.SubPattern(s, [(BRANCH, (None, p))])
|
| 363 |
+
self.scanner = sre_compile.compile(p)
|
| 364 |
+
def scan(self, string):
|
| 365 |
+
result = []
|
| 366 |
+
append = result.append
|
| 367 |
+
match = self.scanner.scanner(string).match
|
| 368 |
+
i = 0
|
| 369 |
+
while True:
|
| 370 |
+
m = match()
|
| 371 |
+
if not m:
|
| 372 |
+
break
|
| 373 |
+
j = m.end()
|
| 374 |
+
if i == j:
|
| 375 |
+
break
|
| 376 |
+
action = self.lexicon[m.lastindex-1][1]
|
| 377 |
+
if callable(action):
|
| 378 |
+
self.match = m
|
| 379 |
+
action = action(self, m.group())
|
| 380 |
+
if action is not None:
|
| 381 |
+
append(action)
|
| 382 |
+
i = j
|
| 383 |
+
return result, string[i:]
|
lib/python3.10/reprlib.py
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Redo the builtin repr() (representation) but with limits on most sizes."""
|
| 2 |
+
|
| 3 |
+
__all__ = ["Repr", "repr", "recursive_repr"]
|
| 4 |
+
|
| 5 |
+
import builtins
|
| 6 |
+
from itertools import islice
|
| 7 |
+
from _thread import get_ident
|
| 8 |
+
|
| 9 |
+
def recursive_repr(fillvalue='...'):
|
| 10 |
+
'Decorator to make a repr function return fillvalue for a recursive call'
|
| 11 |
+
|
| 12 |
+
def decorating_function(user_function):
|
| 13 |
+
repr_running = set()
|
| 14 |
+
|
| 15 |
+
def wrapper(self):
|
| 16 |
+
key = id(self), get_ident()
|
| 17 |
+
if key in repr_running:
|
| 18 |
+
return fillvalue
|
| 19 |
+
repr_running.add(key)
|
| 20 |
+
try:
|
| 21 |
+
result = user_function(self)
|
| 22 |
+
finally:
|
| 23 |
+
repr_running.discard(key)
|
| 24 |
+
return result
|
| 25 |
+
|
| 26 |
+
# Can't use functools.wraps() here because of bootstrap issues
|
| 27 |
+
wrapper.__module__ = getattr(user_function, '__module__')
|
| 28 |
+
wrapper.__doc__ = getattr(user_function, '__doc__')
|
| 29 |
+
wrapper.__name__ = getattr(user_function, '__name__')
|
| 30 |
+
wrapper.__qualname__ = getattr(user_function, '__qualname__')
|
| 31 |
+
wrapper.__annotations__ = getattr(user_function, '__annotations__', {})
|
| 32 |
+
return wrapper
|
| 33 |
+
|
| 34 |
+
return decorating_function
|
| 35 |
+
|
| 36 |
+
class Repr:
|
| 37 |
+
|
| 38 |
+
def __init__(self):
|
| 39 |
+
self.maxlevel = 6
|
| 40 |
+
self.maxtuple = 6
|
| 41 |
+
self.maxlist = 6
|
| 42 |
+
self.maxarray = 5
|
| 43 |
+
self.maxdict = 4
|
| 44 |
+
self.maxset = 6
|
| 45 |
+
self.maxfrozenset = 6
|
| 46 |
+
self.maxdeque = 6
|
| 47 |
+
self.maxstring = 30
|
| 48 |
+
self.maxlong = 40
|
| 49 |
+
self.maxother = 30
|
| 50 |
+
|
| 51 |
+
def repr(self, x):
|
| 52 |
+
return self.repr1(x, self.maxlevel)
|
| 53 |
+
|
| 54 |
+
def repr1(self, x, level):
|
| 55 |
+
typename = type(x).__name__
|
| 56 |
+
if ' ' in typename:
|
| 57 |
+
parts = typename.split()
|
| 58 |
+
typename = '_'.join(parts)
|
| 59 |
+
if hasattr(self, 'repr_' + typename):
|
| 60 |
+
return getattr(self, 'repr_' + typename)(x, level)
|
| 61 |
+
else:
|
| 62 |
+
return self.repr_instance(x, level)
|
| 63 |
+
|
| 64 |
+
def _repr_iterable(self, x, level, left, right, maxiter, trail=''):
|
| 65 |
+
n = len(x)
|
| 66 |
+
if level <= 0 and n:
|
| 67 |
+
s = '...'
|
| 68 |
+
else:
|
| 69 |
+
newlevel = level - 1
|
| 70 |
+
repr1 = self.repr1
|
| 71 |
+
pieces = [repr1(elem, newlevel) for elem in islice(x, maxiter)]
|
| 72 |
+
if n > maxiter: pieces.append('...')
|
| 73 |
+
s = ', '.join(pieces)
|
| 74 |
+
if n == 1 and trail: right = trail + right
|
| 75 |
+
return '%s%s%s' % (left, s, right)
|
| 76 |
+
|
| 77 |
+
def repr_tuple(self, x, level):
|
| 78 |
+
return self._repr_iterable(x, level, '(', ')', self.maxtuple, ',')
|
| 79 |
+
|
| 80 |
+
def repr_list(self, x, level):
|
| 81 |
+
return self._repr_iterable(x, level, '[', ']', self.maxlist)
|
| 82 |
+
|
| 83 |
+
def repr_array(self, x, level):
|
| 84 |
+
if not x:
|
| 85 |
+
return "array('%s')" % x.typecode
|
| 86 |
+
header = "array('%s', [" % x.typecode
|
| 87 |
+
return self._repr_iterable(x, level, header, '])', self.maxarray)
|
| 88 |
+
|
| 89 |
+
def repr_set(self, x, level):
|
| 90 |
+
if not x:
|
| 91 |
+
return 'set()'
|
| 92 |
+
x = _possibly_sorted(x)
|
| 93 |
+
return self._repr_iterable(x, level, '{', '}', self.maxset)
|
| 94 |
+
|
| 95 |
+
def repr_frozenset(self, x, level):
|
| 96 |
+
if not x:
|
| 97 |
+
return 'frozenset()'
|
| 98 |
+
x = _possibly_sorted(x)
|
| 99 |
+
return self._repr_iterable(x, level, 'frozenset({', '})',
|
| 100 |
+
self.maxfrozenset)
|
| 101 |
+
|
| 102 |
+
def repr_deque(self, x, level):
|
| 103 |
+
return self._repr_iterable(x, level, 'deque([', '])', self.maxdeque)
|
| 104 |
+
|
| 105 |
+
def repr_dict(self, x, level):
|
| 106 |
+
n = len(x)
|
| 107 |
+
if n == 0: return '{}'
|
| 108 |
+
if level <= 0: return '{...}'
|
| 109 |
+
newlevel = level - 1
|
| 110 |
+
repr1 = self.repr1
|
| 111 |
+
pieces = []
|
| 112 |
+
for key in islice(_possibly_sorted(x), self.maxdict):
|
| 113 |
+
keyrepr = repr1(key, newlevel)
|
| 114 |
+
valrepr = repr1(x[key], newlevel)
|
| 115 |
+
pieces.append('%s: %s' % (keyrepr, valrepr))
|
| 116 |
+
if n > self.maxdict: pieces.append('...')
|
| 117 |
+
s = ', '.join(pieces)
|
| 118 |
+
return '{%s}' % (s,)
|
| 119 |
+
|
| 120 |
+
def repr_str(self, x, level):
|
| 121 |
+
s = builtins.repr(x[:self.maxstring])
|
| 122 |
+
if len(s) > self.maxstring:
|
| 123 |
+
i = max(0, (self.maxstring-3)//2)
|
| 124 |
+
j = max(0, self.maxstring-3-i)
|
| 125 |
+
s = builtins.repr(x[:i] + x[len(x)-j:])
|
| 126 |
+
s = s[:i] + '...' + s[len(s)-j:]
|
| 127 |
+
return s
|
| 128 |
+
|
| 129 |
+
def repr_int(self, x, level):
|
| 130 |
+
s = builtins.repr(x) # XXX Hope this isn't too slow...
|
| 131 |
+
if len(s) > self.maxlong:
|
| 132 |
+
i = max(0, (self.maxlong-3)//2)
|
| 133 |
+
j = max(0, self.maxlong-3-i)
|
| 134 |
+
s = s[:i] + '...' + s[len(s)-j:]
|
| 135 |
+
return s
|
| 136 |
+
|
| 137 |
+
def repr_instance(self, x, level):
|
| 138 |
+
try:
|
| 139 |
+
s = builtins.repr(x)
|
| 140 |
+
# Bugs in x.__repr__() can cause arbitrary
|
| 141 |
+
# exceptions -- then make up something
|
| 142 |
+
except Exception:
|
| 143 |
+
return '<%s instance at %#x>' % (x.__class__.__name__, id(x))
|
| 144 |
+
if len(s) > self.maxother:
|
| 145 |
+
i = max(0, (self.maxother-3)//2)
|
| 146 |
+
j = max(0, self.maxother-3-i)
|
| 147 |
+
s = s[:i] + '...' + s[len(s)-j:]
|
| 148 |
+
return s
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
def _possibly_sorted(x):
|
| 152 |
+
# Since not all sequences of items can be sorted and comparison
|
| 153 |
+
# functions may raise arbitrary exceptions, return an unsorted
|
| 154 |
+
# sequence in that case.
|
| 155 |
+
try:
|
| 156 |
+
return sorted(x)
|
| 157 |
+
except Exception:
|
| 158 |
+
return list(x)
|
| 159 |
+
|
| 160 |
+
aRepr = Repr()
|
| 161 |
+
repr = aRepr.repr
|
lib/python3.10/rlcompleter.py
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Word completion for GNU readline.
|
| 2 |
+
|
| 3 |
+
The completer completes keywords, built-ins and globals in a selectable
|
| 4 |
+
namespace (which defaults to __main__); when completing NAME.NAME..., it
|
| 5 |
+
evaluates (!) the expression up to the last dot and completes its attributes.
|
| 6 |
+
|
| 7 |
+
It's very cool to do "import sys" type "sys.", hit the completion key (twice),
|
| 8 |
+
and see the list of names defined by the sys module!
|
| 9 |
+
|
| 10 |
+
Tip: to use the tab key as the completion key, call
|
| 11 |
+
|
| 12 |
+
readline.parse_and_bind("tab: complete")
|
| 13 |
+
|
| 14 |
+
Notes:
|
| 15 |
+
|
| 16 |
+
- Exceptions raised by the completer function are *ignored* (and generally cause
|
| 17 |
+
the completion to fail). This is a feature -- since readline sets the tty
|
| 18 |
+
device in raw (or cbreak) mode, printing a traceback wouldn't work well
|
| 19 |
+
without some complicated hoopla to save, reset and restore the tty state.
|
| 20 |
+
|
| 21 |
+
- The evaluation of the NAME.NAME... form may cause arbitrary application
|
| 22 |
+
defined code to be executed if an object with a __getattr__ hook is found.
|
| 23 |
+
Since it is the responsibility of the application (or the user) to enable this
|
| 24 |
+
feature, I consider this an acceptable risk. More complicated expressions
|
| 25 |
+
(e.g. function calls or indexing operations) are *not* evaluated.
|
| 26 |
+
|
| 27 |
+
- When the original stdin is not a tty device, GNU readline is never
|
| 28 |
+
used, and this module (and the readline module) are silently inactive.
|
| 29 |
+
|
| 30 |
+
"""
|
| 31 |
+
|
| 32 |
+
import atexit
|
| 33 |
+
import builtins
|
| 34 |
+
import inspect
|
| 35 |
+
import __main__
|
| 36 |
+
|
| 37 |
+
__all__ = ["Completer"]
|
| 38 |
+
|
| 39 |
+
class Completer:
|
| 40 |
+
def __init__(self, namespace = None):
|
| 41 |
+
"""Create a new completer for the command line.
|
| 42 |
+
|
| 43 |
+
Completer([namespace]) -> completer instance.
|
| 44 |
+
|
| 45 |
+
If unspecified, the default namespace where completions are performed
|
| 46 |
+
is __main__ (technically, __main__.__dict__). Namespaces should be
|
| 47 |
+
given as dictionaries.
|
| 48 |
+
|
| 49 |
+
Completer instances should be used as the completion mechanism of
|
| 50 |
+
readline via the set_completer() call:
|
| 51 |
+
|
| 52 |
+
readline.set_completer(Completer(my_namespace).complete)
|
| 53 |
+
"""
|
| 54 |
+
|
| 55 |
+
if namespace and not isinstance(namespace, dict):
|
| 56 |
+
raise TypeError('namespace must be a dictionary')
|
| 57 |
+
|
| 58 |
+
# Don't bind to namespace quite yet, but flag whether the user wants a
|
| 59 |
+
# specific namespace or to use __main__.__dict__. This will allow us
|
| 60 |
+
# to bind to __main__.__dict__ at completion time, not now.
|
| 61 |
+
if namespace is None:
|
| 62 |
+
self.use_main_ns = 1
|
| 63 |
+
else:
|
| 64 |
+
self.use_main_ns = 0
|
| 65 |
+
self.namespace = namespace
|
| 66 |
+
|
| 67 |
+
def complete(self, text, state):
|
| 68 |
+
"""Return the next possible completion for 'text'.
|
| 69 |
+
|
| 70 |
+
This is called successively with state == 0, 1, 2, ... until it
|
| 71 |
+
returns None. The completion should begin with 'text'.
|
| 72 |
+
|
| 73 |
+
"""
|
| 74 |
+
if self.use_main_ns:
|
| 75 |
+
self.namespace = __main__.__dict__
|
| 76 |
+
|
| 77 |
+
if not text.strip():
|
| 78 |
+
if state == 0:
|
| 79 |
+
if _readline_available:
|
| 80 |
+
readline.insert_text('\t')
|
| 81 |
+
readline.redisplay()
|
| 82 |
+
return ''
|
| 83 |
+
else:
|
| 84 |
+
return '\t'
|
| 85 |
+
else:
|
| 86 |
+
return None
|
| 87 |
+
|
| 88 |
+
if state == 0:
|
| 89 |
+
if "." in text:
|
| 90 |
+
self.matches = self.attr_matches(text)
|
| 91 |
+
else:
|
| 92 |
+
self.matches = self.global_matches(text)
|
| 93 |
+
try:
|
| 94 |
+
return self.matches[state]
|
| 95 |
+
except IndexError:
|
| 96 |
+
return None
|
| 97 |
+
|
| 98 |
+
def _callable_postfix(self, val, word):
|
| 99 |
+
if callable(val):
|
| 100 |
+
word += "("
|
| 101 |
+
try:
|
| 102 |
+
if not inspect.signature(val).parameters:
|
| 103 |
+
word += ")"
|
| 104 |
+
except ValueError:
|
| 105 |
+
pass
|
| 106 |
+
|
| 107 |
+
return word
|
| 108 |
+
|
| 109 |
+
def global_matches(self, text):
|
| 110 |
+
"""Compute matches when text is a simple name.
|
| 111 |
+
|
| 112 |
+
Return a list of all keywords, built-in functions and names currently
|
| 113 |
+
defined in self.namespace that match.
|
| 114 |
+
|
| 115 |
+
"""
|
| 116 |
+
import keyword
|
| 117 |
+
matches = []
|
| 118 |
+
seen = {"__builtins__"}
|
| 119 |
+
n = len(text)
|
| 120 |
+
for word in keyword.kwlist:
|
| 121 |
+
if word[:n] == text:
|
| 122 |
+
seen.add(word)
|
| 123 |
+
if word in {'finally', 'try'}:
|
| 124 |
+
word = word + ':'
|
| 125 |
+
elif word not in {'False', 'None', 'True',
|
| 126 |
+
'break', 'continue', 'pass',
|
| 127 |
+
'else'}:
|
| 128 |
+
word = word + ' '
|
| 129 |
+
matches.append(word)
|
| 130 |
+
for nspace in [self.namespace, builtins.__dict__]:
|
| 131 |
+
for word, val in nspace.items():
|
| 132 |
+
if word[:n] == text and word not in seen:
|
| 133 |
+
seen.add(word)
|
| 134 |
+
matches.append(self._callable_postfix(val, word))
|
| 135 |
+
return matches
|
| 136 |
+
|
| 137 |
+
def attr_matches(self, text):
|
| 138 |
+
"""Compute matches when text contains a dot.
|
| 139 |
+
|
| 140 |
+
Assuming the text is of the form NAME.NAME....[NAME], and is
|
| 141 |
+
evaluable in self.namespace, it will be evaluated and its attributes
|
| 142 |
+
(as revealed by dir()) are used as possible completions. (For class
|
| 143 |
+
instances, class members are also considered.)
|
| 144 |
+
|
| 145 |
+
WARNING: this can still invoke arbitrary C code, if an object
|
| 146 |
+
with a __getattr__ hook is evaluated.
|
| 147 |
+
|
| 148 |
+
"""
|
| 149 |
+
import re
|
| 150 |
+
m = re.match(r"(\w+(\.\w+)*)\.(\w*)", text)
|
| 151 |
+
if not m:
|
| 152 |
+
return []
|
| 153 |
+
expr, attr = m.group(1, 3)
|
| 154 |
+
try:
|
| 155 |
+
thisobject = eval(expr, self.namespace)
|
| 156 |
+
except Exception:
|
| 157 |
+
return []
|
| 158 |
+
|
| 159 |
+
# get the content of the object, except __builtins__
|
| 160 |
+
words = set(dir(thisobject))
|
| 161 |
+
words.discard("__builtins__")
|
| 162 |
+
|
| 163 |
+
if hasattr(thisobject, '__class__'):
|
| 164 |
+
words.add('__class__')
|
| 165 |
+
words.update(get_class_members(thisobject.__class__))
|
| 166 |
+
matches = []
|
| 167 |
+
n = len(attr)
|
| 168 |
+
if attr == '':
|
| 169 |
+
noprefix = '_'
|
| 170 |
+
elif attr == '_':
|
| 171 |
+
noprefix = '__'
|
| 172 |
+
else:
|
| 173 |
+
noprefix = None
|
| 174 |
+
while True:
|
| 175 |
+
for word in words:
|
| 176 |
+
if (word[:n] == attr and
|
| 177 |
+
not (noprefix and word[:n+1] == noprefix)):
|
| 178 |
+
match = "%s.%s" % (expr, word)
|
| 179 |
+
if isinstance(getattr(type(thisobject), word, None),
|
| 180 |
+
property):
|
| 181 |
+
# bpo-44752: thisobject.word is a method decorated by
|
| 182 |
+
# `@property`. What follows applies a postfix if
|
| 183 |
+
# thisobject.word is callable, but know we know that
|
| 184 |
+
# this is not callable (because it is a property).
|
| 185 |
+
# Also, getattr(thisobject, word) will evaluate the
|
| 186 |
+
# property method, which is not desirable.
|
| 187 |
+
matches.append(match)
|
| 188 |
+
continue
|
| 189 |
+
if (value := getattr(thisobject, word, None)) is not None:
|
| 190 |
+
matches.append(self._callable_postfix(value, match))
|
| 191 |
+
else:
|
| 192 |
+
matches.append(match)
|
| 193 |
+
if matches or not noprefix:
|
| 194 |
+
break
|
| 195 |
+
if noprefix == '_':
|
| 196 |
+
noprefix = '__'
|
| 197 |
+
else:
|
| 198 |
+
noprefix = None
|
| 199 |
+
matches.sort()
|
| 200 |
+
return matches
|
| 201 |
+
|
| 202 |
+
def get_class_members(klass):
|
| 203 |
+
ret = dir(klass)
|
| 204 |
+
if hasattr(klass,'__bases__'):
|
| 205 |
+
for base in klass.__bases__:
|
| 206 |
+
ret = ret + get_class_members(base)
|
| 207 |
+
return ret
|
| 208 |
+
|
| 209 |
+
try:
|
| 210 |
+
import readline
|
| 211 |
+
except ImportError:
|
| 212 |
+
_readline_available = False
|
| 213 |
+
else:
|
| 214 |
+
readline.set_completer(Completer().complete)
|
| 215 |
+
# Release references early at shutdown (the readline module's
|
| 216 |
+
# contents are quasi-immortal, and the completer function holds a
|
| 217 |
+
# reference to globals).
|
| 218 |
+
atexit.register(lambda: readline.set_completer(None))
|
| 219 |
+
_readline_available = True
|
lib/python3.10/runpy.py
ADDED
|
@@ -0,0 +1,321 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""runpy.py - locating and running Python code using the module namespace
|
| 2 |
+
|
| 3 |
+
Provides support for locating and running Python scripts using the Python
|
| 4 |
+
module namespace instead of the native filesystem.
|
| 5 |
+
|
| 6 |
+
This allows Python code to play nicely with non-filesystem based PEP 302
|
| 7 |
+
importers when locating support scripts as well as when importing modules.
|
| 8 |
+
"""
|
| 9 |
+
# Written by Nick Coghlan <ncoghlan at gmail.com>
|
| 10 |
+
# to implement PEP 338 (Executing Modules as Scripts)
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
import sys
|
| 14 |
+
import importlib.machinery # importlib first so we can test #15386 via -m
|
| 15 |
+
import importlib.util
|
| 16 |
+
import io
|
| 17 |
+
import types
|
| 18 |
+
import os
|
| 19 |
+
|
| 20 |
+
__all__ = [
|
| 21 |
+
"run_module", "run_path",
|
| 22 |
+
]
|
| 23 |
+
|
| 24 |
+
class _TempModule(object):
|
| 25 |
+
"""Temporarily replace a module in sys.modules with an empty namespace"""
|
| 26 |
+
def __init__(self, mod_name):
|
| 27 |
+
self.mod_name = mod_name
|
| 28 |
+
self.module = types.ModuleType(mod_name)
|
| 29 |
+
self._saved_module = []
|
| 30 |
+
|
| 31 |
+
def __enter__(self):
|
| 32 |
+
mod_name = self.mod_name
|
| 33 |
+
try:
|
| 34 |
+
self._saved_module.append(sys.modules[mod_name])
|
| 35 |
+
except KeyError:
|
| 36 |
+
pass
|
| 37 |
+
sys.modules[mod_name] = self.module
|
| 38 |
+
return self
|
| 39 |
+
|
| 40 |
+
def __exit__(self, *args):
|
| 41 |
+
if self._saved_module:
|
| 42 |
+
sys.modules[self.mod_name] = self._saved_module[0]
|
| 43 |
+
else:
|
| 44 |
+
del sys.modules[self.mod_name]
|
| 45 |
+
self._saved_module = []
|
| 46 |
+
|
| 47 |
+
class _ModifiedArgv0(object):
|
| 48 |
+
def __init__(self, value):
|
| 49 |
+
self.value = value
|
| 50 |
+
self._saved_value = self._sentinel = object()
|
| 51 |
+
|
| 52 |
+
def __enter__(self):
|
| 53 |
+
if self._saved_value is not self._sentinel:
|
| 54 |
+
raise RuntimeError("Already preserving saved value")
|
| 55 |
+
self._saved_value = sys.argv[0]
|
| 56 |
+
sys.argv[0] = self.value
|
| 57 |
+
|
| 58 |
+
def __exit__(self, *args):
|
| 59 |
+
self.value = self._sentinel
|
| 60 |
+
sys.argv[0] = self._saved_value
|
| 61 |
+
|
| 62 |
+
# TODO: Replace these helpers with importlib._bootstrap_external functions.
|
| 63 |
+
def _run_code(code, run_globals, init_globals=None,
|
| 64 |
+
mod_name=None, mod_spec=None,
|
| 65 |
+
pkg_name=None, script_name=None):
|
| 66 |
+
"""Helper to run code in nominated namespace"""
|
| 67 |
+
if init_globals is not None:
|
| 68 |
+
run_globals.update(init_globals)
|
| 69 |
+
if mod_spec is None:
|
| 70 |
+
loader = None
|
| 71 |
+
fname = script_name
|
| 72 |
+
cached = None
|
| 73 |
+
else:
|
| 74 |
+
loader = mod_spec.loader
|
| 75 |
+
fname = mod_spec.origin
|
| 76 |
+
cached = mod_spec.cached
|
| 77 |
+
if pkg_name is None:
|
| 78 |
+
pkg_name = mod_spec.parent
|
| 79 |
+
run_globals.update(__name__ = mod_name,
|
| 80 |
+
__file__ = fname,
|
| 81 |
+
__cached__ = cached,
|
| 82 |
+
__doc__ = None,
|
| 83 |
+
__loader__ = loader,
|
| 84 |
+
__package__ = pkg_name,
|
| 85 |
+
__spec__ = mod_spec)
|
| 86 |
+
exec(code, run_globals)
|
| 87 |
+
return run_globals
|
| 88 |
+
|
| 89 |
+
def _run_module_code(code, init_globals=None,
|
| 90 |
+
mod_name=None, mod_spec=None,
|
| 91 |
+
pkg_name=None, script_name=None):
|
| 92 |
+
"""Helper to run code in new namespace with sys modified"""
|
| 93 |
+
fname = script_name if mod_spec is None else mod_spec.origin
|
| 94 |
+
with _TempModule(mod_name) as temp_module, _ModifiedArgv0(fname):
|
| 95 |
+
mod_globals = temp_module.module.__dict__
|
| 96 |
+
_run_code(code, mod_globals, init_globals,
|
| 97 |
+
mod_name, mod_spec, pkg_name, script_name)
|
| 98 |
+
# Copy the globals of the temporary module, as they
|
| 99 |
+
# may be cleared when the temporary module goes away
|
| 100 |
+
return mod_globals.copy()
|
| 101 |
+
|
| 102 |
+
# Helper to get the full name, spec and code for a module
|
| 103 |
+
def _get_module_details(mod_name, error=ImportError):
|
| 104 |
+
if mod_name.startswith("."):
|
| 105 |
+
raise error("Relative module names not supported")
|
| 106 |
+
pkg_name, _, _ = mod_name.rpartition(".")
|
| 107 |
+
if pkg_name:
|
| 108 |
+
# Try importing the parent to avoid catching initialization errors
|
| 109 |
+
try:
|
| 110 |
+
__import__(pkg_name)
|
| 111 |
+
except ImportError as e:
|
| 112 |
+
# If the parent or higher ancestor package is missing, let the
|
| 113 |
+
# error be raised by find_spec() below and then be caught. But do
|
| 114 |
+
# not allow other errors to be caught.
|
| 115 |
+
if e.name is None or (e.name != pkg_name and
|
| 116 |
+
not pkg_name.startswith(e.name + ".")):
|
| 117 |
+
raise
|
| 118 |
+
# Warn if the module has already been imported under its normal name
|
| 119 |
+
existing = sys.modules.get(mod_name)
|
| 120 |
+
if existing is not None and not hasattr(existing, "__path__"):
|
| 121 |
+
from warnings import warn
|
| 122 |
+
msg = "{mod_name!r} found in sys.modules after import of " \
|
| 123 |
+
"package {pkg_name!r}, but prior to execution of " \
|
| 124 |
+
"{mod_name!r}; this may result in unpredictable " \
|
| 125 |
+
"behaviour".format(mod_name=mod_name, pkg_name=pkg_name)
|
| 126 |
+
warn(RuntimeWarning(msg))
|
| 127 |
+
|
| 128 |
+
try:
|
| 129 |
+
spec = importlib.util.find_spec(mod_name)
|
| 130 |
+
except (ImportError, AttributeError, TypeError, ValueError) as ex:
|
| 131 |
+
# This hack fixes an impedance mismatch between pkgutil and
|
| 132 |
+
# importlib, where the latter raises other errors for cases where
|
| 133 |
+
# pkgutil previously raised ImportError
|
| 134 |
+
msg = "Error while finding module specification for {!r} ({}: {})"
|
| 135 |
+
if mod_name.endswith(".py"):
|
| 136 |
+
msg += (f". Try using '{mod_name[:-3]}' instead of "
|
| 137 |
+
f"'{mod_name}' as the module name.")
|
| 138 |
+
raise error(msg.format(mod_name, type(ex).__name__, ex)) from ex
|
| 139 |
+
if spec is None:
|
| 140 |
+
raise error("No module named %s" % mod_name)
|
| 141 |
+
if spec.submodule_search_locations is not None:
|
| 142 |
+
if mod_name == "__main__" or mod_name.endswith(".__main__"):
|
| 143 |
+
raise error("Cannot use package as __main__ module")
|
| 144 |
+
try:
|
| 145 |
+
pkg_main_name = mod_name + ".__main__"
|
| 146 |
+
return _get_module_details(pkg_main_name, error)
|
| 147 |
+
except error as e:
|
| 148 |
+
if mod_name not in sys.modules:
|
| 149 |
+
raise # No module loaded; being a package is irrelevant
|
| 150 |
+
raise error(("%s; %r is a package and cannot " +
|
| 151 |
+
"be directly executed") %(e, mod_name))
|
| 152 |
+
loader = spec.loader
|
| 153 |
+
if loader is None:
|
| 154 |
+
raise error("%r is a namespace package and cannot be executed"
|
| 155 |
+
% mod_name)
|
| 156 |
+
try:
|
| 157 |
+
code = loader.get_code(mod_name)
|
| 158 |
+
except ImportError as e:
|
| 159 |
+
raise error(format(e)) from e
|
| 160 |
+
if code is None:
|
| 161 |
+
raise error("No code object available for %s" % mod_name)
|
| 162 |
+
return mod_name, spec, code
|
| 163 |
+
|
| 164 |
+
class _Error(Exception):
|
| 165 |
+
"""Error that _run_module_as_main() should report without a traceback"""
|
| 166 |
+
|
| 167 |
+
# XXX ncoghlan: Should this be documented and made public?
|
| 168 |
+
# (Current thoughts: don't repeat the mistake that lead to its
|
| 169 |
+
# creation when run_module() no longer met the needs of
|
| 170 |
+
# mainmodule.c, but couldn't be changed because it was public)
|
| 171 |
+
def _run_module_as_main(mod_name, alter_argv=True):
|
| 172 |
+
"""Runs the designated module in the __main__ namespace
|
| 173 |
+
|
| 174 |
+
Note that the executed module will have full access to the
|
| 175 |
+
__main__ namespace. If this is not desirable, the run_module()
|
| 176 |
+
function should be used to run the module code in a fresh namespace.
|
| 177 |
+
|
| 178 |
+
At the very least, these variables in __main__ will be overwritten:
|
| 179 |
+
__name__
|
| 180 |
+
__file__
|
| 181 |
+
__cached__
|
| 182 |
+
__loader__
|
| 183 |
+
__package__
|
| 184 |
+
"""
|
| 185 |
+
try:
|
| 186 |
+
if alter_argv or mod_name != "__main__": # i.e. -m switch
|
| 187 |
+
mod_name, mod_spec, code = _get_module_details(mod_name, _Error)
|
| 188 |
+
else: # i.e. directory or zipfile execution
|
| 189 |
+
mod_name, mod_spec, code = _get_main_module_details(_Error)
|
| 190 |
+
except _Error as exc:
|
| 191 |
+
msg = "%s: %s" % (sys.executable, exc)
|
| 192 |
+
sys.exit(msg)
|
| 193 |
+
main_globals = sys.modules["__main__"].__dict__
|
| 194 |
+
if alter_argv:
|
| 195 |
+
sys.argv[0] = mod_spec.origin
|
| 196 |
+
return _run_code(code, main_globals, None,
|
| 197 |
+
"__main__", mod_spec)
|
| 198 |
+
|
| 199 |
+
def run_module(mod_name, init_globals=None,
|
| 200 |
+
run_name=None, alter_sys=False):
|
| 201 |
+
"""Execute a module's code without importing it.
|
| 202 |
+
|
| 203 |
+
mod_name -- an absolute module name or package name.
|
| 204 |
+
|
| 205 |
+
Optional arguments:
|
| 206 |
+
init_globals -- dictionary used to pre-populate the module’s
|
| 207 |
+
globals dictionary before the code is executed.
|
| 208 |
+
|
| 209 |
+
run_name -- if not None, this will be used for setting __name__;
|
| 210 |
+
otherwise, __name__ will be set to mod_name + '__main__' if the
|
| 211 |
+
named module is a package and to just mod_name otherwise.
|
| 212 |
+
|
| 213 |
+
alter_sys -- if True, sys.argv[0] is updated with the value of
|
| 214 |
+
__file__ and sys.modules[__name__] is updated with a temporary
|
| 215 |
+
module object for the module being executed. Both are
|
| 216 |
+
restored to their original values before the function returns.
|
| 217 |
+
|
| 218 |
+
Returns the resulting module globals dictionary.
|
| 219 |
+
"""
|
| 220 |
+
mod_name, mod_spec, code = _get_module_details(mod_name)
|
| 221 |
+
if run_name is None:
|
| 222 |
+
run_name = mod_name
|
| 223 |
+
if alter_sys:
|
| 224 |
+
return _run_module_code(code, init_globals, run_name, mod_spec)
|
| 225 |
+
else:
|
| 226 |
+
# Leave the sys module alone
|
| 227 |
+
return _run_code(code, {}, init_globals, run_name, mod_spec)
|
| 228 |
+
|
| 229 |
+
def _get_main_module_details(error=ImportError):
|
| 230 |
+
# Helper that gives a nicer error message when attempting to
|
| 231 |
+
# execute a zipfile or directory by invoking __main__.py
|
| 232 |
+
# Also moves the standard __main__ out of the way so that the
|
| 233 |
+
# preexisting __loader__ entry doesn't cause issues
|
| 234 |
+
main_name = "__main__"
|
| 235 |
+
saved_main = sys.modules[main_name]
|
| 236 |
+
del sys.modules[main_name]
|
| 237 |
+
try:
|
| 238 |
+
return _get_module_details(main_name)
|
| 239 |
+
except ImportError as exc:
|
| 240 |
+
if main_name in str(exc):
|
| 241 |
+
raise error("can't find %r module in %r" %
|
| 242 |
+
(main_name, sys.path[0])) from exc
|
| 243 |
+
raise
|
| 244 |
+
finally:
|
| 245 |
+
sys.modules[main_name] = saved_main
|
| 246 |
+
|
| 247 |
+
|
| 248 |
+
def _get_code_from_file(run_name, fname):
|
| 249 |
+
# Check for a compiled file first
|
| 250 |
+
from pkgutil import read_code
|
| 251 |
+
decoded_path = os.path.abspath(os.fsdecode(fname))
|
| 252 |
+
with io.open_code(decoded_path) as f:
|
| 253 |
+
code = read_code(f)
|
| 254 |
+
if code is None:
|
| 255 |
+
# That didn't work, so try it as normal source code
|
| 256 |
+
with io.open_code(decoded_path) as f:
|
| 257 |
+
code = compile(f.read(), fname, 'exec')
|
| 258 |
+
return code, fname
|
| 259 |
+
|
| 260 |
+
def run_path(path_name, init_globals=None, run_name=None):
|
| 261 |
+
"""Execute code located at the specified filesystem location.
|
| 262 |
+
|
| 263 |
+
path_name -- filesystem location of a Python script, zipfile,
|
| 264 |
+
or directory containing a top level __main__.py script.
|
| 265 |
+
|
| 266 |
+
Optional arguments:
|
| 267 |
+
init_globals -- dictionary used to pre-populate the module’s
|
| 268 |
+
globals dictionary before the code is executed.
|
| 269 |
+
|
| 270 |
+
run_name -- if not None, this will be used to set __name__;
|
| 271 |
+
otherwise, '<run_path>' will be used for __name__.
|
| 272 |
+
|
| 273 |
+
Returns the resulting module globals dictionary.
|
| 274 |
+
"""
|
| 275 |
+
if run_name is None:
|
| 276 |
+
run_name = "<run_path>"
|
| 277 |
+
pkg_name = run_name.rpartition(".")[0]
|
| 278 |
+
from pkgutil import get_importer
|
| 279 |
+
importer = get_importer(path_name)
|
| 280 |
+
# Trying to avoid importing imp so as to not consume the deprecation warning.
|
| 281 |
+
is_NullImporter = False
|
| 282 |
+
if type(importer).__module__ == 'imp':
|
| 283 |
+
if type(importer).__name__ == 'NullImporter':
|
| 284 |
+
is_NullImporter = True
|
| 285 |
+
if isinstance(importer, type(None)) or is_NullImporter:
|
| 286 |
+
# Not a valid sys.path entry, so run the code directly
|
| 287 |
+
# execfile() doesn't help as we want to allow compiled files
|
| 288 |
+
code, fname = _get_code_from_file(run_name, path_name)
|
| 289 |
+
return _run_module_code(code, init_globals, run_name,
|
| 290 |
+
pkg_name=pkg_name, script_name=fname)
|
| 291 |
+
else:
|
| 292 |
+
# Finder is defined for path, so add it to
|
| 293 |
+
# the start of sys.path
|
| 294 |
+
sys.path.insert(0, path_name)
|
| 295 |
+
try:
|
| 296 |
+
# Here's where things are a little different from the run_module
|
| 297 |
+
# case. There, we only had to replace the module in sys while the
|
| 298 |
+
# code was running and doing so was somewhat optional. Here, we
|
| 299 |
+
# have no choice and we have to remove it even while we read the
|
| 300 |
+
# code. If we don't do this, a __loader__ attribute in the
|
| 301 |
+
# existing __main__ module may prevent location of the new module.
|
| 302 |
+
mod_name, mod_spec, code = _get_main_module_details()
|
| 303 |
+
with _TempModule(run_name) as temp_module, \
|
| 304 |
+
_ModifiedArgv0(path_name):
|
| 305 |
+
mod_globals = temp_module.module.__dict__
|
| 306 |
+
return _run_code(code, mod_globals, init_globals,
|
| 307 |
+
run_name, mod_spec, pkg_name).copy()
|
| 308 |
+
finally:
|
| 309 |
+
try:
|
| 310 |
+
sys.path.remove(path_name)
|
| 311 |
+
except ValueError:
|
| 312 |
+
pass
|
| 313 |
+
|
| 314 |
+
|
| 315 |
+
if __name__ == "__main__":
|
| 316 |
+
# Run the module specified as the next command line argument
|
| 317 |
+
if len(sys.argv) < 2:
|
| 318 |
+
print("No module specified for execution", file=sys.stderr)
|
| 319 |
+
else:
|
| 320 |
+
del sys.argv[0] # Make the requested module sys.argv[0]
|
| 321 |
+
_run_module_as_main(sys.argv[0])
|
lib/python3.10/sched.py
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""A generally useful event scheduler class.
|
| 2 |
+
|
| 3 |
+
Each instance of this class manages its own queue.
|
| 4 |
+
No multi-threading is implied; you are supposed to hack that
|
| 5 |
+
yourself, or use a single instance per application.
|
| 6 |
+
|
| 7 |
+
Each instance is parametrized with two functions, one that is
|
| 8 |
+
supposed to return the current time, one that is supposed to
|
| 9 |
+
implement a delay. You can implement real-time scheduling by
|
| 10 |
+
substituting time and sleep from built-in module time, or you can
|
| 11 |
+
implement simulated time by writing your own functions. This can
|
| 12 |
+
also be used to integrate scheduling with STDWIN events; the delay
|
| 13 |
+
function is allowed to modify the queue. Time can be expressed as
|
| 14 |
+
integers or floating point numbers, as long as it is consistent.
|
| 15 |
+
|
| 16 |
+
Events are specified by tuples (time, priority, action, argument, kwargs).
|
| 17 |
+
As in UNIX, lower priority numbers mean higher priority; in this
|
| 18 |
+
way the queue can be maintained as a priority queue. Execution of the
|
| 19 |
+
event means calling the action function, passing it the argument
|
| 20 |
+
sequence in "argument" (remember that in Python, multiple function
|
| 21 |
+
arguments are be packed in a sequence) and keyword parameters in "kwargs".
|
| 22 |
+
The action function may be an instance method so it
|
| 23 |
+
has another way to reference private data (besides global variables).
|
| 24 |
+
"""
|
| 25 |
+
|
| 26 |
+
import time
|
| 27 |
+
import heapq
|
| 28 |
+
from collections import namedtuple
|
| 29 |
+
from itertools import count
|
| 30 |
+
import threading
|
| 31 |
+
from time import monotonic as _time
|
| 32 |
+
|
| 33 |
+
__all__ = ["scheduler"]
|
| 34 |
+
|
| 35 |
+
Event = namedtuple('Event', 'time, priority, sequence, action, argument, kwargs')
|
| 36 |
+
Event.time.__doc__ = ('''Numeric type compatible with the return value of the
|
| 37 |
+
timefunc function passed to the constructor.''')
|
| 38 |
+
Event.priority.__doc__ = ('''Events scheduled for the same time will be executed
|
| 39 |
+
in the order of their priority.''')
|
| 40 |
+
Event.sequence.__doc__ = ('''A continually increasing sequence number that
|
| 41 |
+
separates events if time and priority are equal.''')
|
| 42 |
+
Event.action.__doc__ = ('''Executing the event means executing
|
| 43 |
+
action(*argument, **kwargs)''')
|
| 44 |
+
Event.argument.__doc__ = ('''argument is a sequence holding the positional
|
| 45 |
+
arguments for the action.''')
|
| 46 |
+
Event.kwargs.__doc__ = ('''kwargs is a dictionary holding the keyword
|
| 47 |
+
arguments for the action.''')
|
| 48 |
+
|
| 49 |
+
_sentinel = object()
|
| 50 |
+
|
| 51 |
+
class scheduler:
|
| 52 |
+
|
| 53 |
+
def __init__(self, timefunc=_time, delayfunc=time.sleep):
|
| 54 |
+
"""Initialize a new instance, passing the time and delay
|
| 55 |
+
functions"""
|
| 56 |
+
self._queue = []
|
| 57 |
+
self._lock = threading.RLock()
|
| 58 |
+
self.timefunc = timefunc
|
| 59 |
+
self.delayfunc = delayfunc
|
| 60 |
+
self._sequence_generator = count()
|
| 61 |
+
|
| 62 |
+
def enterabs(self, time, priority, action, argument=(), kwargs=_sentinel):
|
| 63 |
+
"""Enter a new event in the queue at an absolute time.
|
| 64 |
+
|
| 65 |
+
Returns an ID for the event which can be used to remove it,
|
| 66 |
+
if necessary.
|
| 67 |
+
|
| 68 |
+
"""
|
| 69 |
+
if kwargs is _sentinel:
|
| 70 |
+
kwargs = {}
|
| 71 |
+
|
| 72 |
+
with self._lock:
|
| 73 |
+
event = Event(time, priority, next(self._sequence_generator),
|
| 74 |
+
action, argument, kwargs)
|
| 75 |
+
heapq.heappush(self._queue, event)
|
| 76 |
+
return event # The ID
|
| 77 |
+
|
| 78 |
+
def enter(self, delay, priority, action, argument=(), kwargs=_sentinel):
|
| 79 |
+
"""A variant that specifies the time as a relative time.
|
| 80 |
+
|
| 81 |
+
This is actually the more commonly used interface.
|
| 82 |
+
|
| 83 |
+
"""
|
| 84 |
+
time = self.timefunc() + delay
|
| 85 |
+
return self.enterabs(time, priority, action, argument, kwargs)
|
| 86 |
+
|
| 87 |
+
def cancel(self, event):
|
| 88 |
+
"""Remove an event from the queue.
|
| 89 |
+
|
| 90 |
+
This must be presented the ID as returned by enter().
|
| 91 |
+
If the event is not in the queue, this raises ValueError.
|
| 92 |
+
|
| 93 |
+
"""
|
| 94 |
+
with self._lock:
|
| 95 |
+
self._queue.remove(event)
|
| 96 |
+
heapq.heapify(self._queue)
|
| 97 |
+
|
| 98 |
+
def empty(self):
|
| 99 |
+
"""Check whether the queue is empty."""
|
| 100 |
+
with self._lock:
|
| 101 |
+
return not self._queue
|
| 102 |
+
|
| 103 |
+
def run(self, blocking=True):
|
| 104 |
+
"""Execute events until the queue is empty.
|
| 105 |
+
If blocking is False executes the scheduled events due to
|
| 106 |
+
expire soonest (if any) and then return the deadline of the
|
| 107 |
+
next scheduled call in the scheduler.
|
| 108 |
+
|
| 109 |
+
When there is a positive delay until the first event, the
|
| 110 |
+
delay function is called and the event is left in the queue;
|
| 111 |
+
otherwise, the event is removed from the queue and executed
|
| 112 |
+
(its action function is called, passing it the argument). If
|
| 113 |
+
the delay function returns prematurely, it is simply
|
| 114 |
+
restarted.
|
| 115 |
+
|
| 116 |
+
It is legal for both the delay function and the action
|
| 117 |
+
function to modify the queue or to raise an exception;
|
| 118 |
+
exceptions are not caught but the scheduler's state remains
|
| 119 |
+
well-defined so run() may be called again.
|
| 120 |
+
|
| 121 |
+
A questionable hack is added to allow other threads to run:
|
| 122 |
+
just after an event is executed, a delay of 0 is executed, to
|
| 123 |
+
avoid monopolizing the CPU when other threads are also
|
| 124 |
+
runnable.
|
| 125 |
+
|
| 126 |
+
"""
|
| 127 |
+
# localize variable access to minimize overhead
|
| 128 |
+
# and to improve thread safety
|
| 129 |
+
lock = self._lock
|
| 130 |
+
q = self._queue
|
| 131 |
+
delayfunc = self.delayfunc
|
| 132 |
+
timefunc = self.timefunc
|
| 133 |
+
pop = heapq.heappop
|
| 134 |
+
while True:
|
| 135 |
+
with lock:
|
| 136 |
+
if not q:
|
| 137 |
+
break
|
| 138 |
+
(time, priority, sequence, action,
|
| 139 |
+
argument, kwargs) = q[0]
|
| 140 |
+
now = timefunc()
|
| 141 |
+
if time > now:
|
| 142 |
+
delay = True
|
| 143 |
+
else:
|
| 144 |
+
delay = False
|
| 145 |
+
pop(q)
|
| 146 |
+
if delay:
|
| 147 |
+
if not blocking:
|
| 148 |
+
return time - now
|
| 149 |
+
delayfunc(time - now)
|
| 150 |
+
else:
|
| 151 |
+
action(*argument, **kwargs)
|
| 152 |
+
delayfunc(0) # Let other threads run
|
| 153 |
+
|
| 154 |
+
@property
|
| 155 |
+
def queue(self):
|
| 156 |
+
"""An ordered list of upcoming events.
|
| 157 |
+
|
| 158 |
+
Events are named tuples with fields for:
|
| 159 |
+
time, priority, action, arguments, kwargs
|
| 160 |
+
|
| 161 |
+
"""
|
| 162 |
+
# Use heapq to sort the queue rather than using 'sorted(self._queue)'.
|
| 163 |
+
# With heapq, two events scheduled at the same time will show in
|
| 164 |
+
# the actual order they would be retrieved.
|
| 165 |
+
with self._lock:
|
| 166 |
+
events = self._queue[:]
|
| 167 |
+
return list(map(heapq.heappop, [events]*len(events)))
|
lib/python3.10/secrets.py
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Generate cryptographically strong pseudo-random numbers suitable for
|
| 2 |
+
managing secrets such as account authentication, tokens, and similar.
|
| 3 |
+
|
| 4 |
+
See PEP 506 for more information.
|
| 5 |
+
https://www.python.org/dev/peps/pep-0506/
|
| 6 |
+
|
| 7 |
+
"""
|
| 8 |
+
|
| 9 |
+
__all__ = ['choice', 'randbelow', 'randbits', 'SystemRandom',
|
| 10 |
+
'token_bytes', 'token_hex', 'token_urlsafe',
|
| 11 |
+
'compare_digest',
|
| 12 |
+
]
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
import base64
|
| 16 |
+
import binascii
|
| 17 |
+
|
| 18 |
+
from hmac import compare_digest
|
| 19 |
+
from random import SystemRandom
|
| 20 |
+
|
| 21 |
+
_sysrand = SystemRandom()
|
| 22 |
+
|
| 23 |
+
randbits = _sysrand.getrandbits
|
| 24 |
+
choice = _sysrand.choice
|
| 25 |
+
|
| 26 |
+
def randbelow(exclusive_upper_bound):
|
| 27 |
+
"""Return a random int in the range [0, n)."""
|
| 28 |
+
if exclusive_upper_bound <= 0:
|
| 29 |
+
raise ValueError("Upper bound must be positive.")
|
| 30 |
+
return _sysrand._randbelow(exclusive_upper_bound)
|
| 31 |
+
|
| 32 |
+
DEFAULT_ENTROPY = 32 # number of bytes to return by default
|
| 33 |
+
|
| 34 |
+
def token_bytes(nbytes=None):
|
| 35 |
+
"""Return a random byte string containing *nbytes* bytes.
|
| 36 |
+
|
| 37 |
+
If *nbytes* is ``None`` or not supplied, a reasonable
|
| 38 |
+
default is used.
|
| 39 |
+
|
| 40 |
+
>>> token_bytes(16) #doctest:+SKIP
|
| 41 |
+
b'\\xebr\\x17D*t\\xae\\xd4\\xe3S\\xb6\\xe2\\xebP1\\x8b'
|
| 42 |
+
|
| 43 |
+
"""
|
| 44 |
+
if nbytes is None:
|
| 45 |
+
nbytes = DEFAULT_ENTROPY
|
| 46 |
+
return _sysrand.randbytes(nbytes)
|
| 47 |
+
|
| 48 |
+
def token_hex(nbytes=None):
|
| 49 |
+
"""Return a random text string, in hexadecimal.
|
| 50 |
+
|
| 51 |
+
The string has *nbytes* random bytes, each byte converted to two
|
| 52 |
+
hex digits. If *nbytes* is ``None`` or not supplied, a reasonable
|
| 53 |
+
default is used.
|
| 54 |
+
|
| 55 |
+
>>> token_hex(16) #doctest:+SKIP
|
| 56 |
+
'f9bf78b9a18ce6d46a0cd2b0b86df9da'
|
| 57 |
+
|
| 58 |
+
"""
|
| 59 |
+
return binascii.hexlify(token_bytes(nbytes)).decode('ascii')
|
| 60 |
+
|
| 61 |
+
def token_urlsafe(nbytes=None):
|
| 62 |
+
"""Return a random URL-safe text string, in Base64 encoding.
|
| 63 |
+
|
| 64 |
+
The string has *nbytes* random bytes. If *nbytes* is ``None``
|
| 65 |
+
or not supplied, a reasonable default is used.
|
| 66 |
+
|
| 67 |
+
>>> token_urlsafe(16) #doctest:+SKIP
|
| 68 |
+
'Drmhze6EPcv0fN_81Bj-nA'
|
| 69 |
+
|
| 70 |
+
"""
|
| 71 |
+
tok = token_bytes(nbytes)
|
| 72 |
+
return base64.urlsafe_b64encode(tok).rstrip(b'=').decode('ascii')
|
lib/python3.10/selectors.py
ADDED
|
@@ -0,0 +1,619 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Selectors module.
|
| 2 |
+
|
| 3 |
+
This module allows high-level and efficient I/O multiplexing, built upon the
|
| 4 |
+
`select` module primitives.
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
from abc import ABCMeta, abstractmethod
|
| 9 |
+
from collections import namedtuple
|
| 10 |
+
from collections.abc import Mapping
|
| 11 |
+
import math
|
| 12 |
+
import select
|
| 13 |
+
import sys
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
# generic events, that must be mapped to implementation-specific ones
|
| 17 |
+
EVENT_READ = (1 << 0)
|
| 18 |
+
EVENT_WRITE = (1 << 1)
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def _fileobj_to_fd(fileobj):
|
| 22 |
+
"""Return a file descriptor from a file object.
|
| 23 |
+
|
| 24 |
+
Parameters:
|
| 25 |
+
fileobj -- file object or file descriptor
|
| 26 |
+
|
| 27 |
+
Returns:
|
| 28 |
+
corresponding file descriptor
|
| 29 |
+
|
| 30 |
+
Raises:
|
| 31 |
+
ValueError if the object is invalid
|
| 32 |
+
"""
|
| 33 |
+
if isinstance(fileobj, int):
|
| 34 |
+
fd = fileobj
|
| 35 |
+
else:
|
| 36 |
+
try:
|
| 37 |
+
fd = int(fileobj.fileno())
|
| 38 |
+
except (AttributeError, TypeError, ValueError):
|
| 39 |
+
raise ValueError("Invalid file object: "
|
| 40 |
+
"{!r}".format(fileobj)) from None
|
| 41 |
+
if fd < 0:
|
| 42 |
+
raise ValueError("Invalid file descriptor: {}".format(fd))
|
| 43 |
+
return fd
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
SelectorKey = namedtuple('SelectorKey', ['fileobj', 'fd', 'events', 'data'])
|
| 47 |
+
|
| 48 |
+
SelectorKey.__doc__ = """SelectorKey(fileobj, fd, events, data)
|
| 49 |
+
|
| 50 |
+
Object used to associate a file object to its backing
|
| 51 |
+
file descriptor, selected event mask, and attached data.
|
| 52 |
+
"""
|
| 53 |
+
if sys.version_info >= (3, 5):
|
| 54 |
+
SelectorKey.fileobj.__doc__ = 'File object registered.'
|
| 55 |
+
SelectorKey.fd.__doc__ = 'Underlying file descriptor.'
|
| 56 |
+
SelectorKey.events.__doc__ = 'Events that must be waited for on this file object.'
|
| 57 |
+
SelectorKey.data.__doc__ = ('''Optional opaque data associated to this file object.
|
| 58 |
+
For example, this could be used to store a per-client session ID.''')
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
class _SelectorMapping(Mapping):
|
| 62 |
+
"""Mapping of file objects to selector keys."""
|
| 63 |
+
|
| 64 |
+
def __init__(self, selector):
|
| 65 |
+
self._selector = selector
|
| 66 |
+
|
| 67 |
+
def __len__(self):
|
| 68 |
+
return len(self._selector._fd_to_key)
|
| 69 |
+
|
| 70 |
+
def __getitem__(self, fileobj):
|
| 71 |
+
try:
|
| 72 |
+
fd = self._selector._fileobj_lookup(fileobj)
|
| 73 |
+
return self._selector._fd_to_key[fd]
|
| 74 |
+
except KeyError:
|
| 75 |
+
raise KeyError("{!r} is not registered".format(fileobj)) from None
|
| 76 |
+
|
| 77 |
+
def __iter__(self):
|
| 78 |
+
return iter(self._selector._fd_to_key)
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
class BaseSelector(metaclass=ABCMeta):
|
| 82 |
+
"""Selector abstract base class.
|
| 83 |
+
|
| 84 |
+
A selector supports registering file objects to be monitored for specific
|
| 85 |
+
I/O events.
|
| 86 |
+
|
| 87 |
+
A file object is a file descriptor or any object with a `fileno()` method.
|
| 88 |
+
An arbitrary object can be attached to the file object, which can be used
|
| 89 |
+
for example to store context information, a callback, etc.
|
| 90 |
+
|
| 91 |
+
A selector can use various implementations (select(), poll(), epoll()...)
|
| 92 |
+
depending on the platform. The default `Selector` class uses the most
|
| 93 |
+
efficient implementation on the current platform.
|
| 94 |
+
"""
|
| 95 |
+
|
| 96 |
+
@abstractmethod
|
| 97 |
+
def register(self, fileobj, events, data=None):
|
| 98 |
+
"""Register a file object.
|
| 99 |
+
|
| 100 |
+
Parameters:
|
| 101 |
+
fileobj -- file object or file descriptor
|
| 102 |
+
events -- events to monitor (bitwise mask of EVENT_READ|EVENT_WRITE)
|
| 103 |
+
data -- attached data
|
| 104 |
+
|
| 105 |
+
Returns:
|
| 106 |
+
SelectorKey instance
|
| 107 |
+
|
| 108 |
+
Raises:
|
| 109 |
+
ValueError if events is invalid
|
| 110 |
+
KeyError if fileobj is already registered
|
| 111 |
+
OSError if fileobj is closed or otherwise is unacceptable to
|
| 112 |
+
the underlying system call (if a system call is made)
|
| 113 |
+
|
| 114 |
+
Note:
|
| 115 |
+
OSError may or may not be raised
|
| 116 |
+
"""
|
| 117 |
+
raise NotImplementedError
|
| 118 |
+
|
| 119 |
+
@abstractmethod
|
| 120 |
+
def unregister(self, fileobj):
|
| 121 |
+
"""Unregister a file object.
|
| 122 |
+
|
| 123 |
+
Parameters:
|
| 124 |
+
fileobj -- file object or file descriptor
|
| 125 |
+
|
| 126 |
+
Returns:
|
| 127 |
+
SelectorKey instance
|
| 128 |
+
|
| 129 |
+
Raises:
|
| 130 |
+
KeyError if fileobj is not registered
|
| 131 |
+
|
| 132 |
+
Note:
|
| 133 |
+
If fileobj is registered but has since been closed this does
|
| 134 |
+
*not* raise OSError (even if the wrapped syscall does)
|
| 135 |
+
"""
|
| 136 |
+
raise NotImplementedError
|
| 137 |
+
|
| 138 |
+
def modify(self, fileobj, events, data=None):
|
| 139 |
+
"""Change a registered file object monitored events or attached data.
|
| 140 |
+
|
| 141 |
+
Parameters:
|
| 142 |
+
fileobj -- file object or file descriptor
|
| 143 |
+
events -- events to monitor (bitwise mask of EVENT_READ|EVENT_WRITE)
|
| 144 |
+
data -- attached data
|
| 145 |
+
|
| 146 |
+
Returns:
|
| 147 |
+
SelectorKey instance
|
| 148 |
+
|
| 149 |
+
Raises:
|
| 150 |
+
Anything that unregister() or register() raises
|
| 151 |
+
"""
|
| 152 |
+
self.unregister(fileobj)
|
| 153 |
+
return self.register(fileobj, events, data)
|
| 154 |
+
|
| 155 |
+
@abstractmethod
|
| 156 |
+
def select(self, timeout=None):
|
| 157 |
+
"""Perform the actual selection, until some monitored file objects are
|
| 158 |
+
ready or a timeout expires.
|
| 159 |
+
|
| 160 |
+
Parameters:
|
| 161 |
+
timeout -- if timeout > 0, this specifies the maximum wait time, in
|
| 162 |
+
seconds
|
| 163 |
+
if timeout <= 0, the select() call won't block, and will
|
| 164 |
+
report the currently ready file objects
|
| 165 |
+
if timeout is None, select() will block until a monitored
|
| 166 |
+
file object becomes ready
|
| 167 |
+
|
| 168 |
+
Returns:
|
| 169 |
+
list of (key, events) for ready file objects
|
| 170 |
+
`events` is a bitwise mask of EVENT_READ|EVENT_WRITE
|
| 171 |
+
"""
|
| 172 |
+
raise NotImplementedError
|
| 173 |
+
|
| 174 |
+
def close(self):
|
| 175 |
+
"""Close the selector.
|
| 176 |
+
|
| 177 |
+
This must be called to make sure that any underlying resource is freed.
|
| 178 |
+
"""
|
| 179 |
+
pass
|
| 180 |
+
|
| 181 |
+
def get_key(self, fileobj):
|
| 182 |
+
"""Return the key associated to a registered file object.
|
| 183 |
+
|
| 184 |
+
Returns:
|
| 185 |
+
SelectorKey for this file object
|
| 186 |
+
"""
|
| 187 |
+
mapping = self.get_map()
|
| 188 |
+
if mapping is None:
|
| 189 |
+
raise RuntimeError('Selector is closed')
|
| 190 |
+
try:
|
| 191 |
+
return mapping[fileobj]
|
| 192 |
+
except KeyError:
|
| 193 |
+
raise KeyError("{!r} is not registered".format(fileobj)) from None
|
| 194 |
+
|
| 195 |
+
@abstractmethod
|
| 196 |
+
def get_map(self):
|
| 197 |
+
"""Return a mapping of file objects to selector keys."""
|
| 198 |
+
raise NotImplementedError
|
| 199 |
+
|
| 200 |
+
def __enter__(self):
|
| 201 |
+
return self
|
| 202 |
+
|
| 203 |
+
def __exit__(self, *args):
|
| 204 |
+
self.close()
|
| 205 |
+
|
| 206 |
+
|
| 207 |
+
class _BaseSelectorImpl(BaseSelector):
|
| 208 |
+
"""Base selector implementation."""
|
| 209 |
+
|
| 210 |
+
def __init__(self):
|
| 211 |
+
# this maps file descriptors to keys
|
| 212 |
+
self._fd_to_key = {}
|
| 213 |
+
# read-only mapping returned by get_map()
|
| 214 |
+
self._map = _SelectorMapping(self)
|
| 215 |
+
|
| 216 |
+
def _fileobj_lookup(self, fileobj):
|
| 217 |
+
"""Return a file descriptor from a file object.
|
| 218 |
+
|
| 219 |
+
This wraps _fileobj_to_fd() to do an exhaustive search in case
|
| 220 |
+
the object is invalid but we still have it in our map. This
|
| 221 |
+
is used by unregister() so we can unregister an object that
|
| 222 |
+
was previously registered even if it is closed. It is also
|
| 223 |
+
used by _SelectorMapping.
|
| 224 |
+
"""
|
| 225 |
+
try:
|
| 226 |
+
return _fileobj_to_fd(fileobj)
|
| 227 |
+
except ValueError:
|
| 228 |
+
# Do an exhaustive search.
|
| 229 |
+
for key in self._fd_to_key.values():
|
| 230 |
+
if key.fileobj is fileobj:
|
| 231 |
+
return key.fd
|
| 232 |
+
# Raise ValueError after all.
|
| 233 |
+
raise
|
| 234 |
+
|
| 235 |
+
def register(self, fileobj, events, data=None):
|
| 236 |
+
if (not events) or (events & ~(EVENT_READ | EVENT_WRITE)):
|
| 237 |
+
raise ValueError("Invalid events: {!r}".format(events))
|
| 238 |
+
|
| 239 |
+
key = SelectorKey(fileobj, self._fileobj_lookup(fileobj), events, data)
|
| 240 |
+
|
| 241 |
+
if key.fd in self._fd_to_key:
|
| 242 |
+
raise KeyError("{!r} (FD {}) is already registered"
|
| 243 |
+
.format(fileobj, key.fd))
|
| 244 |
+
|
| 245 |
+
self._fd_to_key[key.fd] = key
|
| 246 |
+
return key
|
| 247 |
+
|
| 248 |
+
def unregister(self, fileobj):
|
| 249 |
+
try:
|
| 250 |
+
key = self._fd_to_key.pop(self._fileobj_lookup(fileobj))
|
| 251 |
+
except KeyError:
|
| 252 |
+
raise KeyError("{!r} is not registered".format(fileobj)) from None
|
| 253 |
+
return key
|
| 254 |
+
|
| 255 |
+
def modify(self, fileobj, events, data=None):
|
| 256 |
+
try:
|
| 257 |
+
key = self._fd_to_key[self._fileobj_lookup(fileobj)]
|
| 258 |
+
except KeyError:
|
| 259 |
+
raise KeyError("{!r} is not registered".format(fileobj)) from None
|
| 260 |
+
if events != key.events:
|
| 261 |
+
self.unregister(fileobj)
|
| 262 |
+
key = self.register(fileobj, events, data)
|
| 263 |
+
elif data != key.data:
|
| 264 |
+
# Use a shortcut to update the data.
|
| 265 |
+
key = key._replace(data=data)
|
| 266 |
+
self._fd_to_key[key.fd] = key
|
| 267 |
+
return key
|
| 268 |
+
|
| 269 |
+
def close(self):
|
| 270 |
+
self._fd_to_key.clear()
|
| 271 |
+
self._map = None
|
| 272 |
+
|
| 273 |
+
def get_map(self):
|
| 274 |
+
return self._map
|
| 275 |
+
|
| 276 |
+
def _key_from_fd(self, fd):
|
| 277 |
+
"""Return the key associated to a given file descriptor.
|
| 278 |
+
|
| 279 |
+
Parameters:
|
| 280 |
+
fd -- file descriptor
|
| 281 |
+
|
| 282 |
+
Returns:
|
| 283 |
+
corresponding key, or None if not found
|
| 284 |
+
"""
|
| 285 |
+
try:
|
| 286 |
+
return self._fd_to_key[fd]
|
| 287 |
+
except KeyError:
|
| 288 |
+
return None
|
| 289 |
+
|
| 290 |
+
|
| 291 |
+
class SelectSelector(_BaseSelectorImpl):
|
| 292 |
+
"""Select-based selector."""
|
| 293 |
+
|
| 294 |
+
def __init__(self):
|
| 295 |
+
super().__init__()
|
| 296 |
+
self._readers = set()
|
| 297 |
+
self._writers = set()
|
| 298 |
+
|
| 299 |
+
def register(self, fileobj, events, data=None):
|
| 300 |
+
key = super().register(fileobj, events, data)
|
| 301 |
+
if events & EVENT_READ:
|
| 302 |
+
self._readers.add(key.fd)
|
| 303 |
+
if events & EVENT_WRITE:
|
| 304 |
+
self._writers.add(key.fd)
|
| 305 |
+
return key
|
| 306 |
+
|
| 307 |
+
def unregister(self, fileobj):
|
| 308 |
+
key = super().unregister(fileobj)
|
| 309 |
+
self._readers.discard(key.fd)
|
| 310 |
+
self._writers.discard(key.fd)
|
| 311 |
+
return key
|
| 312 |
+
|
| 313 |
+
if sys.platform == 'win32':
|
| 314 |
+
def _select(self, r, w, _, timeout=None):
|
| 315 |
+
r, w, x = select.select(r, w, w, timeout)
|
| 316 |
+
return r, w + x, []
|
| 317 |
+
else:
|
| 318 |
+
_select = select.select
|
| 319 |
+
|
| 320 |
+
def select(self, timeout=None):
|
| 321 |
+
timeout = None if timeout is None else max(timeout, 0)
|
| 322 |
+
ready = []
|
| 323 |
+
try:
|
| 324 |
+
r, w, _ = self._select(self._readers, self._writers, [], timeout)
|
| 325 |
+
except InterruptedError:
|
| 326 |
+
return ready
|
| 327 |
+
r = set(r)
|
| 328 |
+
w = set(w)
|
| 329 |
+
for fd in r | w:
|
| 330 |
+
events = 0
|
| 331 |
+
if fd in r:
|
| 332 |
+
events |= EVENT_READ
|
| 333 |
+
if fd in w:
|
| 334 |
+
events |= EVENT_WRITE
|
| 335 |
+
|
| 336 |
+
key = self._key_from_fd(fd)
|
| 337 |
+
if key:
|
| 338 |
+
ready.append((key, events & key.events))
|
| 339 |
+
return ready
|
| 340 |
+
|
| 341 |
+
|
| 342 |
+
class _PollLikeSelector(_BaseSelectorImpl):
|
| 343 |
+
"""Base class shared between poll, epoll and devpoll selectors."""
|
| 344 |
+
_selector_cls = None
|
| 345 |
+
_EVENT_READ = None
|
| 346 |
+
_EVENT_WRITE = None
|
| 347 |
+
|
| 348 |
+
def __init__(self):
|
| 349 |
+
super().__init__()
|
| 350 |
+
self._selector = self._selector_cls()
|
| 351 |
+
|
| 352 |
+
def register(self, fileobj, events, data=None):
|
| 353 |
+
key = super().register(fileobj, events, data)
|
| 354 |
+
poller_events = 0
|
| 355 |
+
if events & EVENT_READ:
|
| 356 |
+
poller_events |= self._EVENT_READ
|
| 357 |
+
if events & EVENT_WRITE:
|
| 358 |
+
poller_events |= self._EVENT_WRITE
|
| 359 |
+
try:
|
| 360 |
+
self._selector.register(key.fd, poller_events)
|
| 361 |
+
except:
|
| 362 |
+
super().unregister(fileobj)
|
| 363 |
+
raise
|
| 364 |
+
return key
|
| 365 |
+
|
| 366 |
+
def unregister(self, fileobj):
|
| 367 |
+
key = super().unregister(fileobj)
|
| 368 |
+
try:
|
| 369 |
+
self._selector.unregister(key.fd)
|
| 370 |
+
except OSError:
|
| 371 |
+
# This can happen if the FD was closed since it
|
| 372 |
+
# was registered.
|
| 373 |
+
pass
|
| 374 |
+
return key
|
| 375 |
+
|
| 376 |
+
def modify(self, fileobj, events, data=None):
|
| 377 |
+
try:
|
| 378 |
+
key = self._fd_to_key[self._fileobj_lookup(fileobj)]
|
| 379 |
+
except KeyError:
|
| 380 |
+
raise KeyError(f"{fileobj!r} is not registered") from None
|
| 381 |
+
|
| 382 |
+
changed = False
|
| 383 |
+
if events != key.events:
|
| 384 |
+
selector_events = 0
|
| 385 |
+
if events & EVENT_READ:
|
| 386 |
+
selector_events |= self._EVENT_READ
|
| 387 |
+
if events & EVENT_WRITE:
|
| 388 |
+
selector_events |= self._EVENT_WRITE
|
| 389 |
+
try:
|
| 390 |
+
self._selector.modify(key.fd, selector_events)
|
| 391 |
+
except:
|
| 392 |
+
super().unregister(fileobj)
|
| 393 |
+
raise
|
| 394 |
+
changed = True
|
| 395 |
+
if data != key.data:
|
| 396 |
+
changed = True
|
| 397 |
+
|
| 398 |
+
if changed:
|
| 399 |
+
key = key._replace(events=events, data=data)
|
| 400 |
+
self._fd_to_key[key.fd] = key
|
| 401 |
+
return key
|
| 402 |
+
|
| 403 |
+
def select(self, timeout=None):
|
| 404 |
+
# This is shared between poll() and epoll().
|
| 405 |
+
# epoll() has a different signature and handling of timeout parameter.
|
| 406 |
+
if timeout is None:
|
| 407 |
+
timeout = None
|
| 408 |
+
elif timeout <= 0:
|
| 409 |
+
timeout = 0
|
| 410 |
+
else:
|
| 411 |
+
# poll() has a resolution of 1 millisecond, round away from
|
| 412 |
+
# zero to wait *at least* timeout seconds.
|
| 413 |
+
timeout = math.ceil(timeout * 1e3)
|
| 414 |
+
ready = []
|
| 415 |
+
try:
|
| 416 |
+
fd_event_list = self._selector.poll(timeout)
|
| 417 |
+
except InterruptedError:
|
| 418 |
+
return ready
|
| 419 |
+
for fd, event in fd_event_list:
|
| 420 |
+
events = 0
|
| 421 |
+
if event & ~self._EVENT_READ:
|
| 422 |
+
events |= EVENT_WRITE
|
| 423 |
+
if event & ~self._EVENT_WRITE:
|
| 424 |
+
events |= EVENT_READ
|
| 425 |
+
|
| 426 |
+
key = self._key_from_fd(fd)
|
| 427 |
+
if key:
|
| 428 |
+
ready.append((key, events & key.events))
|
| 429 |
+
return ready
|
| 430 |
+
|
| 431 |
+
|
| 432 |
+
if hasattr(select, 'poll'):
|
| 433 |
+
|
| 434 |
+
class PollSelector(_PollLikeSelector):
|
| 435 |
+
"""Poll-based selector."""
|
| 436 |
+
_selector_cls = select.poll
|
| 437 |
+
_EVENT_READ = select.POLLIN
|
| 438 |
+
_EVENT_WRITE = select.POLLOUT
|
| 439 |
+
|
| 440 |
+
|
| 441 |
+
if hasattr(select, 'epoll'):
|
| 442 |
+
|
| 443 |
+
class EpollSelector(_PollLikeSelector):
|
| 444 |
+
"""Epoll-based selector."""
|
| 445 |
+
_selector_cls = select.epoll
|
| 446 |
+
_EVENT_READ = select.EPOLLIN
|
| 447 |
+
_EVENT_WRITE = select.EPOLLOUT
|
| 448 |
+
|
| 449 |
+
def fileno(self):
|
| 450 |
+
return self._selector.fileno()
|
| 451 |
+
|
| 452 |
+
def select(self, timeout=None):
|
| 453 |
+
if timeout is None:
|
| 454 |
+
timeout = -1
|
| 455 |
+
elif timeout <= 0:
|
| 456 |
+
timeout = 0
|
| 457 |
+
else:
|
| 458 |
+
# epoll_wait() has a resolution of 1 millisecond, round away
|
| 459 |
+
# from zero to wait *at least* timeout seconds.
|
| 460 |
+
timeout = math.ceil(timeout * 1e3) * 1e-3
|
| 461 |
+
|
| 462 |
+
# epoll_wait() expects `maxevents` to be greater than zero;
|
| 463 |
+
# we want to make sure that `select()` can be called when no
|
| 464 |
+
# FD is registered.
|
| 465 |
+
max_ev = max(len(self._fd_to_key), 1)
|
| 466 |
+
|
| 467 |
+
ready = []
|
| 468 |
+
try:
|
| 469 |
+
fd_event_list = self._selector.poll(timeout, max_ev)
|
| 470 |
+
except InterruptedError:
|
| 471 |
+
return ready
|
| 472 |
+
for fd, event in fd_event_list:
|
| 473 |
+
events = 0
|
| 474 |
+
if event & ~select.EPOLLIN:
|
| 475 |
+
events |= EVENT_WRITE
|
| 476 |
+
if event & ~select.EPOLLOUT:
|
| 477 |
+
events |= EVENT_READ
|
| 478 |
+
|
| 479 |
+
key = self._key_from_fd(fd)
|
| 480 |
+
if key:
|
| 481 |
+
ready.append((key, events & key.events))
|
| 482 |
+
return ready
|
| 483 |
+
|
| 484 |
+
def close(self):
|
| 485 |
+
self._selector.close()
|
| 486 |
+
super().close()
|
| 487 |
+
|
| 488 |
+
|
| 489 |
+
if hasattr(select, 'devpoll'):
|
| 490 |
+
|
| 491 |
+
class DevpollSelector(_PollLikeSelector):
|
| 492 |
+
"""Solaris /dev/poll selector."""
|
| 493 |
+
_selector_cls = select.devpoll
|
| 494 |
+
_EVENT_READ = select.POLLIN
|
| 495 |
+
_EVENT_WRITE = select.POLLOUT
|
| 496 |
+
|
| 497 |
+
def fileno(self):
|
| 498 |
+
return self._selector.fileno()
|
| 499 |
+
|
| 500 |
+
def close(self):
|
| 501 |
+
self._selector.close()
|
| 502 |
+
super().close()
|
| 503 |
+
|
| 504 |
+
|
| 505 |
+
if hasattr(select, 'kqueue'):
|
| 506 |
+
|
| 507 |
+
class KqueueSelector(_BaseSelectorImpl):
|
| 508 |
+
"""Kqueue-based selector."""
|
| 509 |
+
|
| 510 |
+
def __init__(self):
|
| 511 |
+
super().__init__()
|
| 512 |
+
self._selector = select.kqueue()
|
| 513 |
+
|
| 514 |
+
def fileno(self):
|
| 515 |
+
return self._selector.fileno()
|
| 516 |
+
|
| 517 |
+
def register(self, fileobj, events, data=None):
|
| 518 |
+
key = super().register(fileobj, events, data)
|
| 519 |
+
try:
|
| 520 |
+
if events & EVENT_READ:
|
| 521 |
+
kev = select.kevent(key.fd, select.KQ_FILTER_READ,
|
| 522 |
+
select.KQ_EV_ADD)
|
| 523 |
+
self._selector.control([kev], 0, 0)
|
| 524 |
+
if events & EVENT_WRITE:
|
| 525 |
+
kev = select.kevent(key.fd, select.KQ_FILTER_WRITE,
|
| 526 |
+
select.KQ_EV_ADD)
|
| 527 |
+
self._selector.control([kev], 0, 0)
|
| 528 |
+
except:
|
| 529 |
+
super().unregister(fileobj)
|
| 530 |
+
raise
|
| 531 |
+
return key
|
| 532 |
+
|
| 533 |
+
def unregister(self, fileobj):
|
| 534 |
+
key = super().unregister(fileobj)
|
| 535 |
+
if key.events & EVENT_READ:
|
| 536 |
+
kev = select.kevent(key.fd, select.KQ_FILTER_READ,
|
| 537 |
+
select.KQ_EV_DELETE)
|
| 538 |
+
try:
|
| 539 |
+
self._selector.control([kev], 0, 0)
|
| 540 |
+
except OSError:
|
| 541 |
+
# This can happen if the FD was closed since it
|
| 542 |
+
# was registered.
|
| 543 |
+
pass
|
| 544 |
+
if key.events & EVENT_WRITE:
|
| 545 |
+
kev = select.kevent(key.fd, select.KQ_FILTER_WRITE,
|
| 546 |
+
select.KQ_EV_DELETE)
|
| 547 |
+
try:
|
| 548 |
+
self._selector.control([kev], 0, 0)
|
| 549 |
+
except OSError:
|
| 550 |
+
# See comment above.
|
| 551 |
+
pass
|
| 552 |
+
return key
|
| 553 |
+
|
| 554 |
+
def select(self, timeout=None):
|
| 555 |
+
timeout = None if timeout is None else max(timeout, 0)
|
| 556 |
+
# If max_ev is 0, kqueue will ignore the timeout. For consistent
|
| 557 |
+
# behavior with the other selector classes, we prevent that here
|
| 558 |
+
# (using max). See https://bugs.python.org/issue29255
|
| 559 |
+
max_ev = max(len(self._fd_to_key), 1)
|
| 560 |
+
ready = []
|
| 561 |
+
try:
|
| 562 |
+
kev_list = self._selector.control(None, max_ev, timeout)
|
| 563 |
+
except InterruptedError:
|
| 564 |
+
return ready
|
| 565 |
+
for kev in kev_list:
|
| 566 |
+
fd = kev.ident
|
| 567 |
+
flag = kev.filter
|
| 568 |
+
events = 0
|
| 569 |
+
if flag == select.KQ_FILTER_READ:
|
| 570 |
+
events |= EVENT_READ
|
| 571 |
+
if flag == select.KQ_FILTER_WRITE:
|
| 572 |
+
events |= EVENT_WRITE
|
| 573 |
+
|
| 574 |
+
key = self._key_from_fd(fd)
|
| 575 |
+
if key:
|
| 576 |
+
ready.append((key, events & key.events))
|
| 577 |
+
return ready
|
| 578 |
+
|
| 579 |
+
def close(self):
|
| 580 |
+
self._selector.close()
|
| 581 |
+
super().close()
|
| 582 |
+
|
| 583 |
+
|
| 584 |
+
def _can_use(method):
|
| 585 |
+
"""Check if we can use the selector depending upon the
|
| 586 |
+
operating system. """
|
| 587 |
+
# Implementation based upon https://github.com/sethmlarson/selectors2/blob/master/selectors2.py
|
| 588 |
+
selector = getattr(select, method, None)
|
| 589 |
+
if selector is None:
|
| 590 |
+
# select module does not implement method
|
| 591 |
+
return False
|
| 592 |
+
# check if the OS and Kernel actually support the method. Call may fail with
|
| 593 |
+
# OSError: [Errno 38] Function not implemented
|
| 594 |
+
try:
|
| 595 |
+
selector_obj = selector()
|
| 596 |
+
if method == 'poll':
|
| 597 |
+
# check that poll actually works
|
| 598 |
+
selector_obj.poll(0)
|
| 599 |
+
else:
|
| 600 |
+
# close epoll, kqueue, and devpoll fd
|
| 601 |
+
selector_obj.close()
|
| 602 |
+
return True
|
| 603 |
+
except OSError:
|
| 604 |
+
return False
|
| 605 |
+
|
| 606 |
+
|
| 607 |
+
# Choose the best implementation, roughly:
|
| 608 |
+
# epoll|kqueue|devpoll > poll > select.
|
| 609 |
+
# select() also can't accept a FD > FD_SETSIZE (usually around 1024)
|
| 610 |
+
if _can_use('kqueue'):
|
| 611 |
+
DefaultSelector = KqueueSelector
|
| 612 |
+
elif _can_use('epoll'):
|
| 613 |
+
DefaultSelector = EpollSelector
|
| 614 |
+
elif _can_use('devpoll'):
|
| 615 |
+
DefaultSelector = DevpollSelector
|
| 616 |
+
elif _can_use('poll'):
|
| 617 |
+
DefaultSelector = PollSelector
|
| 618 |
+
else:
|
| 619 |
+
DefaultSelector = SelectSelector
|
lib/python3.10/shelve.py
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Manage shelves of pickled objects.
|
| 2 |
+
|
| 3 |
+
A "shelf" is a persistent, dictionary-like object. The difference
|
| 4 |
+
with dbm databases is that the values (not the keys!) in a shelf can
|
| 5 |
+
be essentially arbitrary Python objects -- anything that the "pickle"
|
| 6 |
+
module can handle. This includes most class instances, recursive data
|
| 7 |
+
types, and objects containing lots of shared sub-objects. The keys
|
| 8 |
+
are ordinary strings.
|
| 9 |
+
|
| 10 |
+
To summarize the interface (key is a string, data is an arbitrary
|
| 11 |
+
object):
|
| 12 |
+
|
| 13 |
+
import shelve
|
| 14 |
+
d = shelve.open(filename) # open, with (g)dbm filename -- no suffix
|
| 15 |
+
|
| 16 |
+
d[key] = data # store data at key (overwrites old data if
|
| 17 |
+
# using an existing key)
|
| 18 |
+
data = d[key] # retrieve a COPY of the data at key (raise
|
| 19 |
+
# KeyError if no such key) -- NOTE that this
|
| 20 |
+
# access returns a *copy* of the entry!
|
| 21 |
+
del d[key] # delete data stored at key (raises KeyError
|
| 22 |
+
# if no such key)
|
| 23 |
+
flag = key in d # true if the key exists
|
| 24 |
+
list = d.keys() # a list of all existing keys (slow!)
|
| 25 |
+
|
| 26 |
+
d.close() # close it
|
| 27 |
+
|
| 28 |
+
Dependent on the implementation, closing a persistent dictionary may
|
| 29 |
+
or may not be necessary to flush changes to disk.
|
| 30 |
+
|
| 31 |
+
Normally, d[key] returns a COPY of the entry. This needs care when
|
| 32 |
+
mutable entries are mutated: for example, if d[key] is a list,
|
| 33 |
+
d[key].append(anitem)
|
| 34 |
+
does NOT modify the entry d[key] itself, as stored in the persistent
|
| 35 |
+
mapping -- it only modifies the copy, which is then immediately
|
| 36 |
+
discarded, so that the append has NO effect whatsoever. To append an
|
| 37 |
+
item to d[key] in a way that will affect the persistent mapping, use:
|
| 38 |
+
data = d[key]
|
| 39 |
+
data.append(anitem)
|
| 40 |
+
d[key] = data
|
| 41 |
+
|
| 42 |
+
To avoid the problem with mutable entries, you may pass the keyword
|
| 43 |
+
argument writeback=True in the call to shelve.open. When you use:
|
| 44 |
+
d = shelve.open(filename, writeback=True)
|
| 45 |
+
then d keeps a cache of all entries you access, and writes them all back
|
| 46 |
+
to the persistent mapping when you call d.close(). This ensures that
|
| 47 |
+
such usage as d[key].append(anitem) works as intended.
|
| 48 |
+
|
| 49 |
+
However, using keyword argument writeback=True may consume vast amount
|
| 50 |
+
of memory for the cache, and it may make d.close() very slow, if you
|
| 51 |
+
access many of d's entries after opening it in this way: d has no way to
|
| 52 |
+
check which of the entries you access are mutable and/or which ones you
|
| 53 |
+
actually mutate, so it must cache, and write back at close, all of the
|
| 54 |
+
entries that you access. You can call d.sync() to write back all the
|
| 55 |
+
entries in the cache, and empty the cache (d.sync() also synchronizes
|
| 56 |
+
the persistent dictionary on disk, if feasible).
|
| 57 |
+
"""
|
| 58 |
+
|
| 59 |
+
from pickle import DEFAULT_PROTOCOL, Pickler, Unpickler
|
| 60 |
+
from io import BytesIO
|
| 61 |
+
|
| 62 |
+
import collections.abc
|
| 63 |
+
|
| 64 |
+
__all__ = ["Shelf", "BsdDbShelf", "DbfilenameShelf", "open"]
|
| 65 |
+
|
| 66 |
+
class _ClosedDict(collections.abc.MutableMapping):
|
| 67 |
+
'Marker for a closed dict. Access attempts raise a ValueError.'
|
| 68 |
+
|
| 69 |
+
def closed(self, *args):
|
| 70 |
+
raise ValueError('invalid operation on closed shelf')
|
| 71 |
+
__iter__ = __len__ = __getitem__ = __setitem__ = __delitem__ = keys = closed
|
| 72 |
+
|
| 73 |
+
def __repr__(self):
|
| 74 |
+
return '<Closed Dictionary>'
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
class Shelf(collections.abc.MutableMapping):
|
| 78 |
+
"""Base class for shelf implementations.
|
| 79 |
+
|
| 80 |
+
This is initialized with a dictionary-like object.
|
| 81 |
+
See the module's __doc__ string for an overview of the interface.
|
| 82 |
+
"""
|
| 83 |
+
|
| 84 |
+
def __init__(self, dict, protocol=None, writeback=False,
|
| 85 |
+
keyencoding="utf-8"):
|
| 86 |
+
self.dict = dict
|
| 87 |
+
if protocol is None:
|
| 88 |
+
protocol = DEFAULT_PROTOCOL
|
| 89 |
+
self._protocol = protocol
|
| 90 |
+
self.writeback = writeback
|
| 91 |
+
self.cache = {}
|
| 92 |
+
self.keyencoding = keyencoding
|
| 93 |
+
|
| 94 |
+
def __iter__(self):
|
| 95 |
+
for k in self.dict.keys():
|
| 96 |
+
yield k.decode(self.keyencoding)
|
| 97 |
+
|
| 98 |
+
def __len__(self):
|
| 99 |
+
return len(self.dict)
|
| 100 |
+
|
| 101 |
+
def __contains__(self, key):
|
| 102 |
+
return key.encode(self.keyencoding) in self.dict
|
| 103 |
+
|
| 104 |
+
def get(self, key, default=None):
|
| 105 |
+
if key.encode(self.keyencoding) in self.dict:
|
| 106 |
+
return self[key]
|
| 107 |
+
return default
|
| 108 |
+
|
| 109 |
+
def __getitem__(self, key):
|
| 110 |
+
try:
|
| 111 |
+
value = self.cache[key]
|
| 112 |
+
except KeyError:
|
| 113 |
+
f = BytesIO(self.dict[key.encode(self.keyencoding)])
|
| 114 |
+
value = Unpickler(f).load()
|
| 115 |
+
if self.writeback:
|
| 116 |
+
self.cache[key] = value
|
| 117 |
+
return value
|
| 118 |
+
|
| 119 |
+
def __setitem__(self, key, value):
|
| 120 |
+
if self.writeback:
|
| 121 |
+
self.cache[key] = value
|
| 122 |
+
f = BytesIO()
|
| 123 |
+
p = Pickler(f, self._protocol)
|
| 124 |
+
p.dump(value)
|
| 125 |
+
self.dict[key.encode(self.keyencoding)] = f.getvalue()
|
| 126 |
+
|
| 127 |
+
def __delitem__(self, key):
|
| 128 |
+
del self.dict[key.encode(self.keyencoding)]
|
| 129 |
+
try:
|
| 130 |
+
del self.cache[key]
|
| 131 |
+
except KeyError:
|
| 132 |
+
pass
|
| 133 |
+
|
| 134 |
+
def __enter__(self):
|
| 135 |
+
return self
|
| 136 |
+
|
| 137 |
+
def __exit__(self, type, value, traceback):
|
| 138 |
+
self.close()
|
| 139 |
+
|
| 140 |
+
def close(self):
|
| 141 |
+
if self.dict is None:
|
| 142 |
+
return
|
| 143 |
+
try:
|
| 144 |
+
self.sync()
|
| 145 |
+
try:
|
| 146 |
+
self.dict.close()
|
| 147 |
+
except AttributeError:
|
| 148 |
+
pass
|
| 149 |
+
finally:
|
| 150 |
+
# Catch errors that may happen when close is called from __del__
|
| 151 |
+
# because CPython is in interpreter shutdown.
|
| 152 |
+
try:
|
| 153 |
+
self.dict = _ClosedDict()
|
| 154 |
+
except:
|
| 155 |
+
self.dict = None
|
| 156 |
+
|
| 157 |
+
def __del__(self):
|
| 158 |
+
if not hasattr(self, 'writeback'):
|
| 159 |
+
# __init__ didn't succeed, so don't bother closing
|
| 160 |
+
# see http://bugs.python.org/issue1339007 for details
|
| 161 |
+
return
|
| 162 |
+
self.close()
|
| 163 |
+
|
| 164 |
+
def sync(self):
|
| 165 |
+
if self.writeback and self.cache:
|
| 166 |
+
self.writeback = False
|
| 167 |
+
for key, entry in self.cache.items():
|
| 168 |
+
self[key] = entry
|
| 169 |
+
self.writeback = True
|
| 170 |
+
self.cache = {}
|
| 171 |
+
if hasattr(self.dict, 'sync'):
|
| 172 |
+
self.dict.sync()
|
| 173 |
+
|
| 174 |
+
|
| 175 |
+
class BsdDbShelf(Shelf):
|
| 176 |
+
"""Shelf implementation using the "BSD" db interface.
|
| 177 |
+
|
| 178 |
+
This adds methods first(), next(), previous(), last() and
|
| 179 |
+
set_location() that have no counterpart in [g]dbm databases.
|
| 180 |
+
|
| 181 |
+
The actual database must be opened using one of the "bsddb"
|
| 182 |
+
modules "open" routines (i.e. bsddb.hashopen, bsddb.btopen or
|
| 183 |
+
bsddb.rnopen) and passed to the constructor.
|
| 184 |
+
|
| 185 |
+
See the module's __doc__ string for an overview of the interface.
|
| 186 |
+
"""
|
| 187 |
+
|
| 188 |
+
def __init__(self, dict, protocol=None, writeback=False,
|
| 189 |
+
keyencoding="utf-8"):
|
| 190 |
+
Shelf.__init__(self, dict, protocol, writeback, keyencoding)
|
| 191 |
+
|
| 192 |
+
def set_location(self, key):
|
| 193 |
+
(key, value) = self.dict.set_location(key)
|
| 194 |
+
f = BytesIO(value)
|
| 195 |
+
return (key.decode(self.keyencoding), Unpickler(f).load())
|
| 196 |
+
|
| 197 |
+
def next(self):
|
| 198 |
+
(key, value) = next(self.dict)
|
| 199 |
+
f = BytesIO(value)
|
| 200 |
+
return (key.decode(self.keyencoding), Unpickler(f).load())
|
| 201 |
+
|
| 202 |
+
def previous(self):
|
| 203 |
+
(key, value) = self.dict.previous()
|
| 204 |
+
f = BytesIO(value)
|
| 205 |
+
return (key.decode(self.keyencoding), Unpickler(f).load())
|
| 206 |
+
|
| 207 |
+
def first(self):
|
| 208 |
+
(key, value) = self.dict.first()
|
| 209 |
+
f = BytesIO(value)
|
| 210 |
+
return (key.decode(self.keyencoding), Unpickler(f).load())
|
| 211 |
+
|
| 212 |
+
def last(self):
|
| 213 |
+
(key, value) = self.dict.last()
|
| 214 |
+
f = BytesIO(value)
|
| 215 |
+
return (key.decode(self.keyencoding), Unpickler(f).load())
|
| 216 |
+
|
| 217 |
+
|
| 218 |
+
class DbfilenameShelf(Shelf):
|
| 219 |
+
"""Shelf implementation using the "dbm" generic dbm interface.
|
| 220 |
+
|
| 221 |
+
This is initialized with the filename for the dbm database.
|
| 222 |
+
See the module's __doc__ string for an overview of the interface.
|
| 223 |
+
"""
|
| 224 |
+
|
| 225 |
+
def __init__(self, filename, flag='c', protocol=None, writeback=False):
|
| 226 |
+
import dbm
|
| 227 |
+
Shelf.__init__(self, dbm.open(filename, flag), protocol, writeback)
|
| 228 |
+
|
| 229 |
+
|
| 230 |
+
def open(filename, flag='c', protocol=None, writeback=False):
|
| 231 |
+
"""Open a persistent dictionary for reading and writing.
|
| 232 |
+
|
| 233 |
+
The filename parameter is the base filename for the underlying
|
| 234 |
+
database. As a side-effect, an extension may be added to the
|
| 235 |
+
filename and more than one file may be created. The optional flag
|
| 236 |
+
parameter has the same interpretation as the flag parameter of
|
| 237 |
+
dbm.open(). The optional protocol parameter specifies the
|
| 238 |
+
version of the pickle protocol.
|
| 239 |
+
|
| 240 |
+
See the module's __doc__ string for an overview of the interface.
|
| 241 |
+
"""
|
| 242 |
+
|
| 243 |
+
return DbfilenameShelf(filename, flag, protocol, writeback)
|
lib/python3.10/shlex.py
ADDED
|
@@ -0,0 +1,350 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""A lexical analyzer class for simple shell-like syntaxes."""
|
| 2 |
+
|
| 3 |
+
# Module and documentation by Eric S. Raymond, 21 Dec 1998
|
| 4 |
+
# Input stacking and error message cleanup added by ESR, March 2000
|
| 5 |
+
# push_source() and pop_source() made explicit by ESR, January 2001.
|
| 6 |
+
# Posix compliance, split(), string arguments, and
|
| 7 |
+
# iterator interface by Gustavo Niemeyer, April 2003.
|
| 8 |
+
# changes to tokenize more like Posix shells by Vinay Sajip, July 2016.
|
| 9 |
+
|
| 10 |
+
import os
|
| 11 |
+
import re
|
| 12 |
+
import sys
|
| 13 |
+
from collections import deque
|
| 14 |
+
|
| 15 |
+
from io import StringIO
|
| 16 |
+
|
| 17 |
+
__all__ = ["shlex", "split", "quote", "join"]
|
| 18 |
+
|
| 19 |
+
class shlex:
|
| 20 |
+
"A lexical analyzer class for simple shell-like syntaxes."
|
| 21 |
+
def __init__(self, instream=None, infile=None, posix=False,
|
| 22 |
+
punctuation_chars=False):
|
| 23 |
+
if isinstance(instream, str):
|
| 24 |
+
instream = StringIO(instream)
|
| 25 |
+
if instream is not None:
|
| 26 |
+
self.instream = instream
|
| 27 |
+
self.infile = infile
|
| 28 |
+
else:
|
| 29 |
+
self.instream = sys.stdin
|
| 30 |
+
self.infile = None
|
| 31 |
+
self.posix = posix
|
| 32 |
+
if posix:
|
| 33 |
+
self.eof = None
|
| 34 |
+
else:
|
| 35 |
+
self.eof = ''
|
| 36 |
+
self.commenters = '#'
|
| 37 |
+
self.wordchars = ('abcdfeghijklmnopqrstuvwxyz'
|
| 38 |
+
'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_')
|
| 39 |
+
if self.posix:
|
| 40 |
+
self.wordchars += ('ßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ'
|
| 41 |
+
'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ')
|
| 42 |
+
self.whitespace = ' \t\r\n'
|
| 43 |
+
self.whitespace_split = False
|
| 44 |
+
self.quotes = '\'"'
|
| 45 |
+
self.escape = '\\'
|
| 46 |
+
self.escapedquotes = '"'
|
| 47 |
+
self.state = ' '
|
| 48 |
+
self.pushback = deque()
|
| 49 |
+
self.lineno = 1
|
| 50 |
+
self.debug = 0
|
| 51 |
+
self.token = ''
|
| 52 |
+
self.filestack = deque()
|
| 53 |
+
self.source = None
|
| 54 |
+
if not punctuation_chars:
|
| 55 |
+
punctuation_chars = ''
|
| 56 |
+
elif punctuation_chars is True:
|
| 57 |
+
punctuation_chars = '();<>|&'
|
| 58 |
+
self._punctuation_chars = punctuation_chars
|
| 59 |
+
if punctuation_chars:
|
| 60 |
+
# _pushback_chars is a push back queue used by lookahead logic
|
| 61 |
+
self._pushback_chars = deque()
|
| 62 |
+
# these chars added because allowed in file names, args, wildcards
|
| 63 |
+
self.wordchars += '~-./*?='
|
| 64 |
+
#remove any punctuation chars from wordchars
|
| 65 |
+
t = self.wordchars.maketrans(dict.fromkeys(punctuation_chars))
|
| 66 |
+
self.wordchars = self.wordchars.translate(t)
|
| 67 |
+
|
| 68 |
+
@property
|
| 69 |
+
def punctuation_chars(self):
|
| 70 |
+
return self._punctuation_chars
|
| 71 |
+
|
| 72 |
+
def push_token(self, tok):
|
| 73 |
+
"Push a token onto the stack popped by the get_token method"
|
| 74 |
+
if self.debug >= 1:
|
| 75 |
+
print("shlex: pushing token " + repr(tok))
|
| 76 |
+
self.pushback.appendleft(tok)
|
| 77 |
+
|
| 78 |
+
def push_source(self, newstream, newfile=None):
|
| 79 |
+
"Push an input source onto the lexer's input source stack."
|
| 80 |
+
if isinstance(newstream, str):
|
| 81 |
+
newstream = StringIO(newstream)
|
| 82 |
+
self.filestack.appendleft((self.infile, self.instream, self.lineno))
|
| 83 |
+
self.infile = newfile
|
| 84 |
+
self.instream = newstream
|
| 85 |
+
self.lineno = 1
|
| 86 |
+
if self.debug:
|
| 87 |
+
if newfile is not None:
|
| 88 |
+
print('shlex: pushing to file %s' % (self.infile,))
|
| 89 |
+
else:
|
| 90 |
+
print('shlex: pushing to stream %s' % (self.instream,))
|
| 91 |
+
|
| 92 |
+
def pop_source(self):
|
| 93 |
+
"Pop the input source stack."
|
| 94 |
+
self.instream.close()
|
| 95 |
+
(self.infile, self.instream, self.lineno) = self.filestack.popleft()
|
| 96 |
+
if self.debug:
|
| 97 |
+
print('shlex: popping to %s, line %d' \
|
| 98 |
+
% (self.instream, self.lineno))
|
| 99 |
+
self.state = ' '
|
| 100 |
+
|
| 101 |
+
def get_token(self):
|
| 102 |
+
"Get a token from the input stream (or from stack if it's nonempty)"
|
| 103 |
+
if self.pushback:
|
| 104 |
+
tok = self.pushback.popleft()
|
| 105 |
+
if self.debug >= 1:
|
| 106 |
+
print("shlex: popping token " + repr(tok))
|
| 107 |
+
return tok
|
| 108 |
+
# No pushback. Get a token.
|
| 109 |
+
raw = self.read_token()
|
| 110 |
+
# Handle inclusions
|
| 111 |
+
if self.source is not None:
|
| 112 |
+
while raw == self.source:
|
| 113 |
+
spec = self.sourcehook(self.read_token())
|
| 114 |
+
if spec:
|
| 115 |
+
(newfile, newstream) = spec
|
| 116 |
+
self.push_source(newstream, newfile)
|
| 117 |
+
raw = self.get_token()
|
| 118 |
+
# Maybe we got EOF instead?
|
| 119 |
+
while raw == self.eof:
|
| 120 |
+
if not self.filestack:
|
| 121 |
+
return self.eof
|
| 122 |
+
else:
|
| 123 |
+
self.pop_source()
|
| 124 |
+
raw = self.get_token()
|
| 125 |
+
# Neither inclusion nor EOF
|
| 126 |
+
if self.debug >= 1:
|
| 127 |
+
if raw != self.eof:
|
| 128 |
+
print("shlex: token=" + repr(raw))
|
| 129 |
+
else:
|
| 130 |
+
print("shlex: token=EOF")
|
| 131 |
+
return raw
|
| 132 |
+
|
| 133 |
+
def read_token(self):
|
| 134 |
+
quoted = False
|
| 135 |
+
escapedstate = ' '
|
| 136 |
+
while True:
|
| 137 |
+
if self.punctuation_chars and self._pushback_chars:
|
| 138 |
+
nextchar = self._pushback_chars.pop()
|
| 139 |
+
else:
|
| 140 |
+
nextchar = self.instream.read(1)
|
| 141 |
+
if nextchar == '\n':
|
| 142 |
+
self.lineno += 1
|
| 143 |
+
if self.debug >= 3:
|
| 144 |
+
print("shlex: in state %r I see character: %r" % (self.state,
|
| 145 |
+
nextchar))
|
| 146 |
+
if self.state is None:
|
| 147 |
+
self.token = '' # past end of file
|
| 148 |
+
break
|
| 149 |
+
elif self.state == ' ':
|
| 150 |
+
if not nextchar:
|
| 151 |
+
self.state = None # end of file
|
| 152 |
+
break
|
| 153 |
+
elif nextchar in self.whitespace:
|
| 154 |
+
if self.debug >= 2:
|
| 155 |
+
print("shlex: I see whitespace in whitespace state")
|
| 156 |
+
if self.token or (self.posix and quoted):
|
| 157 |
+
break # emit current token
|
| 158 |
+
else:
|
| 159 |
+
continue
|
| 160 |
+
elif nextchar in self.commenters:
|
| 161 |
+
self.instream.readline()
|
| 162 |
+
self.lineno += 1
|
| 163 |
+
elif self.posix and nextchar in self.escape:
|
| 164 |
+
escapedstate = 'a'
|
| 165 |
+
self.state = nextchar
|
| 166 |
+
elif nextchar in self.wordchars:
|
| 167 |
+
self.token = nextchar
|
| 168 |
+
self.state = 'a'
|
| 169 |
+
elif nextchar in self.punctuation_chars:
|
| 170 |
+
self.token = nextchar
|
| 171 |
+
self.state = 'c'
|
| 172 |
+
elif nextchar in self.quotes:
|
| 173 |
+
if not self.posix:
|
| 174 |
+
self.token = nextchar
|
| 175 |
+
self.state = nextchar
|
| 176 |
+
elif self.whitespace_split:
|
| 177 |
+
self.token = nextchar
|
| 178 |
+
self.state = 'a'
|
| 179 |
+
else:
|
| 180 |
+
self.token = nextchar
|
| 181 |
+
if self.token or (self.posix and quoted):
|
| 182 |
+
break # emit current token
|
| 183 |
+
else:
|
| 184 |
+
continue
|
| 185 |
+
elif self.state in self.quotes:
|
| 186 |
+
quoted = True
|
| 187 |
+
if not nextchar: # end of file
|
| 188 |
+
if self.debug >= 2:
|
| 189 |
+
print("shlex: I see EOF in quotes state")
|
| 190 |
+
# XXX what error should be raised here?
|
| 191 |
+
raise ValueError("No closing quotation")
|
| 192 |
+
if nextchar == self.state:
|
| 193 |
+
if not self.posix:
|
| 194 |
+
self.token += nextchar
|
| 195 |
+
self.state = ' '
|
| 196 |
+
break
|
| 197 |
+
else:
|
| 198 |
+
self.state = 'a'
|
| 199 |
+
elif (self.posix and nextchar in self.escape and self.state
|
| 200 |
+
in self.escapedquotes):
|
| 201 |
+
escapedstate = self.state
|
| 202 |
+
self.state = nextchar
|
| 203 |
+
else:
|
| 204 |
+
self.token += nextchar
|
| 205 |
+
elif self.state in self.escape:
|
| 206 |
+
if not nextchar: # end of file
|
| 207 |
+
if self.debug >= 2:
|
| 208 |
+
print("shlex: I see EOF in escape state")
|
| 209 |
+
# XXX what error should be raised here?
|
| 210 |
+
raise ValueError("No escaped character")
|
| 211 |
+
# In posix shells, only the quote itself or the escape
|
| 212 |
+
# character may be escaped within quotes.
|
| 213 |
+
if (escapedstate in self.quotes and
|
| 214 |
+
nextchar != self.state and nextchar != escapedstate):
|
| 215 |
+
self.token += self.state
|
| 216 |
+
self.token += nextchar
|
| 217 |
+
self.state = escapedstate
|
| 218 |
+
elif self.state in ('a', 'c'):
|
| 219 |
+
if not nextchar:
|
| 220 |
+
self.state = None # end of file
|
| 221 |
+
break
|
| 222 |
+
elif nextchar in self.whitespace:
|
| 223 |
+
if self.debug >= 2:
|
| 224 |
+
print("shlex: I see whitespace in word state")
|
| 225 |
+
self.state = ' '
|
| 226 |
+
if self.token or (self.posix and quoted):
|
| 227 |
+
break # emit current token
|
| 228 |
+
else:
|
| 229 |
+
continue
|
| 230 |
+
elif nextchar in self.commenters:
|
| 231 |
+
self.instream.readline()
|
| 232 |
+
self.lineno += 1
|
| 233 |
+
if self.posix:
|
| 234 |
+
self.state = ' '
|
| 235 |
+
if self.token or (self.posix and quoted):
|
| 236 |
+
break # emit current token
|
| 237 |
+
else:
|
| 238 |
+
continue
|
| 239 |
+
elif self.state == 'c':
|
| 240 |
+
if nextchar in self.punctuation_chars:
|
| 241 |
+
self.token += nextchar
|
| 242 |
+
else:
|
| 243 |
+
if nextchar not in self.whitespace:
|
| 244 |
+
self._pushback_chars.append(nextchar)
|
| 245 |
+
self.state = ' '
|
| 246 |
+
break
|
| 247 |
+
elif self.posix and nextchar in self.quotes:
|
| 248 |
+
self.state = nextchar
|
| 249 |
+
elif self.posix and nextchar in self.escape:
|
| 250 |
+
escapedstate = 'a'
|
| 251 |
+
self.state = nextchar
|
| 252 |
+
elif (nextchar in self.wordchars or nextchar in self.quotes
|
| 253 |
+
or (self.whitespace_split and
|
| 254 |
+
nextchar not in self.punctuation_chars)):
|
| 255 |
+
self.token += nextchar
|
| 256 |
+
else:
|
| 257 |
+
if self.punctuation_chars:
|
| 258 |
+
self._pushback_chars.append(nextchar)
|
| 259 |
+
else:
|
| 260 |
+
self.pushback.appendleft(nextchar)
|
| 261 |
+
if self.debug >= 2:
|
| 262 |
+
print("shlex: I see punctuation in word state")
|
| 263 |
+
self.state = ' '
|
| 264 |
+
if self.token or (self.posix and quoted):
|
| 265 |
+
break # emit current token
|
| 266 |
+
else:
|
| 267 |
+
continue
|
| 268 |
+
result = self.token
|
| 269 |
+
self.token = ''
|
| 270 |
+
if self.posix and not quoted and result == '':
|
| 271 |
+
result = None
|
| 272 |
+
if self.debug > 1:
|
| 273 |
+
if result:
|
| 274 |
+
print("shlex: raw token=" + repr(result))
|
| 275 |
+
else:
|
| 276 |
+
print("shlex: raw token=EOF")
|
| 277 |
+
return result
|
| 278 |
+
|
| 279 |
+
def sourcehook(self, newfile):
|
| 280 |
+
"Hook called on a filename to be sourced."
|
| 281 |
+
if newfile[0] == '"':
|
| 282 |
+
newfile = newfile[1:-1]
|
| 283 |
+
# This implements cpp-like semantics for relative-path inclusion.
|
| 284 |
+
if isinstance(self.infile, str) and not os.path.isabs(newfile):
|
| 285 |
+
newfile = os.path.join(os.path.dirname(self.infile), newfile)
|
| 286 |
+
return (newfile, open(newfile, "r"))
|
| 287 |
+
|
| 288 |
+
def error_leader(self, infile=None, lineno=None):
|
| 289 |
+
"Emit a C-compiler-like, Emacs-friendly error-message leader."
|
| 290 |
+
if infile is None:
|
| 291 |
+
infile = self.infile
|
| 292 |
+
if lineno is None:
|
| 293 |
+
lineno = self.lineno
|
| 294 |
+
return "\"%s\", line %d: " % (infile, lineno)
|
| 295 |
+
|
| 296 |
+
def __iter__(self):
|
| 297 |
+
return self
|
| 298 |
+
|
| 299 |
+
def __next__(self):
|
| 300 |
+
token = self.get_token()
|
| 301 |
+
if token == self.eof:
|
| 302 |
+
raise StopIteration
|
| 303 |
+
return token
|
| 304 |
+
|
| 305 |
+
def split(s, comments=False, posix=True):
|
| 306 |
+
"""Split the string *s* using shell-like syntax."""
|
| 307 |
+
if s is None:
|
| 308 |
+
import warnings
|
| 309 |
+
warnings.warn("Passing None for 's' to shlex.split() is deprecated.",
|
| 310 |
+
DeprecationWarning, stacklevel=2)
|
| 311 |
+
lex = shlex(s, posix=posix)
|
| 312 |
+
lex.whitespace_split = True
|
| 313 |
+
if not comments:
|
| 314 |
+
lex.commenters = ''
|
| 315 |
+
return list(lex)
|
| 316 |
+
|
| 317 |
+
|
| 318 |
+
def join(split_command):
|
| 319 |
+
"""Return a shell-escaped string from *split_command*."""
|
| 320 |
+
return ' '.join(quote(arg) for arg in split_command)
|
| 321 |
+
|
| 322 |
+
|
| 323 |
+
_find_unsafe = re.compile(r'[^\w@%+=:,./-]', re.ASCII).search
|
| 324 |
+
|
| 325 |
+
def quote(s):
|
| 326 |
+
"""Return a shell-escaped version of the string *s*."""
|
| 327 |
+
if not s:
|
| 328 |
+
return "''"
|
| 329 |
+
if _find_unsafe(s) is None:
|
| 330 |
+
return s
|
| 331 |
+
|
| 332 |
+
# use single quotes, and put single quotes into double quotes
|
| 333 |
+
# the string $'b is then quoted as '$'"'"'b'
|
| 334 |
+
return "'" + s.replace("'", "'\"'\"'") + "'"
|
| 335 |
+
|
| 336 |
+
|
| 337 |
+
def _print_tokens(lexer):
|
| 338 |
+
while 1:
|
| 339 |
+
tt = lexer.get_token()
|
| 340 |
+
if not tt:
|
| 341 |
+
break
|
| 342 |
+
print("Token: " + repr(tt))
|
| 343 |
+
|
| 344 |
+
if __name__ == '__main__':
|
| 345 |
+
if len(sys.argv) == 1:
|
| 346 |
+
_print_tokens(shlex())
|
| 347 |
+
else:
|
| 348 |
+
fn = sys.argv[1]
|
| 349 |
+
with open(fn) as f:
|
| 350 |
+
_print_tokens(shlex(f, fn))
|
lib/python3.10/shutil.py
ADDED
|
@@ -0,0 +1,1517 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Utility functions for copying and archiving files and directory trees.
|
| 2 |
+
|
| 3 |
+
XXX The functions here don't copy the resource fork or other metadata on Mac.
|
| 4 |
+
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
import os
|
| 8 |
+
import sys
|
| 9 |
+
import stat
|
| 10 |
+
import fnmatch
|
| 11 |
+
import collections
|
| 12 |
+
import errno
|
| 13 |
+
|
| 14 |
+
try:
|
| 15 |
+
import zlib
|
| 16 |
+
del zlib
|
| 17 |
+
_ZLIB_SUPPORTED = True
|
| 18 |
+
except ImportError:
|
| 19 |
+
_ZLIB_SUPPORTED = False
|
| 20 |
+
|
| 21 |
+
try:
|
| 22 |
+
import bz2
|
| 23 |
+
del bz2
|
| 24 |
+
_BZ2_SUPPORTED = True
|
| 25 |
+
except ImportError:
|
| 26 |
+
_BZ2_SUPPORTED = False
|
| 27 |
+
|
| 28 |
+
try:
|
| 29 |
+
import lzma
|
| 30 |
+
del lzma
|
| 31 |
+
_LZMA_SUPPORTED = True
|
| 32 |
+
except ImportError:
|
| 33 |
+
_LZMA_SUPPORTED = False
|
| 34 |
+
|
| 35 |
+
_WINDOWS = os.name == 'nt'
|
| 36 |
+
posix = nt = None
|
| 37 |
+
if os.name == 'posix':
|
| 38 |
+
import posix
|
| 39 |
+
elif _WINDOWS:
|
| 40 |
+
import nt
|
| 41 |
+
|
| 42 |
+
COPY_BUFSIZE = 1024 * 1024 if _WINDOWS else 64 * 1024
|
| 43 |
+
_USE_CP_SENDFILE = hasattr(os, "sendfile") and sys.platform.startswith("linux")
|
| 44 |
+
_HAS_FCOPYFILE = posix and hasattr(posix, "_fcopyfile") # macOS
|
| 45 |
+
|
| 46 |
+
# CMD defaults in Windows 10
|
| 47 |
+
_WIN_DEFAULT_PATHEXT = ".COM;.EXE;.BAT;.CMD;.VBS;.JS;.WS;.MSC"
|
| 48 |
+
|
| 49 |
+
__all__ = ["copyfileobj", "copyfile", "copymode", "copystat", "copy", "copy2",
|
| 50 |
+
"copytree", "move", "rmtree", "Error", "SpecialFileError",
|
| 51 |
+
"ExecError", "make_archive", "get_archive_formats",
|
| 52 |
+
"register_archive_format", "unregister_archive_format",
|
| 53 |
+
"get_unpack_formats", "register_unpack_format",
|
| 54 |
+
"unregister_unpack_format", "unpack_archive",
|
| 55 |
+
"ignore_patterns", "chown", "which", "get_terminal_size",
|
| 56 |
+
"SameFileError"]
|
| 57 |
+
# disk_usage is added later, if available on the platform
|
| 58 |
+
|
| 59 |
+
class Error(OSError):
|
| 60 |
+
pass
|
| 61 |
+
|
| 62 |
+
class SameFileError(Error):
|
| 63 |
+
"""Raised when source and destination are the same file."""
|
| 64 |
+
|
| 65 |
+
class SpecialFileError(OSError):
|
| 66 |
+
"""Raised when trying to do a kind of operation (e.g. copying) which is
|
| 67 |
+
not supported on a special file (e.g. a named pipe)"""
|
| 68 |
+
|
| 69 |
+
class ExecError(OSError):
|
| 70 |
+
"""Raised when a command could not be executed"""
|
| 71 |
+
|
| 72 |
+
class ReadError(OSError):
|
| 73 |
+
"""Raised when an archive cannot be read"""
|
| 74 |
+
|
| 75 |
+
class RegistryError(Exception):
|
| 76 |
+
"""Raised when a registry operation with the archiving
|
| 77 |
+
and unpacking registries fails"""
|
| 78 |
+
|
| 79 |
+
class _GiveupOnFastCopy(Exception):
|
| 80 |
+
"""Raised as a signal to fallback on using raw read()/write()
|
| 81 |
+
file copy when fast-copy functions fail to do so.
|
| 82 |
+
"""
|
| 83 |
+
|
| 84 |
+
def _fastcopy_fcopyfile(fsrc, fdst, flags):
|
| 85 |
+
"""Copy a regular file content or metadata by using high-performance
|
| 86 |
+
fcopyfile(3) syscall (macOS).
|
| 87 |
+
"""
|
| 88 |
+
try:
|
| 89 |
+
infd = fsrc.fileno()
|
| 90 |
+
outfd = fdst.fileno()
|
| 91 |
+
except Exception as err:
|
| 92 |
+
raise _GiveupOnFastCopy(err) # not a regular file
|
| 93 |
+
|
| 94 |
+
try:
|
| 95 |
+
posix._fcopyfile(infd, outfd, flags)
|
| 96 |
+
except OSError as err:
|
| 97 |
+
err.filename = fsrc.name
|
| 98 |
+
err.filename2 = fdst.name
|
| 99 |
+
if err.errno in {errno.EINVAL, errno.ENOTSUP}:
|
| 100 |
+
raise _GiveupOnFastCopy(err)
|
| 101 |
+
else:
|
| 102 |
+
raise err from None
|
| 103 |
+
|
| 104 |
+
def _fastcopy_sendfile(fsrc, fdst):
|
| 105 |
+
"""Copy data from one regular mmap-like fd to another by using
|
| 106 |
+
high-performance sendfile(2) syscall.
|
| 107 |
+
This should work on Linux >= 2.6.33 only.
|
| 108 |
+
"""
|
| 109 |
+
# Note: copyfileobj() is left alone in order to not introduce any
|
| 110 |
+
# unexpected breakage. Possible risks by using zero-copy calls
|
| 111 |
+
# in copyfileobj() are:
|
| 112 |
+
# - fdst cannot be open in "a"(ppend) mode
|
| 113 |
+
# - fsrc and fdst may be open in "t"(ext) mode
|
| 114 |
+
# - fsrc may be a BufferedReader (which hides unread data in a buffer),
|
| 115 |
+
# GzipFile (which decompresses data), HTTPResponse (which decodes
|
| 116 |
+
# chunks).
|
| 117 |
+
# - possibly others (e.g. encrypted fs/partition?)
|
| 118 |
+
global _USE_CP_SENDFILE
|
| 119 |
+
try:
|
| 120 |
+
infd = fsrc.fileno()
|
| 121 |
+
outfd = fdst.fileno()
|
| 122 |
+
except Exception as err:
|
| 123 |
+
raise _GiveupOnFastCopy(err) # not a regular file
|
| 124 |
+
|
| 125 |
+
# Hopefully the whole file will be copied in a single call.
|
| 126 |
+
# sendfile() is called in a loop 'till EOF is reached (0 return)
|
| 127 |
+
# so a bufsize smaller or bigger than the actual file size
|
| 128 |
+
# should not make any difference, also in case the file content
|
| 129 |
+
# changes while being copied.
|
| 130 |
+
try:
|
| 131 |
+
blocksize = max(os.fstat(infd).st_size, 2 ** 23) # min 8MiB
|
| 132 |
+
except OSError:
|
| 133 |
+
blocksize = 2 ** 27 # 128MiB
|
| 134 |
+
# On 32-bit architectures truncate to 1GiB to avoid OverflowError,
|
| 135 |
+
# see bpo-38319.
|
| 136 |
+
if sys.maxsize < 2 ** 32:
|
| 137 |
+
blocksize = min(blocksize, 2 ** 30)
|
| 138 |
+
|
| 139 |
+
offset = 0
|
| 140 |
+
while True:
|
| 141 |
+
try:
|
| 142 |
+
sent = os.sendfile(outfd, infd, offset, blocksize)
|
| 143 |
+
except OSError as err:
|
| 144 |
+
# ...in oder to have a more informative exception.
|
| 145 |
+
err.filename = fsrc.name
|
| 146 |
+
err.filename2 = fdst.name
|
| 147 |
+
|
| 148 |
+
if err.errno == errno.ENOTSOCK:
|
| 149 |
+
# sendfile() on this platform (probably Linux < 2.6.33)
|
| 150 |
+
# does not support copies between regular files (only
|
| 151 |
+
# sockets).
|
| 152 |
+
_USE_CP_SENDFILE = False
|
| 153 |
+
raise _GiveupOnFastCopy(err)
|
| 154 |
+
|
| 155 |
+
if err.errno == errno.ENOSPC: # filesystem is full
|
| 156 |
+
raise err from None
|
| 157 |
+
|
| 158 |
+
# Give up on first call and if no data was copied.
|
| 159 |
+
if offset == 0 and os.lseek(outfd, 0, os.SEEK_CUR) == 0:
|
| 160 |
+
raise _GiveupOnFastCopy(err)
|
| 161 |
+
|
| 162 |
+
raise err
|
| 163 |
+
else:
|
| 164 |
+
if sent == 0:
|
| 165 |
+
break # EOF
|
| 166 |
+
offset += sent
|
| 167 |
+
|
| 168 |
+
def _copyfileobj_readinto(fsrc, fdst, length=COPY_BUFSIZE):
|
| 169 |
+
"""readinto()/memoryview() based variant of copyfileobj().
|
| 170 |
+
*fsrc* must support readinto() method and both files must be
|
| 171 |
+
open in binary mode.
|
| 172 |
+
"""
|
| 173 |
+
# Localize variable access to minimize overhead.
|
| 174 |
+
fsrc_readinto = fsrc.readinto
|
| 175 |
+
fdst_write = fdst.write
|
| 176 |
+
with memoryview(bytearray(length)) as mv:
|
| 177 |
+
while True:
|
| 178 |
+
n = fsrc_readinto(mv)
|
| 179 |
+
if not n:
|
| 180 |
+
break
|
| 181 |
+
elif n < length:
|
| 182 |
+
with mv[:n] as smv:
|
| 183 |
+
fdst.write(smv)
|
| 184 |
+
else:
|
| 185 |
+
fdst_write(mv)
|
| 186 |
+
|
| 187 |
+
def copyfileobj(fsrc, fdst, length=0):
|
| 188 |
+
"""copy data from file-like object fsrc to file-like object fdst"""
|
| 189 |
+
# Localize variable access to minimize overhead.
|
| 190 |
+
if not length:
|
| 191 |
+
length = COPY_BUFSIZE
|
| 192 |
+
fsrc_read = fsrc.read
|
| 193 |
+
fdst_write = fdst.write
|
| 194 |
+
while True:
|
| 195 |
+
buf = fsrc_read(length)
|
| 196 |
+
if not buf:
|
| 197 |
+
break
|
| 198 |
+
fdst_write(buf)
|
| 199 |
+
|
| 200 |
+
def _samefile(src, dst):
|
| 201 |
+
# Macintosh, Unix.
|
| 202 |
+
if isinstance(src, os.DirEntry) and hasattr(os.path, 'samestat'):
|
| 203 |
+
try:
|
| 204 |
+
return os.path.samestat(src.stat(), os.stat(dst))
|
| 205 |
+
except OSError:
|
| 206 |
+
return False
|
| 207 |
+
|
| 208 |
+
if hasattr(os.path, 'samefile'):
|
| 209 |
+
try:
|
| 210 |
+
return os.path.samefile(src, dst)
|
| 211 |
+
except OSError:
|
| 212 |
+
return False
|
| 213 |
+
|
| 214 |
+
# All other platforms: check for same pathname.
|
| 215 |
+
return (os.path.normcase(os.path.abspath(src)) ==
|
| 216 |
+
os.path.normcase(os.path.abspath(dst)))
|
| 217 |
+
|
| 218 |
+
def _stat(fn):
|
| 219 |
+
return fn.stat() if isinstance(fn, os.DirEntry) else os.stat(fn)
|
| 220 |
+
|
| 221 |
+
def _islink(fn):
|
| 222 |
+
return fn.is_symlink() if isinstance(fn, os.DirEntry) else os.path.islink(fn)
|
| 223 |
+
|
| 224 |
+
def copyfile(src, dst, *, follow_symlinks=True):
|
| 225 |
+
"""Copy data from src to dst in the most efficient way possible.
|
| 226 |
+
|
| 227 |
+
If follow_symlinks is not set and src is a symbolic link, a new
|
| 228 |
+
symlink will be created instead of copying the file it points to.
|
| 229 |
+
|
| 230 |
+
"""
|
| 231 |
+
sys.audit("shutil.copyfile", src, dst)
|
| 232 |
+
|
| 233 |
+
if _samefile(src, dst):
|
| 234 |
+
raise SameFileError("{!r} and {!r} are the same file".format(src, dst))
|
| 235 |
+
|
| 236 |
+
file_size = 0
|
| 237 |
+
for i, fn in enumerate([src, dst]):
|
| 238 |
+
try:
|
| 239 |
+
st = _stat(fn)
|
| 240 |
+
except OSError:
|
| 241 |
+
# File most likely does not exist
|
| 242 |
+
pass
|
| 243 |
+
else:
|
| 244 |
+
# XXX What about other special files? (sockets, devices...)
|
| 245 |
+
if stat.S_ISFIFO(st.st_mode):
|
| 246 |
+
fn = fn.path if isinstance(fn, os.DirEntry) else fn
|
| 247 |
+
raise SpecialFileError("`%s` is a named pipe" % fn)
|
| 248 |
+
if _WINDOWS and i == 0:
|
| 249 |
+
file_size = st.st_size
|
| 250 |
+
|
| 251 |
+
if not follow_symlinks and _islink(src):
|
| 252 |
+
os.symlink(os.readlink(src), dst)
|
| 253 |
+
else:
|
| 254 |
+
with open(src, 'rb') as fsrc:
|
| 255 |
+
try:
|
| 256 |
+
with open(dst, 'wb') as fdst:
|
| 257 |
+
# macOS
|
| 258 |
+
if _HAS_FCOPYFILE:
|
| 259 |
+
try:
|
| 260 |
+
_fastcopy_fcopyfile(fsrc, fdst, posix._COPYFILE_DATA)
|
| 261 |
+
return dst
|
| 262 |
+
except _GiveupOnFastCopy:
|
| 263 |
+
pass
|
| 264 |
+
# Linux
|
| 265 |
+
elif _USE_CP_SENDFILE:
|
| 266 |
+
try:
|
| 267 |
+
_fastcopy_sendfile(fsrc, fdst)
|
| 268 |
+
return dst
|
| 269 |
+
except _GiveupOnFastCopy:
|
| 270 |
+
pass
|
| 271 |
+
# Windows, see:
|
| 272 |
+
# https://github.com/python/cpython/pull/7160#discussion_r195405230
|
| 273 |
+
elif _WINDOWS and file_size > 0:
|
| 274 |
+
_copyfileobj_readinto(fsrc, fdst, min(file_size, COPY_BUFSIZE))
|
| 275 |
+
return dst
|
| 276 |
+
|
| 277 |
+
copyfileobj(fsrc, fdst)
|
| 278 |
+
|
| 279 |
+
# Issue 43219, raise a less confusing exception
|
| 280 |
+
except IsADirectoryError as e:
|
| 281 |
+
if not os.path.exists(dst):
|
| 282 |
+
raise FileNotFoundError(f'Directory does not exist: {dst}') from e
|
| 283 |
+
else:
|
| 284 |
+
raise
|
| 285 |
+
|
| 286 |
+
return dst
|
| 287 |
+
|
| 288 |
+
def copymode(src, dst, *, follow_symlinks=True):
|
| 289 |
+
"""Copy mode bits from src to dst.
|
| 290 |
+
|
| 291 |
+
If follow_symlinks is not set, symlinks aren't followed if and only
|
| 292 |
+
if both `src` and `dst` are symlinks. If `lchmod` isn't available
|
| 293 |
+
(e.g. Linux) this method does nothing.
|
| 294 |
+
|
| 295 |
+
"""
|
| 296 |
+
sys.audit("shutil.copymode", src, dst)
|
| 297 |
+
|
| 298 |
+
if not follow_symlinks and _islink(src) and os.path.islink(dst):
|
| 299 |
+
if hasattr(os, 'lchmod'):
|
| 300 |
+
stat_func, chmod_func = os.lstat, os.lchmod
|
| 301 |
+
else:
|
| 302 |
+
return
|
| 303 |
+
else:
|
| 304 |
+
stat_func, chmod_func = _stat, os.chmod
|
| 305 |
+
|
| 306 |
+
st = stat_func(src)
|
| 307 |
+
chmod_func(dst, stat.S_IMODE(st.st_mode))
|
| 308 |
+
|
| 309 |
+
if hasattr(os, 'listxattr'):
|
| 310 |
+
def _copyxattr(src, dst, *, follow_symlinks=True):
|
| 311 |
+
"""Copy extended filesystem attributes from `src` to `dst`.
|
| 312 |
+
|
| 313 |
+
Overwrite existing attributes.
|
| 314 |
+
|
| 315 |
+
If `follow_symlinks` is false, symlinks won't be followed.
|
| 316 |
+
|
| 317 |
+
"""
|
| 318 |
+
|
| 319 |
+
try:
|
| 320 |
+
names = os.listxattr(src, follow_symlinks=follow_symlinks)
|
| 321 |
+
except OSError as e:
|
| 322 |
+
if e.errno not in (errno.ENOTSUP, errno.ENODATA, errno.EINVAL):
|
| 323 |
+
raise
|
| 324 |
+
return
|
| 325 |
+
for name in names:
|
| 326 |
+
try:
|
| 327 |
+
value = os.getxattr(src, name, follow_symlinks=follow_symlinks)
|
| 328 |
+
os.setxattr(dst, name, value, follow_symlinks=follow_symlinks)
|
| 329 |
+
except OSError as e:
|
| 330 |
+
if e.errno not in (errno.EPERM, errno.ENOTSUP, errno.ENODATA,
|
| 331 |
+
errno.EINVAL):
|
| 332 |
+
raise
|
| 333 |
+
else:
|
| 334 |
+
def _copyxattr(*args, **kwargs):
|
| 335 |
+
pass
|
| 336 |
+
|
| 337 |
+
def copystat(src, dst, *, follow_symlinks=True):
|
| 338 |
+
"""Copy file metadata
|
| 339 |
+
|
| 340 |
+
Copy the permission bits, last access time, last modification time, and
|
| 341 |
+
flags from `src` to `dst`. On Linux, copystat() also copies the "extended
|
| 342 |
+
attributes" where possible. The file contents, owner, and group are
|
| 343 |
+
unaffected. `src` and `dst` are path-like objects or path names given as
|
| 344 |
+
strings.
|
| 345 |
+
|
| 346 |
+
If the optional flag `follow_symlinks` is not set, symlinks aren't
|
| 347 |
+
followed if and only if both `src` and `dst` are symlinks.
|
| 348 |
+
"""
|
| 349 |
+
sys.audit("shutil.copystat", src, dst)
|
| 350 |
+
|
| 351 |
+
def _nop(*args, ns=None, follow_symlinks=None):
|
| 352 |
+
pass
|
| 353 |
+
|
| 354 |
+
# follow symlinks (aka don't not follow symlinks)
|
| 355 |
+
follow = follow_symlinks or not (_islink(src) and os.path.islink(dst))
|
| 356 |
+
if follow:
|
| 357 |
+
# use the real function if it exists
|
| 358 |
+
def lookup(name):
|
| 359 |
+
return getattr(os, name, _nop)
|
| 360 |
+
else:
|
| 361 |
+
# use the real function only if it exists
|
| 362 |
+
# *and* it supports follow_symlinks
|
| 363 |
+
def lookup(name):
|
| 364 |
+
fn = getattr(os, name, _nop)
|
| 365 |
+
if fn in os.supports_follow_symlinks:
|
| 366 |
+
return fn
|
| 367 |
+
return _nop
|
| 368 |
+
|
| 369 |
+
if isinstance(src, os.DirEntry):
|
| 370 |
+
st = src.stat(follow_symlinks=follow)
|
| 371 |
+
else:
|
| 372 |
+
st = lookup("stat")(src, follow_symlinks=follow)
|
| 373 |
+
mode = stat.S_IMODE(st.st_mode)
|
| 374 |
+
lookup("utime")(dst, ns=(st.st_atime_ns, st.st_mtime_ns),
|
| 375 |
+
follow_symlinks=follow)
|
| 376 |
+
# We must copy extended attributes before the file is (potentially)
|
| 377 |
+
# chmod()'ed read-only, otherwise setxattr() will error with -EACCES.
|
| 378 |
+
_copyxattr(src, dst, follow_symlinks=follow)
|
| 379 |
+
try:
|
| 380 |
+
lookup("chmod")(dst, mode, follow_symlinks=follow)
|
| 381 |
+
except NotImplementedError:
|
| 382 |
+
# if we got a NotImplementedError, it's because
|
| 383 |
+
# * follow_symlinks=False,
|
| 384 |
+
# * lchown() is unavailable, and
|
| 385 |
+
# * either
|
| 386 |
+
# * fchownat() is unavailable or
|
| 387 |
+
# * fchownat() doesn't implement AT_SYMLINK_NOFOLLOW.
|
| 388 |
+
# (it returned ENOSUP.)
|
| 389 |
+
# therefore we're out of options--we simply cannot chown the
|
| 390 |
+
# symlink. give up, suppress the error.
|
| 391 |
+
# (which is what shutil always did in this circumstance.)
|
| 392 |
+
pass
|
| 393 |
+
if hasattr(st, 'st_flags'):
|
| 394 |
+
try:
|
| 395 |
+
lookup("chflags")(dst, st.st_flags, follow_symlinks=follow)
|
| 396 |
+
except OSError as why:
|
| 397 |
+
for err in 'EOPNOTSUPP', 'ENOTSUP':
|
| 398 |
+
if hasattr(errno, err) and why.errno == getattr(errno, err):
|
| 399 |
+
break
|
| 400 |
+
else:
|
| 401 |
+
raise
|
| 402 |
+
|
| 403 |
+
def copy(src, dst, *, follow_symlinks=True):
|
| 404 |
+
"""Copy data and mode bits ("cp src dst"). Return the file's destination.
|
| 405 |
+
|
| 406 |
+
The destination may be a directory.
|
| 407 |
+
|
| 408 |
+
If follow_symlinks is false, symlinks won't be followed. This
|
| 409 |
+
resembles GNU's "cp -P src dst".
|
| 410 |
+
|
| 411 |
+
If source and destination are the same file, a SameFileError will be
|
| 412 |
+
raised.
|
| 413 |
+
|
| 414 |
+
"""
|
| 415 |
+
if os.path.isdir(dst):
|
| 416 |
+
dst = os.path.join(dst, os.path.basename(src))
|
| 417 |
+
copyfile(src, dst, follow_symlinks=follow_symlinks)
|
| 418 |
+
copymode(src, dst, follow_symlinks=follow_symlinks)
|
| 419 |
+
return dst
|
| 420 |
+
|
| 421 |
+
def copy2(src, dst, *, follow_symlinks=True):
|
| 422 |
+
"""Copy data and metadata. Return the file's destination.
|
| 423 |
+
|
| 424 |
+
Metadata is copied with copystat(). Please see the copystat function
|
| 425 |
+
for more information.
|
| 426 |
+
|
| 427 |
+
The destination may be a directory.
|
| 428 |
+
|
| 429 |
+
If follow_symlinks is false, symlinks won't be followed. This
|
| 430 |
+
resembles GNU's "cp -P src dst".
|
| 431 |
+
"""
|
| 432 |
+
if os.path.isdir(dst):
|
| 433 |
+
dst = os.path.join(dst, os.path.basename(src))
|
| 434 |
+
copyfile(src, dst, follow_symlinks=follow_symlinks)
|
| 435 |
+
copystat(src, dst, follow_symlinks=follow_symlinks)
|
| 436 |
+
return dst
|
| 437 |
+
|
| 438 |
+
def ignore_patterns(*patterns):
|
| 439 |
+
"""Function that can be used as copytree() ignore parameter.
|
| 440 |
+
|
| 441 |
+
Patterns is a sequence of glob-style patterns
|
| 442 |
+
that are used to exclude files"""
|
| 443 |
+
def _ignore_patterns(path, names):
|
| 444 |
+
ignored_names = []
|
| 445 |
+
for pattern in patterns:
|
| 446 |
+
ignored_names.extend(fnmatch.filter(names, pattern))
|
| 447 |
+
return set(ignored_names)
|
| 448 |
+
return _ignore_patterns
|
| 449 |
+
|
| 450 |
+
def _copytree(entries, src, dst, symlinks, ignore, copy_function,
|
| 451 |
+
ignore_dangling_symlinks, dirs_exist_ok=False):
|
| 452 |
+
if ignore is not None:
|
| 453 |
+
ignored_names = ignore(os.fspath(src), [x.name for x in entries])
|
| 454 |
+
else:
|
| 455 |
+
ignored_names = set()
|
| 456 |
+
|
| 457 |
+
os.makedirs(dst, exist_ok=dirs_exist_ok)
|
| 458 |
+
errors = []
|
| 459 |
+
use_srcentry = copy_function is copy2 or copy_function is copy
|
| 460 |
+
|
| 461 |
+
for srcentry in entries:
|
| 462 |
+
if srcentry.name in ignored_names:
|
| 463 |
+
continue
|
| 464 |
+
srcname = os.path.join(src, srcentry.name)
|
| 465 |
+
dstname = os.path.join(dst, srcentry.name)
|
| 466 |
+
srcobj = srcentry if use_srcentry else srcname
|
| 467 |
+
try:
|
| 468 |
+
is_symlink = srcentry.is_symlink()
|
| 469 |
+
if is_symlink and os.name == 'nt':
|
| 470 |
+
# Special check for directory junctions, which appear as
|
| 471 |
+
# symlinks but we want to recurse.
|
| 472 |
+
lstat = srcentry.stat(follow_symlinks=False)
|
| 473 |
+
if lstat.st_reparse_tag == stat.IO_REPARSE_TAG_MOUNT_POINT:
|
| 474 |
+
is_symlink = False
|
| 475 |
+
if is_symlink:
|
| 476 |
+
linkto = os.readlink(srcname)
|
| 477 |
+
if symlinks:
|
| 478 |
+
# We can't just leave it to `copy_function` because legacy
|
| 479 |
+
# code with a custom `copy_function` may rely on copytree
|
| 480 |
+
# doing the right thing.
|
| 481 |
+
os.symlink(linkto, dstname)
|
| 482 |
+
copystat(srcobj, dstname, follow_symlinks=not symlinks)
|
| 483 |
+
else:
|
| 484 |
+
# ignore dangling symlink if the flag is on
|
| 485 |
+
if not os.path.exists(linkto) and ignore_dangling_symlinks:
|
| 486 |
+
continue
|
| 487 |
+
# otherwise let the copy occur. copy2 will raise an error
|
| 488 |
+
if srcentry.is_dir():
|
| 489 |
+
copytree(srcobj, dstname, symlinks, ignore,
|
| 490 |
+
copy_function, ignore_dangling_symlinks,
|
| 491 |
+
dirs_exist_ok)
|
| 492 |
+
else:
|
| 493 |
+
copy_function(srcobj, dstname)
|
| 494 |
+
elif srcentry.is_dir():
|
| 495 |
+
copytree(srcobj, dstname, symlinks, ignore, copy_function,
|
| 496 |
+
ignore_dangling_symlinks, dirs_exist_ok)
|
| 497 |
+
else:
|
| 498 |
+
# Will raise a SpecialFileError for unsupported file types
|
| 499 |
+
copy_function(srcobj, dstname)
|
| 500 |
+
# catch the Error from the recursive copytree so that we can
|
| 501 |
+
# continue with other files
|
| 502 |
+
except Error as err:
|
| 503 |
+
errors.extend(err.args[0])
|
| 504 |
+
except OSError as why:
|
| 505 |
+
errors.append((srcname, dstname, str(why)))
|
| 506 |
+
try:
|
| 507 |
+
copystat(src, dst)
|
| 508 |
+
except OSError as why:
|
| 509 |
+
# Copying file access times may fail on Windows
|
| 510 |
+
if getattr(why, 'winerror', None) is None:
|
| 511 |
+
errors.append((src, dst, str(why)))
|
| 512 |
+
if errors:
|
| 513 |
+
raise Error(errors)
|
| 514 |
+
return dst
|
| 515 |
+
|
| 516 |
+
def copytree(src, dst, symlinks=False, ignore=None, copy_function=copy2,
|
| 517 |
+
ignore_dangling_symlinks=False, dirs_exist_ok=False):
|
| 518 |
+
"""Recursively copy a directory tree and return the destination directory.
|
| 519 |
+
|
| 520 |
+
If exception(s) occur, an Error is raised with a list of reasons.
|
| 521 |
+
|
| 522 |
+
If the optional symlinks flag is true, symbolic links in the
|
| 523 |
+
source tree result in symbolic links in the destination tree; if
|
| 524 |
+
it is false, the contents of the files pointed to by symbolic
|
| 525 |
+
links are copied. If the file pointed by the symlink doesn't
|
| 526 |
+
exist, an exception will be added in the list of errors raised in
|
| 527 |
+
an Error exception at the end of the copy process.
|
| 528 |
+
|
| 529 |
+
You can set the optional ignore_dangling_symlinks flag to true if you
|
| 530 |
+
want to silence this exception. Notice that this has no effect on
|
| 531 |
+
platforms that don't support os.symlink.
|
| 532 |
+
|
| 533 |
+
The optional ignore argument is a callable. If given, it
|
| 534 |
+
is called with the `src` parameter, which is the directory
|
| 535 |
+
being visited by copytree(), and `names` which is the list of
|
| 536 |
+
`src` contents, as returned by os.listdir():
|
| 537 |
+
|
| 538 |
+
callable(src, names) -> ignored_names
|
| 539 |
+
|
| 540 |
+
Since copytree() is called recursively, the callable will be
|
| 541 |
+
called once for each directory that is copied. It returns a
|
| 542 |
+
list of names relative to the `src` directory that should
|
| 543 |
+
not be copied.
|
| 544 |
+
|
| 545 |
+
The optional copy_function argument is a callable that will be used
|
| 546 |
+
to copy each file. It will be called with the source path and the
|
| 547 |
+
destination path as arguments. By default, copy2() is used, but any
|
| 548 |
+
function that supports the same signature (like copy()) can be used.
|
| 549 |
+
|
| 550 |
+
If dirs_exist_ok is false (the default) and `dst` already exists, a
|
| 551 |
+
`FileExistsError` is raised. If `dirs_exist_ok` is true, the copying
|
| 552 |
+
operation will continue if it encounters existing directories, and files
|
| 553 |
+
within the `dst` tree will be overwritten by corresponding files from the
|
| 554 |
+
`src` tree.
|
| 555 |
+
"""
|
| 556 |
+
sys.audit("shutil.copytree", src, dst)
|
| 557 |
+
with os.scandir(src) as itr:
|
| 558 |
+
entries = list(itr)
|
| 559 |
+
return _copytree(entries=entries, src=src, dst=dst, symlinks=symlinks,
|
| 560 |
+
ignore=ignore, copy_function=copy_function,
|
| 561 |
+
ignore_dangling_symlinks=ignore_dangling_symlinks,
|
| 562 |
+
dirs_exist_ok=dirs_exist_ok)
|
| 563 |
+
|
| 564 |
+
if hasattr(os.stat_result, 'st_file_attributes'):
|
| 565 |
+
# Special handling for directory junctions to make them behave like
|
| 566 |
+
# symlinks for shutil.rmtree, since in general they do not appear as
|
| 567 |
+
# regular links.
|
| 568 |
+
def _rmtree_isdir(entry):
|
| 569 |
+
try:
|
| 570 |
+
st = entry.stat(follow_symlinks=False)
|
| 571 |
+
return (stat.S_ISDIR(st.st_mode) and not
|
| 572 |
+
(st.st_file_attributes & stat.FILE_ATTRIBUTE_REPARSE_POINT
|
| 573 |
+
and st.st_reparse_tag == stat.IO_REPARSE_TAG_MOUNT_POINT))
|
| 574 |
+
except OSError:
|
| 575 |
+
return False
|
| 576 |
+
|
| 577 |
+
def _rmtree_islink(path):
|
| 578 |
+
try:
|
| 579 |
+
st = os.lstat(path)
|
| 580 |
+
return (stat.S_ISLNK(st.st_mode) or
|
| 581 |
+
(st.st_file_attributes & stat.FILE_ATTRIBUTE_REPARSE_POINT
|
| 582 |
+
and st.st_reparse_tag == stat.IO_REPARSE_TAG_MOUNT_POINT))
|
| 583 |
+
except OSError:
|
| 584 |
+
return False
|
| 585 |
+
else:
|
| 586 |
+
def _rmtree_isdir(entry):
|
| 587 |
+
try:
|
| 588 |
+
return entry.is_dir(follow_symlinks=False)
|
| 589 |
+
except OSError:
|
| 590 |
+
return False
|
| 591 |
+
|
| 592 |
+
def _rmtree_islink(path):
|
| 593 |
+
return os.path.islink(path)
|
| 594 |
+
|
| 595 |
+
# version vulnerable to race conditions
|
| 596 |
+
def _rmtree_unsafe(path, onerror):
|
| 597 |
+
try:
|
| 598 |
+
with os.scandir(path) as scandir_it:
|
| 599 |
+
entries = list(scandir_it)
|
| 600 |
+
except OSError:
|
| 601 |
+
onerror(os.scandir, path, sys.exc_info())
|
| 602 |
+
entries = []
|
| 603 |
+
for entry in entries:
|
| 604 |
+
fullname = entry.path
|
| 605 |
+
if _rmtree_isdir(entry):
|
| 606 |
+
try:
|
| 607 |
+
if entry.is_symlink():
|
| 608 |
+
# This can only happen if someone replaces
|
| 609 |
+
# a directory with a symlink after the call to
|
| 610 |
+
# os.scandir or entry.is_dir above.
|
| 611 |
+
raise OSError("Cannot call rmtree on a symbolic link")
|
| 612 |
+
except OSError:
|
| 613 |
+
onerror(os.path.islink, fullname, sys.exc_info())
|
| 614 |
+
continue
|
| 615 |
+
_rmtree_unsafe(fullname, onerror)
|
| 616 |
+
else:
|
| 617 |
+
try:
|
| 618 |
+
os.unlink(fullname)
|
| 619 |
+
except OSError:
|
| 620 |
+
onerror(os.unlink, fullname, sys.exc_info())
|
| 621 |
+
try:
|
| 622 |
+
os.rmdir(path)
|
| 623 |
+
except OSError:
|
| 624 |
+
onerror(os.rmdir, path, sys.exc_info())
|
| 625 |
+
|
| 626 |
+
# Version using fd-based APIs to protect against races
|
| 627 |
+
def _rmtree_safe_fd(topfd, path, onerror):
|
| 628 |
+
try:
|
| 629 |
+
with os.scandir(topfd) as scandir_it:
|
| 630 |
+
entries = list(scandir_it)
|
| 631 |
+
except OSError as err:
|
| 632 |
+
err.filename = path
|
| 633 |
+
onerror(os.scandir, path, sys.exc_info())
|
| 634 |
+
return
|
| 635 |
+
for entry in entries:
|
| 636 |
+
fullname = os.path.join(path, entry.name)
|
| 637 |
+
try:
|
| 638 |
+
is_dir = entry.is_dir(follow_symlinks=False)
|
| 639 |
+
except OSError:
|
| 640 |
+
is_dir = False
|
| 641 |
+
else:
|
| 642 |
+
if is_dir:
|
| 643 |
+
try:
|
| 644 |
+
orig_st = entry.stat(follow_symlinks=False)
|
| 645 |
+
is_dir = stat.S_ISDIR(orig_st.st_mode)
|
| 646 |
+
except OSError:
|
| 647 |
+
onerror(os.lstat, fullname, sys.exc_info())
|
| 648 |
+
continue
|
| 649 |
+
if is_dir:
|
| 650 |
+
try:
|
| 651 |
+
dirfd = os.open(entry.name, os.O_RDONLY, dir_fd=topfd)
|
| 652 |
+
dirfd_closed = False
|
| 653 |
+
except OSError:
|
| 654 |
+
onerror(os.open, fullname, sys.exc_info())
|
| 655 |
+
else:
|
| 656 |
+
try:
|
| 657 |
+
if os.path.samestat(orig_st, os.fstat(dirfd)):
|
| 658 |
+
_rmtree_safe_fd(dirfd, fullname, onerror)
|
| 659 |
+
try:
|
| 660 |
+
os.close(dirfd)
|
| 661 |
+
dirfd_closed = True
|
| 662 |
+
os.rmdir(entry.name, dir_fd=topfd)
|
| 663 |
+
except OSError:
|
| 664 |
+
onerror(os.rmdir, fullname, sys.exc_info())
|
| 665 |
+
else:
|
| 666 |
+
try:
|
| 667 |
+
# This can only happen if someone replaces
|
| 668 |
+
# a directory with a symlink after the call to
|
| 669 |
+
# os.scandir or stat.S_ISDIR above.
|
| 670 |
+
raise OSError("Cannot call rmtree on a symbolic "
|
| 671 |
+
"link")
|
| 672 |
+
except OSError:
|
| 673 |
+
onerror(os.path.islink, fullname, sys.exc_info())
|
| 674 |
+
finally:
|
| 675 |
+
if not dirfd_closed:
|
| 676 |
+
os.close(dirfd)
|
| 677 |
+
else:
|
| 678 |
+
try:
|
| 679 |
+
os.unlink(entry.name, dir_fd=topfd)
|
| 680 |
+
except OSError:
|
| 681 |
+
onerror(os.unlink, fullname, sys.exc_info())
|
| 682 |
+
|
| 683 |
+
_use_fd_functions = ({os.open, os.stat, os.unlink, os.rmdir} <=
|
| 684 |
+
os.supports_dir_fd and
|
| 685 |
+
os.scandir in os.supports_fd and
|
| 686 |
+
os.stat in os.supports_follow_symlinks)
|
| 687 |
+
|
| 688 |
+
def rmtree(path, ignore_errors=False, onerror=None):
|
| 689 |
+
"""Recursively delete a directory tree.
|
| 690 |
+
|
| 691 |
+
If ignore_errors is set, errors are ignored; otherwise, if onerror
|
| 692 |
+
is set, it is called to handle the error with arguments (func,
|
| 693 |
+
path, exc_info) where func is platform and implementation dependent;
|
| 694 |
+
path is the argument to that function that caused it to fail; and
|
| 695 |
+
exc_info is a tuple returned by sys.exc_info(). If ignore_errors
|
| 696 |
+
is false and onerror is None, an exception is raised.
|
| 697 |
+
|
| 698 |
+
"""
|
| 699 |
+
sys.audit("shutil.rmtree", path)
|
| 700 |
+
if ignore_errors:
|
| 701 |
+
def onerror(*args):
|
| 702 |
+
pass
|
| 703 |
+
elif onerror is None:
|
| 704 |
+
def onerror(*args):
|
| 705 |
+
raise
|
| 706 |
+
if _use_fd_functions:
|
| 707 |
+
# While the unsafe rmtree works fine on bytes, the fd based does not.
|
| 708 |
+
if isinstance(path, bytes):
|
| 709 |
+
path = os.fsdecode(path)
|
| 710 |
+
# Note: To guard against symlink races, we use the standard
|
| 711 |
+
# lstat()/open()/fstat() trick.
|
| 712 |
+
try:
|
| 713 |
+
orig_st = os.lstat(path)
|
| 714 |
+
except Exception:
|
| 715 |
+
onerror(os.lstat, path, sys.exc_info())
|
| 716 |
+
return
|
| 717 |
+
try:
|
| 718 |
+
fd = os.open(path, os.O_RDONLY)
|
| 719 |
+
fd_closed = False
|
| 720 |
+
except Exception:
|
| 721 |
+
onerror(os.open, path, sys.exc_info())
|
| 722 |
+
return
|
| 723 |
+
try:
|
| 724 |
+
if os.path.samestat(orig_st, os.fstat(fd)):
|
| 725 |
+
_rmtree_safe_fd(fd, path, onerror)
|
| 726 |
+
try:
|
| 727 |
+
os.close(fd)
|
| 728 |
+
fd_closed = True
|
| 729 |
+
os.rmdir(path)
|
| 730 |
+
except OSError:
|
| 731 |
+
onerror(os.rmdir, path, sys.exc_info())
|
| 732 |
+
else:
|
| 733 |
+
try:
|
| 734 |
+
# symlinks to directories are forbidden, see bug #1669
|
| 735 |
+
raise OSError("Cannot call rmtree on a symbolic link")
|
| 736 |
+
except OSError:
|
| 737 |
+
onerror(os.path.islink, path, sys.exc_info())
|
| 738 |
+
finally:
|
| 739 |
+
if not fd_closed:
|
| 740 |
+
os.close(fd)
|
| 741 |
+
else:
|
| 742 |
+
try:
|
| 743 |
+
if _rmtree_islink(path):
|
| 744 |
+
# symlinks to directories are forbidden, see bug #1669
|
| 745 |
+
raise OSError("Cannot call rmtree on a symbolic link")
|
| 746 |
+
except OSError:
|
| 747 |
+
onerror(os.path.islink, path, sys.exc_info())
|
| 748 |
+
# can't continue even if onerror hook returns
|
| 749 |
+
return
|
| 750 |
+
return _rmtree_unsafe(path, onerror)
|
| 751 |
+
|
| 752 |
+
# Allow introspection of whether or not the hardening against symlink
|
| 753 |
+
# attacks is supported on the current platform
|
| 754 |
+
rmtree.avoids_symlink_attacks = _use_fd_functions
|
| 755 |
+
|
| 756 |
+
def _basename(path):
|
| 757 |
+
"""A basename() variant which first strips the trailing slash, if present.
|
| 758 |
+
Thus we always get the last component of the path, even for directories.
|
| 759 |
+
|
| 760 |
+
path: Union[PathLike, str]
|
| 761 |
+
|
| 762 |
+
e.g.
|
| 763 |
+
>>> os.path.basename('/bar/foo')
|
| 764 |
+
'foo'
|
| 765 |
+
>>> os.path.basename('/bar/foo/')
|
| 766 |
+
''
|
| 767 |
+
>>> _basename('/bar/foo/')
|
| 768 |
+
'foo'
|
| 769 |
+
"""
|
| 770 |
+
path = os.fspath(path)
|
| 771 |
+
sep = os.path.sep + (os.path.altsep or '')
|
| 772 |
+
return os.path.basename(path.rstrip(sep))
|
| 773 |
+
|
| 774 |
+
def move(src, dst, copy_function=copy2):
|
| 775 |
+
"""Recursively move a file or directory to another location. This is
|
| 776 |
+
similar to the Unix "mv" command. Return the file or directory's
|
| 777 |
+
destination.
|
| 778 |
+
|
| 779 |
+
If the destination is a directory or a symlink to a directory, the source
|
| 780 |
+
is moved inside the directory. The destination path must not already
|
| 781 |
+
exist.
|
| 782 |
+
|
| 783 |
+
If the destination already exists but is not a directory, it may be
|
| 784 |
+
overwritten depending on os.rename() semantics.
|
| 785 |
+
|
| 786 |
+
If the destination is on our current filesystem, then rename() is used.
|
| 787 |
+
Otherwise, src is copied to the destination and then removed. Symlinks are
|
| 788 |
+
recreated under the new name if os.rename() fails because of cross
|
| 789 |
+
filesystem renames.
|
| 790 |
+
|
| 791 |
+
The optional `copy_function` argument is a callable that will be used
|
| 792 |
+
to copy the source or it will be delegated to `copytree`.
|
| 793 |
+
By default, copy2() is used, but any function that supports the same
|
| 794 |
+
signature (like copy()) can be used.
|
| 795 |
+
|
| 796 |
+
A lot more could be done here... A look at a mv.c shows a lot of
|
| 797 |
+
the issues this implementation glosses over.
|
| 798 |
+
|
| 799 |
+
"""
|
| 800 |
+
sys.audit("shutil.move", src, dst)
|
| 801 |
+
real_dst = dst
|
| 802 |
+
if os.path.isdir(dst):
|
| 803 |
+
if _samefile(src, dst):
|
| 804 |
+
# We might be on a case insensitive filesystem,
|
| 805 |
+
# perform the rename anyway.
|
| 806 |
+
os.rename(src, dst)
|
| 807 |
+
return
|
| 808 |
+
|
| 809 |
+
# Using _basename instead of os.path.basename is important, as we must
|
| 810 |
+
# ignore any trailing slash to avoid the basename returning ''
|
| 811 |
+
real_dst = os.path.join(dst, _basename(src))
|
| 812 |
+
|
| 813 |
+
if os.path.exists(real_dst):
|
| 814 |
+
raise Error("Destination path '%s' already exists" % real_dst)
|
| 815 |
+
try:
|
| 816 |
+
os.rename(src, real_dst)
|
| 817 |
+
except OSError:
|
| 818 |
+
if os.path.islink(src):
|
| 819 |
+
linkto = os.readlink(src)
|
| 820 |
+
os.symlink(linkto, real_dst)
|
| 821 |
+
os.unlink(src)
|
| 822 |
+
elif os.path.isdir(src):
|
| 823 |
+
if _destinsrc(src, dst):
|
| 824 |
+
raise Error("Cannot move a directory '%s' into itself"
|
| 825 |
+
" '%s'." % (src, dst))
|
| 826 |
+
if (_is_immutable(src)
|
| 827 |
+
or (not os.access(src, os.W_OK) and os.listdir(src)
|
| 828 |
+
and sys.platform == 'darwin')):
|
| 829 |
+
raise PermissionError("Cannot move the non-empty directory "
|
| 830 |
+
"'%s': Lacking write permission to '%s'."
|
| 831 |
+
% (src, src))
|
| 832 |
+
copytree(src, real_dst, copy_function=copy_function,
|
| 833 |
+
symlinks=True)
|
| 834 |
+
rmtree(src)
|
| 835 |
+
else:
|
| 836 |
+
copy_function(src, real_dst)
|
| 837 |
+
os.unlink(src)
|
| 838 |
+
return real_dst
|
| 839 |
+
|
| 840 |
+
def _destinsrc(src, dst):
|
| 841 |
+
src = os.path.abspath(src)
|
| 842 |
+
dst = os.path.abspath(dst)
|
| 843 |
+
if not src.endswith(os.path.sep):
|
| 844 |
+
src += os.path.sep
|
| 845 |
+
if not dst.endswith(os.path.sep):
|
| 846 |
+
dst += os.path.sep
|
| 847 |
+
return dst.startswith(src)
|
| 848 |
+
|
| 849 |
+
def _is_immutable(src):
|
| 850 |
+
st = _stat(src)
|
| 851 |
+
immutable_states = [stat.UF_IMMUTABLE, stat.SF_IMMUTABLE]
|
| 852 |
+
return hasattr(st, 'st_flags') and st.st_flags in immutable_states
|
| 853 |
+
|
| 854 |
+
def _get_gid(name):
|
| 855 |
+
"""Returns a gid, given a group name."""
|
| 856 |
+
if name is None:
|
| 857 |
+
return None
|
| 858 |
+
|
| 859 |
+
try:
|
| 860 |
+
from grp import getgrnam
|
| 861 |
+
except ImportError:
|
| 862 |
+
return None
|
| 863 |
+
|
| 864 |
+
try:
|
| 865 |
+
result = getgrnam(name)
|
| 866 |
+
except KeyError:
|
| 867 |
+
result = None
|
| 868 |
+
if result is not None:
|
| 869 |
+
return result[2]
|
| 870 |
+
return None
|
| 871 |
+
|
| 872 |
+
def _get_uid(name):
|
| 873 |
+
"""Returns an uid, given a user name."""
|
| 874 |
+
if name is None:
|
| 875 |
+
return None
|
| 876 |
+
|
| 877 |
+
try:
|
| 878 |
+
from pwd import getpwnam
|
| 879 |
+
except ImportError:
|
| 880 |
+
return None
|
| 881 |
+
|
| 882 |
+
try:
|
| 883 |
+
result = getpwnam(name)
|
| 884 |
+
except KeyError:
|
| 885 |
+
result = None
|
| 886 |
+
if result is not None:
|
| 887 |
+
return result[2]
|
| 888 |
+
return None
|
| 889 |
+
|
| 890 |
+
def _make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0,
|
| 891 |
+
owner=None, group=None, logger=None, root_dir=None):
|
| 892 |
+
"""Create a (possibly compressed) tar file from all the files under
|
| 893 |
+
'base_dir'.
|
| 894 |
+
|
| 895 |
+
'compress' must be "gzip" (the default), "bzip2", "xz", or None.
|
| 896 |
+
|
| 897 |
+
'owner' and 'group' can be used to define an owner and a group for the
|
| 898 |
+
archive that is being built. If not provided, the current owner and group
|
| 899 |
+
will be used.
|
| 900 |
+
|
| 901 |
+
The output tar file will be named 'base_name' + ".tar", possibly plus
|
| 902 |
+
the appropriate compression extension (".gz", ".bz2", or ".xz").
|
| 903 |
+
|
| 904 |
+
Returns the output filename.
|
| 905 |
+
"""
|
| 906 |
+
if compress is None:
|
| 907 |
+
tar_compression = ''
|
| 908 |
+
elif _ZLIB_SUPPORTED and compress == 'gzip':
|
| 909 |
+
tar_compression = 'gz'
|
| 910 |
+
elif _BZ2_SUPPORTED and compress == 'bzip2':
|
| 911 |
+
tar_compression = 'bz2'
|
| 912 |
+
elif _LZMA_SUPPORTED and compress == 'xz':
|
| 913 |
+
tar_compression = 'xz'
|
| 914 |
+
else:
|
| 915 |
+
raise ValueError("bad value for 'compress', or compression format not "
|
| 916 |
+
"supported : {0}".format(compress))
|
| 917 |
+
|
| 918 |
+
import tarfile # late import for breaking circular dependency
|
| 919 |
+
|
| 920 |
+
compress_ext = '.' + tar_compression if compress else ''
|
| 921 |
+
archive_name = base_name + '.tar' + compress_ext
|
| 922 |
+
archive_dir = os.path.dirname(archive_name)
|
| 923 |
+
|
| 924 |
+
if archive_dir and not os.path.exists(archive_dir):
|
| 925 |
+
if logger is not None:
|
| 926 |
+
logger.info("creating %s", archive_dir)
|
| 927 |
+
if not dry_run:
|
| 928 |
+
os.makedirs(archive_dir)
|
| 929 |
+
|
| 930 |
+
# creating the tarball
|
| 931 |
+
if logger is not None:
|
| 932 |
+
logger.info('Creating tar archive')
|
| 933 |
+
|
| 934 |
+
uid = _get_uid(owner)
|
| 935 |
+
gid = _get_gid(group)
|
| 936 |
+
|
| 937 |
+
def _set_uid_gid(tarinfo):
|
| 938 |
+
if gid is not None:
|
| 939 |
+
tarinfo.gid = gid
|
| 940 |
+
tarinfo.gname = group
|
| 941 |
+
if uid is not None:
|
| 942 |
+
tarinfo.uid = uid
|
| 943 |
+
tarinfo.uname = owner
|
| 944 |
+
return tarinfo
|
| 945 |
+
|
| 946 |
+
if not dry_run:
|
| 947 |
+
tar = tarfile.open(archive_name, 'w|%s' % tar_compression)
|
| 948 |
+
arcname = base_dir
|
| 949 |
+
if root_dir is not None:
|
| 950 |
+
base_dir = os.path.join(root_dir, base_dir)
|
| 951 |
+
try:
|
| 952 |
+
tar.add(base_dir, arcname, filter=_set_uid_gid)
|
| 953 |
+
finally:
|
| 954 |
+
tar.close()
|
| 955 |
+
|
| 956 |
+
if root_dir is not None:
|
| 957 |
+
archive_name = os.path.abspath(archive_name)
|
| 958 |
+
return archive_name
|
| 959 |
+
|
| 960 |
+
def _make_zipfile(base_name, base_dir, verbose=0, dry_run=0,
|
| 961 |
+
logger=None, owner=None, group=None, root_dir=None):
|
| 962 |
+
"""Create a zip file from all the files under 'base_dir'.
|
| 963 |
+
|
| 964 |
+
The output zip file will be named 'base_name' + ".zip". Returns the
|
| 965 |
+
name of the output zip file.
|
| 966 |
+
"""
|
| 967 |
+
import zipfile # late import for breaking circular dependency
|
| 968 |
+
|
| 969 |
+
zip_filename = base_name + ".zip"
|
| 970 |
+
archive_dir = os.path.dirname(base_name)
|
| 971 |
+
|
| 972 |
+
if archive_dir and not os.path.exists(archive_dir):
|
| 973 |
+
if logger is not None:
|
| 974 |
+
logger.info("creating %s", archive_dir)
|
| 975 |
+
if not dry_run:
|
| 976 |
+
os.makedirs(archive_dir)
|
| 977 |
+
|
| 978 |
+
if logger is not None:
|
| 979 |
+
logger.info("creating '%s' and adding '%s' to it",
|
| 980 |
+
zip_filename, base_dir)
|
| 981 |
+
|
| 982 |
+
if not dry_run:
|
| 983 |
+
with zipfile.ZipFile(zip_filename, "w",
|
| 984 |
+
compression=zipfile.ZIP_DEFLATED) as zf:
|
| 985 |
+
arcname = os.path.normpath(base_dir)
|
| 986 |
+
if root_dir is not None:
|
| 987 |
+
base_dir = os.path.join(root_dir, base_dir)
|
| 988 |
+
base_dir = os.path.normpath(base_dir)
|
| 989 |
+
if arcname != os.curdir:
|
| 990 |
+
zf.write(base_dir, arcname)
|
| 991 |
+
if logger is not None:
|
| 992 |
+
logger.info("adding '%s'", base_dir)
|
| 993 |
+
for dirpath, dirnames, filenames in os.walk(base_dir):
|
| 994 |
+
arcdirpath = dirpath
|
| 995 |
+
if root_dir is not None:
|
| 996 |
+
arcdirpath = os.path.relpath(arcdirpath, root_dir)
|
| 997 |
+
arcdirpath = os.path.normpath(arcdirpath)
|
| 998 |
+
for name in sorted(dirnames):
|
| 999 |
+
path = os.path.join(dirpath, name)
|
| 1000 |
+
arcname = os.path.join(arcdirpath, name)
|
| 1001 |
+
zf.write(path, arcname)
|
| 1002 |
+
if logger is not None:
|
| 1003 |
+
logger.info("adding '%s'", path)
|
| 1004 |
+
for name in filenames:
|
| 1005 |
+
path = os.path.join(dirpath, name)
|
| 1006 |
+
path = os.path.normpath(path)
|
| 1007 |
+
if os.path.isfile(path):
|
| 1008 |
+
arcname = os.path.join(arcdirpath, name)
|
| 1009 |
+
zf.write(path, arcname)
|
| 1010 |
+
if logger is not None:
|
| 1011 |
+
logger.info("adding '%s'", path)
|
| 1012 |
+
|
| 1013 |
+
if root_dir is not None:
|
| 1014 |
+
zip_filename = os.path.abspath(zip_filename)
|
| 1015 |
+
return zip_filename
|
| 1016 |
+
|
| 1017 |
+
# Maps the name of the archive format to a tuple containing:
|
| 1018 |
+
# * the archiving function
|
| 1019 |
+
# * extra keyword arguments
|
| 1020 |
+
# * description
|
| 1021 |
+
# * does it support the root_dir argument?
|
| 1022 |
+
_ARCHIVE_FORMATS = {
|
| 1023 |
+
'tar': (_make_tarball, [('compress', None)],
|
| 1024 |
+
"uncompressed tar file", True),
|
| 1025 |
+
}
|
| 1026 |
+
|
| 1027 |
+
if _ZLIB_SUPPORTED:
|
| 1028 |
+
_ARCHIVE_FORMATS['gztar'] = (_make_tarball, [('compress', 'gzip')],
|
| 1029 |
+
"gzip'ed tar-file", True)
|
| 1030 |
+
_ARCHIVE_FORMATS['zip'] = (_make_zipfile, [], "ZIP file", True)
|
| 1031 |
+
|
| 1032 |
+
if _BZ2_SUPPORTED:
|
| 1033 |
+
_ARCHIVE_FORMATS['bztar'] = (_make_tarball, [('compress', 'bzip2')],
|
| 1034 |
+
"bzip2'ed tar-file", True)
|
| 1035 |
+
|
| 1036 |
+
if _LZMA_SUPPORTED:
|
| 1037 |
+
_ARCHIVE_FORMATS['xztar'] = (_make_tarball, [('compress', 'xz')],
|
| 1038 |
+
"xz'ed tar-file", True)
|
| 1039 |
+
|
| 1040 |
+
def get_archive_formats():
|
| 1041 |
+
"""Returns a list of supported formats for archiving and unarchiving.
|
| 1042 |
+
|
| 1043 |
+
Each element of the returned sequence is a tuple (name, description)
|
| 1044 |
+
"""
|
| 1045 |
+
formats = [(name, registry[2]) for name, registry in
|
| 1046 |
+
_ARCHIVE_FORMATS.items()]
|
| 1047 |
+
formats.sort()
|
| 1048 |
+
return formats
|
| 1049 |
+
|
| 1050 |
+
def register_archive_format(name, function, extra_args=None, description=''):
|
| 1051 |
+
"""Registers an archive format.
|
| 1052 |
+
|
| 1053 |
+
name is the name of the format. function is the callable that will be
|
| 1054 |
+
used to create archives. If provided, extra_args is a sequence of
|
| 1055 |
+
(name, value) tuples that will be passed as arguments to the callable.
|
| 1056 |
+
description can be provided to describe the format, and will be returned
|
| 1057 |
+
by the get_archive_formats() function.
|
| 1058 |
+
"""
|
| 1059 |
+
if extra_args is None:
|
| 1060 |
+
extra_args = []
|
| 1061 |
+
if not callable(function):
|
| 1062 |
+
raise TypeError('The %s object is not callable' % function)
|
| 1063 |
+
if not isinstance(extra_args, (tuple, list)):
|
| 1064 |
+
raise TypeError('extra_args needs to be a sequence')
|
| 1065 |
+
for element in extra_args:
|
| 1066 |
+
if not isinstance(element, (tuple, list)) or len(element) !=2:
|
| 1067 |
+
raise TypeError('extra_args elements are : (arg_name, value)')
|
| 1068 |
+
|
| 1069 |
+
_ARCHIVE_FORMATS[name] = (function, extra_args, description, False)
|
| 1070 |
+
|
| 1071 |
+
def unregister_archive_format(name):
|
| 1072 |
+
del _ARCHIVE_FORMATS[name]
|
| 1073 |
+
|
| 1074 |
+
def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0,
|
| 1075 |
+
dry_run=0, owner=None, group=None, logger=None):
|
| 1076 |
+
"""Create an archive file (eg. zip or tar).
|
| 1077 |
+
|
| 1078 |
+
'base_name' is the name of the file to create, minus any format-specific
|
| 1079 |
+
extension; 'format' is the archive format: one of "zip", "tar", "gztar",
|
| 1080 |
+
"bztar", or "xztar". Or any other registered format.
|
| 1081 |
+
|
| 1082 |
+
'root_dir' is a directory that will be the root directory of the
|
| 1083 |
+
archive; ie. we typically chdir into 'root_dir' before creating the
|
| 1084 |
+
archive. 'base_dir' is the directory where we start archiving from;
|
| 1085 |
+
ie. 'base_dir' will be the common prefix of all files and
|
| 1086 |
+
directories in the archive. 'root_dir' and 'base_dir' both default
|
| 1087 |
+
to the current directory. Returns the name of the archive file.
|
| 1088 |
+
|
| 1089 |
+
'owner' and 'group' are used when creating a tar archive. By default,
|
| 1090 |
+
uses the current owner and group.
|
| 1091 |
+
"""
|
| 1092 |
+
sys.audit("shutil.make_archive", base_name, format, root_dir, base_dir)
|
| 1093 |
+
try:
|
| 1094 |
+
format_info = _ARCHIVE_FORMATS[format]
|
| 1095 |
+
except KeyError:
|
| 1096 |
+
raise ValueError("unknown archive format '%s'" % format) from None
|
| 1097 |
+
|
| 1098 |
+
kwargs = {'dry_run': dry_run, 'logger': logger,
|
| 1099 |
+
'owner': owner, 'group': group}
|
| 1100 |
+
|
| 1101 |
+
func = format_info[0]
|
| 1102 |
+
for arg, val in format_info[1]:
|
| 1103 |
+
kwargs[arg] = val
|
| 1104 |
+
|
| 1105 |
+
if base_dir is None:
|
| 1106 |
+
base_dir = os.curdir
|
| 1107 |
+
|
| 1108 |
+
support_root_dir = format_info[3]
|
| 1109 |
+
save_cwd = None
|
| 1110 |
+
if root_dir is not None:
|
| 1111 |
+
if support_root_dir:
|
| 1112 |
+
# Support path-like base_name here for backwards-compatibility.
|
| 1113 |
+
base_name = os.fspath(base_name)
|
| 1114 |
+
kwargs['root_dir'] = root_dir
|
| 1115 |
+
else:
|
| 1116 |
+
save_cwd = os.getcwd()
|
| 1117 |
+
if logger is not None:
|
| 1118 |
+
logger.debug("changing into '%s'", root_dir)
|
| 1119 |
+
base_name = os.path.abspath(base_name)
|
| 1120 |
+
if not dry_run:
|
| 1121 |
+
os.chdir(root_dir)
|
| 1122 |
+
|
| 1123 |
+
try:
|
| 1124 |
+
filename = func(base_name, base_dir, **kwargs)
|
| 1125 |
+
finally:
|
| 1126 |
+
if save_cwd is not None:
|
| 1127 |
+
if logger is not None:
|
| 1128 |
+
logger.debug("changing back to '%s'", save_cwd)
|
| 1129 |
+
os.chdir(save_cwd)
|
| 1130 |
+
|
| 1131 |
+
return filename
|
| 1132 |
+
|
| 1133 |
+
|
| 1134 |
+
def get_unpack_formats():
|
| 1135 |
+
"""Returns a list of supported formats for unpacking.
|
| 1136 |
+
|
| 1137 |
+
Each element of the returned sequence is a tuple
|
| 1138 |
+
(name, extensions, description)
|
| 1139 |
+
"""
|
| 1140 |
+
formats = [(name, info[0], info[3]) for name, info in
|
| 1141 |
+
_UNPACK_FORMATS.items()]
|
| 1142 |
+
formats.sort()
|
| 1143 |
+
return formats
|
| 1144 |
+
|
| 1145 |
+
def _check_unpack_options(extensions, function, extra_args):
|
| 1146 |
+
"""Checks what gets registered as an unpacker."""
|
| 1147 |
+
# first make sure no other unpacker is registered for this extension
|
| 1148 |
+
existing_extensions = {}
|
| 1149 |
+
for name, info in _UNPACK_FORMATS.items():
|
| 1150 |
+
for ext in info[0]:
|
| 1151 |
+
existing_extensions[ext] = name
|
| 1152 |
+
|
| 1153 |
+
for extension in extensions:
|
| 1154 |
+
if extension in existing_extensions:
|
| 1155 |
+
msg = '%s is already registered for "%s"'
|
| 1156 |
+
raise RegistryError(msg % (extension,
|
| 1157 |
+
existing_extensions[extension]))
|
| 1158 |
+
|
| 1159 |
+
if not callable(function):
|
| 1160 |
+
raise TypeError('The registered function must be a callable')
|
| 1161 |
+
|
| 1162 |
+
|
| 1163 |
+
def register_unpack_format(name, extensions, function, extra_args=None,
|
| 1164 |
+
description=''):
|
| 1165 |
+
"""Registers an unpack format.
|
| 1166 |
+
|
| 1167 |
+
`name` is the name of the format. `extensions` is a list of extensions
|
| 1168 |
+
corresponding to the format.
|
| 1169 |
+
|
| 1170 |
+
`function` is the callable that will be
|
| 1171 |
+
used to unpack archives. The callable will receive archives to unpack.
|
| 1172 |
+
If it's unable to handle an archive, it needs to raise a ReadError
|
| 1173 |
+
exception.
|
| 1174 |
+
|
| 1175 |
+
If provided, `extra_args` is a sequence of
|
| 1176 |
+
(name, value) tuples that will be passed as arguments to the callable.
|
| 1177 |
+
description can be provided to describe the format, and will be returned
|
| 1178 |
+
by the get_unpack_formats() function.
|
| 1179 |
+
"""
|
| 1180 |
+
if extra_args is None:
|
| 1181 |
+
extra_args = []
|
| 1182 |
+
_check_unpack_options(extensions, function, extra_args)
|
| 1183 |
+
_UNPACK_FORMATS[name] = extensions, function, extra_args, description
|
| 1184 |
+
|
| 1185 |
+
def unregister_unpack_format(name):
|
| 1186 |
+
"""Removes the pack format from the registry."""
|
| 1187 |
+
del _UNPACK_FORMATS[name]
|
| 1188 |
+
|
| 1189 |
+
def _ensure_directory(path):
|
| 1190 |
+
"""Ensure that the parent directory of `path` exists"""
|
| 1191 |
+
dirname = os.path.dirname(path)
|
| 1192 |
+
if not os.path.isdir(dirname):
|
| 1193 |
+
os.makedirs(dirname)
|
| 1194 |
+
|
| 1195 |
+
def _unpack_zipfile(filename, extract_dir):
|
| 1196 |
+
"""Unpack zip `filename` to `extract_dir`
|
| 1197 |
+
"""
|
| 1198 |
+
import zipfile # late import for breaking circular dependency
|
| 1199 |
+
|
| 1200 |
+
if not zipfile.is_zipfile(filename):
|
| 1201 |
+
raise ReadError("%s is not a zip file" % filename)
|
| 1202 |
+
|
| 1203 |
+
zip = zipfile.ZipFile(filename)
|
| 1204 |
+
try:
|
| 1205 |
+
for info in zip.infolist():
|
| 1206 |
+
name = info.filename
|
| 1207 |
+
|
| 1208 |
+
# don't extract absolute paths or ones with .. in them
|
| 1209 |
+
if name.startswith('/') or '..' in name:
|
| 1210 |
+
continue
|
| 1211 |
+
|
| 1212 |
+
targetpath = os.path.join(extract_dir, *name.split('/'))
|
| 1213 |
+
if not targetpath:
|
| 1214 |
+
continue
|
| 1215 |
+
|
| 1216 |
+
_ensure_directory(targetpath)
|
| 1217 |
+
if not name.endswith('/'):
|
| 1218 |
+
# file
|
| 1219 |
+
with zip.open(name, 'r') as source, \
|
| 1220 |
+
open(targetpath, 'wb') as target:
|
| 1221 |
+
copyfileobj(source, target)
|
| 1222 |
+
finally:
|
| 1223 |
+
zip.close()
|
| 1224 |
+
|
| 1225 |
+
def _unpack_tarfile(filename, extract_dir, *, filter=None):
|
| 1226 |
+
"""Unpack tar/tar.gz/tar.bz2/tar.xz `filename` to `extract_dir`
|
| 1227 |
+
"""
|
| 1228 |
+
import tarfile # late import for breaking circular dependency
|
| 1229 |
+
try:
|
| 1230 |
+
tarobj = tarfile.open(filename)
|
| 1231 |
+
except tarfile.TarError:
|
| 1232 |
+
raise ReadError(
|
| 1233 |
+
"%s is not a compressed or uncompressed tar file" % filename)
|
| 1234 |
+
try:
|
| 1235 |
+
tarobj.extractall(extract_dir, filter=filter)
|
| 1236 |
+
finally:
|
| 1237 |
+
tarobj.close()
|
| 1238 |
+
|
| 1239 |
+
# Maps the name of the unpack format to a tuple containing:
|
| 1240 |
+
# * extensions
|
| 1241 |
+
# * the unpacking function
|
| 1242 |
+
# * extra keyword arguments
|
| 1243 |
+
# * description
|
| 1244 |
+
_UNPACK_FORMATS = {
|
| 1245 |
+
'tar': (['.tar'], _unpack_tarfile, [], "uncompressed tar file"),
|
| 1246 |
+
'zip': (['.zip'], _unpack_zipfile, [], "ZIP file"),
|
| 1247 |
+
}
|
| 1248 |
+
|
| 1249 |
+
if _ZLIB_SUPPORTED:
|
| 1250 |
+
_UNPACK_FORMATS['gztar'] = (['.tar.gz', '.tgz'], _unpack_tarfile, [],
|
| 1251 |
+
"gzip'ed tar-file")
|
| 1252 |
+
|
| 1253 |
+
if _BZ2_SUPPORTED:
|
| 1254 |
+
_UNPACK_FORMATS['bztar'] = (['.tar.bz2', '.tbz2'], _unpack_tarfile, [],
|
| 1255 |
+
"bzip2'ed tar-file")
|
| 1256 |
+
|
| 1257 |
+
if _LZMA_SUPPORTED:
|
| 1258 |
+
_UNPACK_FORMATS['xztar'] = (['.tar.xz', '.txz'], _unpack_tarfile, [],
|
| 1259 |
+
"xz'ed tar-file")
|
| 1260 |
+
|
| 1261 |
+
def _find_unpack_format(filename):
|
| 1262 |
+
for name, info in _UNPACK_FORMATS.items():
|
| 1263 |
+
for extension in info[0]:
|
| 1264 |
+
if filename.endswith(extension):
|
| 1265 |
+
return name
|
| 1266 |
+
return None
|
| 1267 |
+
|
| 1268 |
+
def unpack_archive(filename, extract_dir=None, format=None, *, filter=None):
|
| 1269 |
+
"""Unpack an archive.
|
| 1270 |
+
|
| 1271 |
+
`filename` is the name of the archive.
|
| 1272 |
+
|
| 1273 |
+
`extract_dir` is the name of the target directory, where the archive
|
| 1274 |
+
is unpacked. If not provided, the current working directory is used.
|
| 1275 |
+
|
| 1276 |
+
`format` is the archive format: one of "zip", "tar", "gztar", "bztar",
|
| 1277 |
+
or "xztar". Or any other registered format. If not provided,
|
| 1278 |
+
unpack_archive will use the filename extension and see if an unpacker
|
| 1279 |
+
was registered for that extension.
|
| 1280 |
+
|
| 1281 |
+
In case none is found, a ValueError is raised.
|
| 1282 |
+
|
| 1283 |
+
If `filter` is given, it is passed to the underlying
|
| 1284 |
+
extraction function.
|
| 1285 |
+
"""
|
| 1286 |
+
sys.audit("shutil.unpack_archive", filename, extract_dir, format)
|
| 1287 |
+
|
| 1288 |
+
if extract_dir is None:
|
| 1289 |
+
extract_dir = os.getcwd()
|
| 1290 |
+
|
| 1291 |
+
extract_dir = os.fspath(extract_dir)
|
| 1292 |
+
filename = os.fspath(filename)
|
| 1293 |
+
|
| 1294 |
+
if filter is None:
|
| 1295 |
+
filter_kwargs = {}
|
| 1296 |
+
else:
|
| 1297 |
+
filter_kwargs = {'filter': filter}
|
| 1298 |
+
if format is not None:
|
| 1299 |
+
try:
|
| 1300 |
+
format_info = _UNPACK_FORMATS[format]
|
| 1301 |
+
except KeyError:
|
| 1302 |
+
raise ValueError("Unknown unpack format '{0}'".format(format)) from None
|
| 1303 |
+
|
| 1304 |
+
func = format_info[1]
|
| 1305 |
+
func(filename, extract_dir, **dict(format_info[2]), **filter_kwargs)
|
| 1306 |
+
else:
|
| 1307 |
+
# we need to look at the registered unpackers supported extensions
|
| 1308 |
+
format = _find_unpack_format(filename)
|
| 1309 |
+
if format is None:
|
| 1310 |
+
raise ReadError("Unknown archive format '{0}'".format(filename))
|
| 1311 |
+
|
| 1312 |
+
func = _UNPACK_FORMATS[format][1]
|
| 1313 |
+
kwargs = dict(_UNPACK_FORMATS[format][2]) | filter_kwargs
|
| 1314 |
+
func(filename, extract_dir, **kwargs)
|
| 1315 |
+
|
| 1316 |
+
|
| 1317 |
+
if hasattr(os, 'statvfs'):
|
| 1318 |
+
|
| 1319 |
+
__all__.append('disk_usage')
|
| 1320 |
+
_ntuple_diskusage = collections.namedtuple('usage', 'total used free')
|
| 1321 |
+
_ntuple_diskusage.total.__doc__ = 'Total space in bytes'
|
| 1322 |
+
_ntuple_diskusage.used.__doc__ = 'Used space in bytes'
|
| 1323 |
+
_ntuple_diskusage.free.__doc__ = 'Free space in bytes'
|
| 1324 |
+
|
| 1325 |
+
def disk_usage(path):
|
| 1326 |
+
"""Return disk usage statistics about the given path.
|
| 1327 |
+
|
| 1328 |
+
Returned value is a named tuple with attributes 'total', 'used' and
|
| 1329 |
+
'free', which are the amount of total, used and free space, in bytes.
|
| 1330 |
+
"""
|
| 1331 |
+
st = os.statvfs(path)
|
| 1332 |
+
free = st.f_bavail * st.f_frsize
|
| 1333 |
+
total = st.f_blocks * st.f_frsize
|
| 1334 |
+
used = (st.f_blocks - st.f_bfree) * st.f_frsize
|
| 1335 |
+
return _ntuple_diskusage(total, used, free)
|
| 1336 |
+
|
| 1337 |
+
elif _WINDOWS:
|
| 1338 |
+
|
| 1339 |
+
__all__.append('disk_usage')
|
| 1340 |
+
_ntuple_diskusage = collections.namedtuple('usage', 'total used free')
|
| 1341 |
+
|
| 1342 |
+
def disk_usage(path):
|
| 1343 |
+
"""Return disk usage statistics about the given path.
|
| 1344 |
+
|
| 1345 |
+
Returned values is a named tuple with attributes 'total', 'used' and
|
| 1346 |
+
'free', which are the amount of total, used and free space, in bytes.
|
| 1347 |
+
"""
|
| 1348 |
+
total, free = nt._getdiskusage(path)
|
| 1349 |
+
used = total - free
|
| 1350 |
+
return _ntuple_diskusage(total, used, free)
|
| 1351 |
+
|
| 1352 |
+
|
| 1353 |
+
def chown(path, user=None, group=None):
|
| 1354 |
+
"""Change owner user and group of the given path.
|
| 1355 |
+
|
| 1356 |
+
user and group can be the uid/gid or the user/group names, and in that case,
|
| 1357 |
+
they are converted to their respective uid/gid.
|
| 1358 |
+
"""
|
| 1359 |
+
sys.audit('shutil.chown', path, user, group)
|
| 1360 |
+
|
| 1361 |
+
if user is None and group is None:
|
| 1362 |
+
raise ValueError("user and/or group must be set")
|
| 1363 |
+
|
| 1364 |
+
_user = user
|
| 1365 |
+
_group = group
|
| 1366 |
+
|
| 1367 |
+
# -1 means don't change it
|
| 1368 |
+
if user is None:
|
| 1369 |
+
_user = -1
|
| 1370 |
+
# user can either be an int (the uid) or a string (the system username)
|
| 1371 |
+
elif isinstance(user, str):
|
| 1372 |
+
_user = _get_uid(user)
|
| 1373 |
+
if _user is None:
|
| 1374 |
+
raise LookupError("no such user: {!r}".format(user))
|
| 1375 |
+
|
| 1376 |
+
if group is None:
|
| 1377 |
+
_group = -1
|
| 1378 |
+
elif not isinstance(group, int):
|
| 1379 |
+
_group = _get_gid(group)
|
| 1380 |
+
if _group is None:
|
| 1381 |
+
raise LookupError("no such group: {!r}".format(group))
|
| 1382 |
+
|
| 1383 |
+
os.chown(path, _user, _group)
|
| 1384 |
+
|
| 1385 |
+
def get_terminal_size(fallback=(80, 24)):
|
| 1386 |
+
"""Get the size of the terminal window.
|
| 1387 |
+
|
| 1388 |
+
For each of the two dimensions, the environment variable, COLUMNS
|
| 1389 |
+
and LINES respectively, is checked. If the variable is defined and
|
| 1390 |
+
the value is a positive integer, it is used.
|
| 1391 |
+
|
| 1392 |
+
When COLUMNS or LINES is not defined, which is the common case,
|
| 1393 |
+
the terminal connected to sys.__stdout__ is queried
|
| 1394 |
+
by invoking os.get_terminal_size.
|
| 1395 |
+
|
| 1396 |
+
If the terminal size cannot be successfully queried, either because
|
| 1397 |
+
the system doesn't support querying, or because we are not
|
| 1398 |
+
connected to a terminal, the value given in fallback parameter
|
| 1399 |
+
is used. Fallback defaults to (80, 24) which is the default
|
| 1400 |
+
size used by many terminal emulators.
|
| 1401 |
+
|
| 1402 |
+
The value returned is a named tuple of type os.terminal_size.
|
| 1403 |
+
"""
|
| 1404 |
+
# columns, lines are the working values
|
| 1405 |
+
try:
|
| 1406 |
+
columns = int(os.environ['COLUMNS'])
|
| 1407 |
+
except (KeyError, ValueError):
|
| 1408 |
+
columns = 0
|
| 1409 |
+
|
| 1410 |
+
try:
|
| 1411 |
+
lines = int(os.environ['LINES'])
|
| 1412 |
+
except (KeyError, ValueError):
|
| 1413 |
+
lines = 0
|
| 1414 |
+
|
| 1415 |
+
# only query if necessary
|
| 1416 |
+
if columns <= 0 or lines <= 0:
|
| 1417 |
+
try:
|
| 1418 |
+
size = os.get_terminal_size(sys.__stdout__.fileno())
|
| 1419 |
+
except (AttributeError, ValueError, OSError):
|
| 1420 |
+
# stdout is None, closed, detached, or not a terminal, or
|
| 1421 |
+
# os.get_terminal_size() is unsupported
|
| 1422 |
+
size = os.terminal_size(fallback)
|
| 1423 |
+
if columns <= 0:
|
| 1424 |
+
columns = size.columns
|
| 1425 |
+
if lines <= 0:
|
| 1426 |
+
lines = size.lines
|
| 1427 |
+
|
| 1428 |
+
return os.terminal_size((columns, lines))
|
| 1429 |
+
|
| 1430 |
+
|
| 1431 |
+
# Check that a given file can be accessed with the correct mode.
|
| 1432 |
+
# Additionally check that `file` is not a directory, as on Windows
|
| 1433 |
+
# directories pass the os.access check.
|
| 1434 |
+
def _access_check(fn, mode):
|
| 1435 |
+
return (os.path.exists(fn) and os.access(fn, mode)
|
| 1436 |
+
and not os.path.isdir(fn))
|
| 1437 |
+
|
| 1438 |
+
|
| 1439 |
+
def which(cmd, mode=os.F_OK | os.X_OK, path=None):
|
| 1440 |
+
"""Given a command, mode, and a PATH string, return the path which
|
| 1441 |
+
conforms to the given mode on the PATH, or None if there is no such
|
| 1442 |
+
file.
|
| 1443 |
+
|
| 1444 |
+
`mode` defaults to os.F_OK | os.X_OK. `path` defaults to the result
|
| 1445 |
+
of os.environ.get("PATH"), or can be overridden with a custom search
|
| 1446 |
+
path.
|
| 1447 |
+
|
| 1448 |
+
"""
|
| 1449 |
+
# If we're given a path with a directory part, look it up directly rather
|
| 1450 |
+
# than referring to PATH directories. This includes checking relative to the
|
| 1451 |
+
# current directory, e.g. ./script
|
| 1452 |
+
if os.path.dirname(cmd):
|
| 1453 |
+
if _access_check(cmd, mode):
|
| 1454 |
+
return cmd
|
| 1455 |
+
return None
|
| 1456 |
+
|
| 1457 |
+
use_bytes = isinstance(cmd, bytes)
|
| 1458 |
+
|
| 1459 |
+
if path is None:
|
| 1460 |
+
path = os.environ.get("PATH", None)
|
| 1461 |
+
if path is None:
|
| 1462 |
+
try:
|
| 1463 |
+
path = os.confstr("CS_PATH")
|
| 1464 |
+
except (AttributeError, ValueError):
|
| 1465 |
+
# os.confstr() or CS_PATH is not available
|
| 1466 |
+
path = os.defpath
|
| 1467 |
+
# bpo-35755: Don't use os.defpath if the PATH environment variable is
|
| 1468 |
+
# set to an empty string
|
| 1469 |
+
|
| 1470 |
+
# PATH='' doesn't match, whereas PATH=':' looks in the current directory
|
| 1471 |
+
if not path:
|
| 1472 |
+
return None
|
| 1473 |
+
|
| 1474 |
+
if use_bytes:
|
| 1475 |
+
path = os.fsencode(path)
|
| 1476 |
+
path = path.split(os.fsencode(os.pathsep))
|
| 1477 |
+
else:
|
| 1478 |
+
path = os.fsdecode(path)
|
| 1479 |
+
path = path.split(os.pathsep)
|
| 1480 |
+
|
| 1481 |
+
if sys.platform == "win32":
|
| 1482 |
+
# The current directory takes precedence on Windows.
|
| 1483 |
+
curdir = os.curdir
|
| 1484 |
+
if use_bytes:
|
| 1485 |
+
curdir = os.fsencode(curdir)
|
| 1486 |
+
if curdir not in path:
|
| 1487 |
+
path.insert(0, curdir)
|
| 1488 |
+
|
| 1489 |
+
# PATHEXT is necessary to check on Windows.
|
| 1490 |
+
pathext_source = os.getenv("PATHEXT") or _WIN_DEFAULT_PATHEXT
|
| 1491 |
+
pathext = [ext for ext in pathext_source.split(os.pathsep) if ext]
|
| 1492 |
+
|
| 1493 |
+
if use_bytes:
|
| 1494 |
+
pathext = [os.fsencode(ext) for ext in pathext]
|
| 1495 |
+
# See if the given file matches any of the expected path extensions.
|
| 1496 |
+
# This will allow us to short circuit when given "python.exe".
|
| 1497 |
+
# If it does match, only test that one, otherwise we have to try
|
| 1498 |
+
# others.
|
| 1499 |
+
if any(cmd.lower().endswith(ext.lower()) for ext in pathext):
|
| 1500 |
+
files = [cmd]
|
| 1501 |
+
else:
|
| 1502 |
+
files = [cmd + ext for ext in pathext]
|
| 1503 |
+
else:
|
| 1504 |
+
# On other platforms you don't have things like PATHEXT to tell you
|
| 1505 |
+
# what file suffixes are executable, so just pass on cmd as-is.
|
| 1506 |
+
files = [cmd]
|
| 1507 |
+
|
| 1508 |
+
seen = set()
|
| 1509 |
+
for dir in path:
|
| 1510 |
+
normdir = os.path.normcase(dir)
|
| 1511 |
+
if not normdir in seen:
|
| 1512 |
+
seen.add(normdir)
|
| 1513 |
+
for thefile in files:
|
| 1514 |
+
name = os.path.join(dir, thefile)
|
| 1515 |
+
if _access_check(name, mode):
|
| 1516 |
+
return name
|
| 1517 |
+
return None
|
lib/python3.10/signal.py
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import _signal
|
| 2 |
+
from _signal import *
|
| 3 |
+
from enum import IntEnum as _IntEnum
|
| 4 |
+
|
| 5 |
+
_globals = globals()
|
| 6 |
+
|
| 7 |
+
_IntEnum._convert_(
|
| 8 |
+
'Signals', __name__,
|
| 9 |
+
lambda name:
|
| 10 |
+
name.isupper()
|
| 11 |
+
and (name.startswith('SIG') and not name.startswith('SIG_'))
|
| 12 |
+
or name.startswith('CTRL_'))
|
| 13 |
+
|
| 14 |
+
_IntEnum._convert_(
|
| 15 |
+
'Handlers', __name__,
|
| 16 |
+
lambda name: name in ('SIG_DFL', 'SIG_IGN'))
|
| 17 |
+
|
| 18 |
+
if 'pthread_sigmask' in _globals:
|
| 19 |
+
_IntEnum._convert_(
|
| 20 |
+
'Sigmasks', __name__,
|
| 21 |
+
lambda name: name in ('SIG_BLOCK', 'SIG_UNBLOCK', 'SIG_SETMASK'))
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
def _int_to_enum(value, enum_klass):
|
| 25 |
+
"""Convert a numeric value to an IntEnum member.
|
| 26 |
+
If it's not a known member, return the numeric value itself.
|
| 27 |
+
"""
|
| 28 |
+
try:
|
| 29 |
+
return enum_klass(value)
|
| 30 |
+
except ValueError:
|
| 31 |
+
return value
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def _enum_to_int(value):
|
| 35 |
+
"""Convert an IntEnum member to a numeric value.
|
| 36 |
+
If it's not an IntEnum member return the value itself.
|
| 37 |
+
"""
|
| 38 |
+
try:
|
| 39 |
+
return int(value)
|
| 40 |
+
except (ValueError, TypeError):
|
| 41 |
+
return value
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
# Similar to functools.wraps(), but only assign __doc__.
|
| 45 |
+
# __module__ should be preserved,
|
| 46 |
+
# __name__ and __qualname__ are already fine,
|
| 47 |
+
# __annotations__ is not set.
|
| 48 |
+
def _wraps(wrapped):
|
| 49 |
+
def decorator(wrapper):
|
| 50 |
+
wrapper.__doc__ = wrapped.__doc__
|
| 51 |
+
return wrapper
|
| 52 |
+
return decorator
|
| 53 |
+
|
| 54 |
+
@_wraps(_signal.signal)
|
| 55 |
+
def signal(signalnum, handler):
|
| 56 |
+
handler = _signal.signal(_enum_to_int(signalnum), _enum_to_int(handler))
|
| 57 |
+
return _int_to_enum(handler, Handlers)
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
@_wraps(_signal.getsignal)
|
| 61 |
+
def getsignal(signalnum):
|
| 62 |
+
handler = _signal.getsignal(signalnum)
|
| 63 |
+
return _int_to_enum(handler, Handlers)
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
if 'pthread_sigmask' in _globals:
|
| 67 |
+
@_wraps(_signal.pthread_sigmask)
|
| 68 |
+
def pthread_sigmask(how, mask):
|
| 69 |
+
sigs_set = _signal.pthread_sigmask(how, mask)
|
| 70 |
+
return set(_int_to_enum(x, Signals) for x in sigs_set)
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
if 'sigpending' in _globals:
|
| 74 |
+
@_wraps(_signal.sigpending)
|
| 75 |
+
def sigpending():
|
| 76 |
+
return {_int_to_enum(x, Signals) for x in _signal.sigpending()}
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
if 'sigwait' in _globals:
|
| 80 |
+
@_wraps(_signal.sigwait)
|
| 81 |
+
def sigwait(sigset):
|
| 82 |
+
retsig = _signal.sigwait(sigset)
|
| 83 |
+
return _int_to_enum(retsig, Signals)
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
if 'valid_signals' in _globals:
|
| 87 |
+
@_wraps(_signal.valid_signals)
|
| 88 |
+
def valid_signals():
|
| 89 |
+
return {_int_to_enum(x, Signals) for x in _signal.valid_signals()}
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
del _globals, _wraps
|
lib/python3.10/uu.py
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#! /usr/bin/env python3
|
| 2 |
+
|
| 3 |
+
# Copyright 1994 by Lance Ellinghouse
|
| 4 |
+
# Cathedral City, California Republic, United States of America.
|
| 5 |
+
# All Rights Reserved
|
| 6 |
+
# Permission to use, copy, modify, and distribute this software and its
|
| 7 |
+
# documentation for any purpose and without fee is hereby granted,
|
| 8 |
+
# provided that the above copyright notice appear in all copies and that
|
| 9 |
+
# both that copyright notice and this permission notice appear in
|
| 10 |
+
# supporting documentation, and that the name of Lance Ellinghouse
|
| 11 |
+
# not be used in advertising or publicity pertaining to distribution
|
| 12 |
+
# of the software without specific, written prior permission.
|
| 13 |
+
# LANCE ELLINGHOUSE DISCLAIMS ALL WARRANTIES WITH REGARD TO
|
| 14 |
+
# THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
| 15 |
+
# FITNESS, IN NO EVENT SHALL LANCE ELLINGHOUSE CENTRUM BE LIABLE
|
| 16 |
+
# FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
| 17 |
+
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
| 18 |
+
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
| 19 |
+
# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
| 20 |
+
#
|
| 21 |
+
# Modified by Jack Jansen, CWI, July 1995:
|
| 22 |
+
# - Use binascii module to do the actual line-by-line conversion
|
| 23 |
+
# between ascii and binary. This results in a 1000-fold speedup. The C
|
| 24 |
+
# version is still 5 times faster, though.
|
| 25 |
+
# - Arguments more compliant with python standard
|
| 26 |
+
|
| 27 |
+
"""Implementation of the UUencode and UUdecode functions.
|
| 28 |
+
|
| 29 |
+
encode(in_file, out_file [,name, mode], *, backtick=False)
|
| 30 |
+
decode(in_file [, out_file, mode, quiet])
|
| 31 |
+
"""
|
| 32 |
+
|
| 33 |
+
import binascii
|
| 34 |
+
import os
|
| 35 |
+
import sys
|
| 36 |
+
|
| 37 |
+
__all__ = ["Error", "encode", "decode"]
|
| 38 |
+
|
| 39 |
+
class Error(Exception):
|
| 40 |
+
pass
|
| 41 |
+
|
| 42 |
+
def encode(in_file, out_file, name=None, mode=None, *, backtick=False):
|
| 43 |
+
"""Uuencode file"""
|
| 44 |
+
#
|
| 45 |
+
# If in_file is a pathname open it and change defaults
|
| 46 |
+
#
|
| 47 |
+
opened_files = []
|
| 48 |
+
try:
|
| 49 |
+
if in_file == '-':
|
| 50 |
+
in_file = sys.stdin.buffer
|
| 51 |
+
elif isinstance(in_file, str):
|
| 52 |
+
if name is None:
|
| 53 |
+
name = os.path.basename(in_file)
|
| 54 |
+
if mode is None:
|
| 55 |
+
try:
|
| 56 |
+
mode = os.stat(in_file).st_mode
|
| 57 |
+
except AttributeError:
|
| 58 |
+
pass
|
| 59 |
+
in_file = open(in_file, 'rb')
|
| 60 |
+
opened_files.append(in_file)
|
| 61 |
+
#
|
| 62 |
+
# Open out_file if it is a pathname
|
| 63 |
+
#
|
| 64 |
+
if out_file == '-':
|
| 65 |
+
out_file = sys.stdout.buffer
|
| 66 |
+
elif isinstance(out_file, str):
|
| 67 |
+
out_file = open(out_file, 'wb')
|
| 68 |
+
opened_files.append(out_file)
|
| 69 |
+
#
|
| 70 |
+
# Set defaults for name and mode
|
| 71 |
+
#
|
| 72 |
+
if name is None:
|
| 73 |
+
name = '-'
|
| 74 |
+
if mode is None:
|
| 75 |
+
mode = 0o666
|
| 76 |
+
|
| 77 |
+
#
|
| 78 |
+
# Remove newline chars from name
|
| 79 |
+
#
|
| 80 |
+
name = name.replace('\n','\\n')
|
| 81 |
+
name = name.replace('\r','\\r')
|
| 82 |
+
|
| 83 |
+
#
|
| 84 |
+
# Write the data
|
| 85 |
+
#
|
| 86 |
+
out_file.write(('begin %o %s\n' % ((mode & 0o777), name)).encode("ascii"))
|
| 87 |
+
data = in_file.read(45)
|
| 88 |
+
while len(data) > 0:
|
| 89 |
+
out_file.write(binascii.b2a_uu(data, backtick=backtick))
|
| 90 |
+
data = in_file.read(45)
|
| 91 |
+
if backtick:
|
| 92 |
+
out_file.write(b'`\nend\n')
|
| 93 |
+
else:
|
| 94 |
+
out_file.write(b' \nend\n')
|
| 95 |
+
finally:
|
| 96 |
+
for f in opened_files:
|
| 97 |
+
f.close()
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
def decode(in_file, out_file=None, mode=None, quiet=False):
|
| 101 |
+
"""Decode uuencoded file"""
|
| 102 |
+
#
|
| 103 |
+
# Open the input file, if needed.
|
| 104 |
+
#
|
| 105 |
+
opened_files = []
|
| 106 |
+
if in_file == '-':
|
| 107 |
+
in_file = sys.stdin.buffer
|
| 108 |
+
elif isinstance(in_file, str):
|
| 109 |
+
in_file = open(in_file, 'rb')
|
| 110 |
+
opened_files.append(in_file)
|
| 111 |
+
|
| 112 |
+
try:
|
| 113 |
+
#
|
| 114 |
+
# Read until a begin is encountered or we've exhausted the file
|
| 115 |
+
#
|
| 116 |
+
while True:
|
| 117 |
+
hdr = in_file.readline()
|
| 118 |
+
if not hdr:
|
| 119 |
+
raise Error('No valid begin line found in input file')
|
| 120 |
+
if not hdr.startswith(b'begin'):
|
| 121 |
+
continue
|
| 122 |
+
hdrfields = hdr.split(b' ', 2)
|
| 123 |
+
if len(hdrfields) == 3 and hdrfields[0] == b'begin':
|
| 124 |
+
try:
|
| 125 |
+
int(hdrfields[1], 8)
|
| 126 |
+
break
|
| 127 |
+
except ValueError:
|
| 128 |
+
pass
|
| 129 |
+
if out_file is None:
|
| 130 |
+
# If the filename isn't ASCII, what's up with that?!?
|
| 131 |
+
out_file = hdrfields[2].rstrip(b' \t\r\n\f').decode("ascii")
|
| 132 |
+
if os.path.exists(out_file):
|
| 133 |
+
raise Error(f'Cannot overwrite existing file: {out_file}')
|
| 134 |
+
if (out_file.startswith(os.sep) or
|
| 135 |
+
f'..{os.sep}' in out_file or (
|
| 136 |
+
os.altsep and
|
| 137 |
+
(out_file.startswith(os.altsep) or
|
| 138 |
+
f'..{os.altsep}' in out_file))
|
| 139 |
+
):
|
| 140 |
+
raise Error(f'Refusing to write to {out_file} due to directory traversal')
|
| 141 |
+
if mode is None:
|
| 142 |
+
mode = int(hdrfields[1], 8)
|
| 143 |
+
#
|
| 144 |
+
# Open the output file
|
| 145 |
+
#
|
| 146 |
+
if out_file == '-':
|
| 147 |
+
out_file = sys.stdout.buffer
|
| 148 |
+
elif isinstance(out_file, str):
|
| 149 |
+
fp = open(out_file, 'wb')
|
| 150 |
+
os.chmod(out_file, mode)
|
| 151 |
+
out_file = fp
|
| 152 |
+
opened_files.append(out_file)
|
| 153 |
+
#
|
| 154 |
+
# Main decoding loop
|
| 155 |
+
#
|
| 156 |
+
s = in_file.readline()
|
| 157 |
+
while s and s.strip(b' \t\r\n\f') != b'end':
|
| 158 |
+
try:
|
| 159 |
+
data = binascii.a2b_uu(s)
|
| 160 |
+
except binascii.Error as v:
|
| 161 |
+
# Workaround for broken uuencoders by /Fredrik Lundh
|
| 162 |
+
nbytes = (((s[0]-32) & 63) * 4 + 5) // 3
|
| 163 |
+
data = binascii.a2b_uu(s[:nbytes])
|
| 164 |
+
if not quiet:
|
| 165 |
+
sys.stderr.write("Warning: %s\n" % v)
|
| 166 |
+
out_file.write(data)
|
| 167 |
+
s = in_file.readline()
|
| 168 |
+
if not s:
|
| 169 |
+
raise Error('Truncated input file')
|
| 170 |
+
finally:
|
| 171 |
+
for f in opened_files:
|
| 172 |
+
f.close()
|
| 173 |
+
|
| 174 |
+
def test():
|
| 175 |
+
"""uuencode/uudecode main program"""
|
| 176 |
+
|
| 177 |
+
import optparse
|
| 178 |
+
parser = optparse.OptionParser(usage='usage: %prog [-d] [-t] [input [output]]')
|
| 179 |
+
parser.add_option('-d', '--decode', dest='decode', help='Decode (instead of encode)?', default=False, action='store_true')
|
| 180 |
+
parser.add_option('-t', '--text', dest='text', help='data is text, encoded format unix-compatible text?', default=False, action='store_true')
|
| 181 |
+
|
| 182 |
+
(options, args) = parser.parse_args()
|
| 183 |
+
if len(args) > 2:
|
| 184 |
+
parser.error('incorrect number of arguments')
|
| 185 |
+
sys.exit(1)
|
| 186 |
+
|
| 187 |
+
# Use the binary streams underlying stdin/stdout
|
| 188 |
+
input = sys.stdin.buffer
|
| 189 |
+
output = sys.stdout.buffer
|
| 190 |
+
if len(args) > 0:
|
| 191 |
+
input = args[0]
|
| 192 |
+
if len(args) > 1:
|
| 193 |
+
output = args[1]
|
| 194 |
+
|
| 195 |
+
if options.decode:
|
| 196 |
+
if options.text:
|
| 197 |
+
if isinstance(output, str):
|
| 198 |
+
output = open(output, 'wb')
|
| 199 |
+
else:
|
| 200 |
+
print(sys.argv[0], ': cannot do -t to stdout')
|
| 201 |
+
sys.exit(1)
|
| 202 |
+
decode(input, output)
|
| 203 |
+
else:
|
| 204 |
+
if options.text:
|
| 205 |
+
if isinstance(input, str):
|
| 206 |
+
input = open(input, 'rb')
|
| 207 |
+
else:
|
| 208 |
+
print(sys.argv[0], ': cannot do -t from stdin')
|
| 209 |
+
sys.exit(1)
|
| 210 |
+
encode(input, output)
|
| 211 |
+
|
| 212 |
+
if __name__ == '__main__':
|
| 213 |
+
test()
|
lib/python3.10/uuid.py
ADDED
|
@@ -0,0 +1,733 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
r"""UUID objects (universally unique identifiers) according to RFC 4122.
|
| 2 |
+
|
| 3 |
+
This module provides immutable UUID objects (class UUID) and the functions
|
| 4 |
+
uuid1(), uuid3(), uuid4(), uuid5() for generating version 1, 3, 4, and 5
|
| 5 |
+
UUIDs as specified in RFC 4122.
|
| 6 |
+
|
| 7 |
+
If all you want is a unique ID, you should probably call uuid1() or uuid4().
|
| 8 |
+
Note that uuid1() may compromise privacy since it creates a UUID containing
|
| 9 |
+
the computer's network address. uuid4() creates a random UUID.
|
| 10 |
+
|
| 11 |
+
Typical usage:
|
| 12 |
+
|
| 13 |
+
>>> import uuid
|
| 14 |
+
|
| 15 |
+
# make a UUID based on the host ID and current time
|
| 16 |
+
>>> uuid.uuid1() # doctest: +SKIP
|
| 17 |
+
UUID('a8098c1a-f86e-11da-bd1a-00112444be1e')
|
| 18 |
+
|
| 19 |
+
# make a UUID using an MD5 hash of a namespace UUID and a name
|
| 20 |
+
>>> uuid.uuid3(uuid.NAMESPACE_DNS, 'python.org')
|
| 21 |
+
UUID('6fa459ea-ee8a-3ca4-894e-db77e160355e')
|
| 22 |
+
|
| 23 |
+
# make a random UUID
|
| 24 |
+
>>> uuid.uuid4() # doctest: +SKIP
|
| 25 |
+
UUID('16fd2706-8baf-433b-82eb-8c7fada847da')
|
| 26 |
+
|
| 27 |
+
# make a UUID using a SHA-1 hash of a namespace UUID and a name
|
| 28 |
+
>>> uuid.uuid5(uuid.NAMESPACE_DNS, 'python.org')
|
| 29 |
+
UUID('886313e1-3b8a-5372-9b90-0c9aee199e5d')
|
| 30 |
+
|
| 31 |
+
# make a UUID from a string of hex digits (braces and hyphens ignored)
|
| 32 |
+
>>> x = uuid.UUID('{00010203-0405-0607-0809-0a0b0c0d0e0f}')
|
| 33 |
+
|
| 34 |
+
# convert a UUID to a string of hex digits in standard form
|
| 35 |
+
>>> str(x)
|
| 36 |
+
'00010203-0405-0607-0809-0a0b0c0d0e0f'
|
| 37 |
+
|
| 38 |
+
# get the raw 16 bytes of the UUID
|
| 39 |
+
>>> x.bytes
|
| 40 |
+
b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f'
|
| 41 |
+
|
| 42 |
+
# make a UUID from a 16-byte string
|
| 43 |
+
>>> uuid.UUID(bytes=x.bytes)
|
| 44 |
+
UUID('00010203-0405-0607-0809-0a0b0c0d0e0f')
|
| 45 |
+
"""
|
| 46 |
+
|
| 47 |
+
import os
|
| 48 |
+
import sys
|
| 49 |
+
|
| 50 |
+
from enum import Enum
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
__author__ = 'Ka-Ping Yee <ping@zesty.ca>'
|
| 54 |
+
|
| 55 |
+
# The recognized platforms - known behaviors
|
| 56 |
+
if sys.platform in ('win32', 'darwin'):
|
| 57 |
+
_AIX = _LINUX = False
|
| 58 |
+
else:
|
| 59 |
+
import platform
|
| 60 |
+
_platform_system = platform.system()
|
| 61 |
+
_AIX = _platform_system == 'AIX'
|
| 62 |
+
_LINUX = _platform_system == 'Linux'
|
| 63 |
+
|
| 64 |
+
_MAC_DELIM = b':'
|
| 65 |
+
_MAC_OMITS_LEADING_ZEROES = False
|
| 66 |
+
if _AIX:
|
| 67 |
+
_MAC_DELIM = b'.'
|
| 68 |
+
_MAC_OMITS_LEADING_ZEROES = True
|
| 69 |
+
|
| 70 |
+
RESERVED_NCS, RFC_4122, RESERVED_MICROSOFT, RESERVED_FUTURE = [
|
| 71 |
+
'reserved for NCS compatibility', 'specified in RFC 4122',
|
| 72 |
+
'reserved for Microsoft compatibility', 'reserved for future definition']
|
| 73 |
+
|
| 74 |
+
int_ = int # The built-in int type
|
| 75 |
+
bytes_ = bytes # The built-in bytes type
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
class SafeUUID(Enum):
|
| 79 |
+
safe = 0
|
| 80 |
+
unsafe = -1
|
| 81 |
+
unknown = None
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
class UUID:
|
| 85 |
+
"""Instances of the UUID class represent UUIDs as specified in RFC 4122.
|
| 86 |
+
UUID objects are immutable, hashable, and usable as dictionary keys.
|
| 87 |
+
Converting a UUID to a string with str() yields something in the form
|
| 88 |
+
'12345678-1234-1234-1234-123456789abc'. The UUID constructor accepts
|
| 89 |
+
five possible forms: a similar string of hexadecimal digits, or a tuple
|
| 90 |
+
of six integer fields (with 32-bit, 16-bit, 16-bit, 8-bit, 8-bit, and
|
| 91 |
+
48-bit values respectively) as an argument named 'fields', or a string
|
| 92 |
+
of 16 bytes (with all the integer fields in big-endian order) as an
|
| 93 |
+
argument named 'bytes', or a string of 16 bytes (with the first three
|
| 94 |
+
fields in little-endian order) as an argument named 'bytes_le', or a
|
| 95 |
+
single 128-bit integer as an argument named 'int'.
|
| 96 |
+
|
| 97 |
+
UUIDs have these read-only attributes:
|
| 98 |
+
|
| 99 |
+
bytes the UUID as a 16-byte string (containing the six
|
| 100 |
+
integer fields in big-endian byte order)
|
| 101 |
+
|
| 102 |
+
bytes_le the UUID as a 16-byte string (with time_low, time_mid,
|
| 103 |
+
and time_hi_version in little-endian byte order)
|
| 104 |
+
|
| 105 |
+
fields a tuple of the six integer fields of the UUID,
|
| 106 |
+
which are also available as six individual attributes
|
| 107 |
+
and two derived attributes:
|
| 108 |
+
|
| 109 |
+
time_low the first 32 bits of the UUID
|
| 110 |
+
time_mid the next 16 bits of the UUID
|
| 111 |
+
time_hi_version the next 16 bits of the UUID
|
| 112 |
+
clock_seq_hi_variant the next 8 bits of the UUID
|
| 113 |
+
clock_seq_low the next 8 bits of the UUID
|
| 114 |
+
node the last 48 bits of the UUID
|
| 115 |
+
|
| 116 |
+
time the 60-bit timestamp
|
| 117 |
+
clock_seq the 14-bit sequence number
|
| 118 |
+
|
| 119 |
+
hex the UUID as a 32-character hexadecimal string
|
| 120 |
+
|
| 121 |
+
int the UUID as a 128-bit integer
|
| 122 |
+
|
| 123 |
+
urn the UUID as a URN as specified in RFC 4122
|
| 124 |
+
|
| 125 |
+
variant the UUID variant (one of the constants RESERVED_NCS,
|
| 126 |
+
RFC_4122, RESERVED_MICROSOFT, or RESERVED_FUTURE)
|
| 127 |
+
|
| 128 |
+
version the UUID version number (1 through 5, meaningful only
|
| 129 |
+
when the variant is RFC_4122)
|
| 130 |
+
|
| 131 |
+
is_safe An enum indicating whether the UUID has been generated in
|
| 132 |
+
a way that is safe for multiprocessing applications, via
|
| 133 |
+
uuid_generate_time_safe(3).
|
| 134 |
+
"""
|
| 135 |
+
|
| 136 |
+
__slots__ = ('int', 'is_safe', '__weakref__')
|
| 137 |
+
|
| 138 |
+
def __init__(self, hex=None, bytes=None, bytes_le=None, fields=None,
|
| 139 |
+
int=None, version=None,
|
| 140 |
+
*, is_safe=SafeUUID.unknown):
|
| 141 |
+
r"""Create a UUID from either a string of 32 hexadecimal digits,
|
| 142 |
+
a string of 16 bytes as the 'bytes' argument, a string of 16 bytes
|
| 143 |
+
in little-endian order as the 'bytes_le' argument, a tuple of six
|
| 144 |
+
integers (32-bit time_low, 16-bit time_mid, 16-bit time_hi_version,
|
| 145 |
+
8-bit clock_seq_hi_variant, 8-bit clock_seq_low, 48-bit node) as
|
| 146 |
+
the 'fields' argument, or a single 128-bit integer as the 'int'
|
| 147 |
+
argument. When a string of hex digits is given, curly braces,
|
| 148 |
+
hyphens, and a URN prefix are all optional. For example, these
|
| 149 |
+
expressions all yield the same UUID:
|
| 150 |
+
|
| 151 |
+
UUID('{12345678-1234-5678-1234-567812345678}')
|
| 152 |
+
UUID('12345678123456781234567812345678')
|
| 153 |
+
UUID('urn:uuid:12345678-1234-5678-1234-567812345678')
|
| 154 |
+
UUID(bytes='\x12\x34\x56\x78'*4)
|
| 155 |
+
UUID(bytes_le='\x78\x56\x34\x12\x34\x12\x78\x56' +
|
| 156 |
+
'\x12\x34\x56\x78\x12\x34\x56\x78')
|
| 157 |
+
UUID(fields=(0x12345678, 0x1234, 0x5678, 0x12, 0x34, 0x567812345678))
|
| 158 |
+
UUID(int=0x12345678123456781234567812345678)
|
| 159 |
+
|
| 160 |
+
Exactly one of 'hex', 'bytes', 'bytes_le', 'fields', or 'int' must
|
| 161 |
+
be given. The 'version' argument is optional; if given, the resulting
|
| 162 |
+
UUID will have its variant and version set according to RFC 4122,
|
| 163 |
+
overriding the given 'hex', 'bytes', 'bytes_le', 'fields', or 'int'.
|
| 164 |
+
|
| 165 |
+
is_safe is an enum exposed as an attribute on the instance. It
|
| 166 |
+
indicates whether the UUID has been generated in a way that is safe
|
| 167 |
+
for multiprocessing applications, via uuid_generate_time_safe(3).
|
| 168 |
+
"""
|
| 169 |
+
|
| 170 |
+
if [hex, bytes, bytes_le, fields, int].count(None) != 4:
|
| 171 |
+
raise TypeError('one of the hex, bytes, bytes_le, fields, '
|
| 172 |
+
'or int arguments must be given')
|
| 173 |
+
if hex is not None:
|
| 174 |
+
hex = hex.replace('urn:', '').replace('uuid:', '')
|
| 175 |
+
hex = hex.strip('{}').replace('-', '')
|
| 176 |
+
if len(hex) != 32:
|
| 177 |
+
raise ValueError('badly formed hexadecimal UUID string')
|
| 178 |
+
int = int_(hex, 16)
|
| 179 |
+
if bytes_le is not None:
|
| 180 |
+
if len(bytes_le) != 16:
|
| 181 |
+
raise ValueError('bytes_le is not a 16-char string')
|
| 182 |
+
bytes = (bytes_le[4-1::-1] + bytes_le[6-1:4-1:-1] +
|
| 183 |
+
bytes_le[8-1:6-1:-1] + bytes_le[8:])
|
| 184 |
+
if bytes is not None:
|
| 185 |
+
if len(bytes) != 16:
|
| 186 |
+
raise ValueError('bytes is not a 16-char string')
|
| 187 |
+
assert isinstance(bytes, bytes_), repr(bytes)
|
| 188 |
+
int = int_.from_bytes(bytes, byteorder='big')
|
| 189 |
+
if fields is not None:
|
| 190 |
+
if len(fields) != 6:
|
| 191 |
+
raise ValueError('fields is not a 6-tuple')
|
| 192 |
+
(time_low, time_mid, time_hi_version,
|
| 193 |
+
clock_seq_hi_variant, clock_seq_low, node) = fields
|
| 194 |
+
if not 0 <= time_low < 1<<32:
|
| 195 |
+
raise ValueError('field 1 out of range (need a 32-bit value)')
|
| 196 |
+
if not 0 <= time_mid < 1<<16:
|
| 197 |
+
raise ValueError('field 2 out of range (need a 16-bit value)')
|
| 198 |
+
if not 0 <= time_hi_version < 1<<16:
|
| 199 |
+
raise ValueError('field 3 out of range (need a 16-bit value)')
|
| 200 |
+
if not 0 <= clock_seq_hi_variant < 1<<8:
|
| 201 |
+
raise ValueError('field 4 out of range (need an 8-bit value)')
|
| 202 |
+
if not 0 <= clock_seq_low < 1<<8:
|
| 203 |
+
raise ValueError('field 5 out of range (need an 8-bit value)')
|
| 204 |
+
if not 0 <= node < 1<<48:
|
| 205 |
+
raise ValueError('field 6 out of range (need a 48-bit value)')
|
| 206 |
+
clock_seq = (clock_seq_hi_variant << 8) | clock_seq_low
|
| 207 |
+
int = ((time_low << 96) | (time_mid << 80) |
|
| 208 |
+
(time_hi_version << 64) | (clock_seq << 48) | node)
|
| 209 |
+
if int is not None:
|
| 210 |
+
if not 0 <= int < 1<<128:
|
| 211 |
+
raise ValueError('int is out of range (need a 128-bit value)')
|
| 212 |
+
if version is not None:
|
| 213 |
+
if not 1 <= version <= 5:
|
| 214 |
+
raise ValueError('illegal version number')
|
| 215 |
+
# Set the variant to RFC 4122.
|
| 216 |
+
int &= ~(0xc000 << 48)
|
| 217 |
+
int |= 0x8000 << 48
|
| 218 |
+
# Set the version number.
|
| 219 |
+
int &= ~(0xf000 << 64)
|
| 220 |
+
int |= version << 76
|
| 221 |
+
object.__setattr__(self, 'int', int)
|
| 222 |
+
object.__setattr__(self, 'is_safe', is_safe)
|
| 223 |
+
|
| 224 |
+
def __getstate__(self):
|
| 225 |
+
d = {'int': self.int}
|
| 226 |
+
if self.is_safe != SafeUUID.unknown:
|
| 227 |
+
# is_safe is a SafeUUID instance. Return just its value, so that
|
| 228 |
+
# it can be un-pickled in older Python versions without SafeUUID.
|
| 229 |
+
d['is_safe'] = self.is_safe.value
|
| 230 |
+
return d
|
| 231 |
+
|
| 232 |
+
def __setstate__(self, state):
|
| 233 |
+
object.__setattr__(self, 'int', state['int'])
|
| 234 |
+
# is_safe was added in 3.7; it is also omitted when it is "unknown"
|
| 235 |
+
object.__setattr__(self, 'is_safe',
|
| 236 |
+
SafeUUID(state['is_safe'])
|
| 237 |
+
if 'is_safe' in state else SafeUUID.unknown)
|
| 238 |
+
|
| 239 |
+
def __eq__(self, other):
|
| 240 |
+
if isinstance(other, UUID):
|
| 241 |
+
return self.int == other.int
|
| 242 |
+
return NotImplemented
|
| 243 |
+
|
| 244 |
+
# Q. What's the value of being able to sort UUIDs?
|
| 245 |
+
# A. Use them as keys in a B-Tree or similar mapping.
|
| 246 |
+
|
| 247 |
+
def __lt__(self, other):
|
| 248 |
+
if isinstance(other, UUID):
|
| 249 |
+
return self.int < other.int
|
| 250 |
+
return NotImplemented
|
| 251 |
+
|
| 252 |
+
def __gt__(self, other):
|
| 253 |
+
if isinstance(other, UUID):
|
| 254 |
+
return self.int > other.int
|
| 255 |
+
return NotImplemented
|
| 256 |
+
|
| 257 |
+
def __le__(self, other):
|
| 258 |
+
if isinstance(other, UUID):
|
| 259 |
+
return self.int <= other.int
|
| 260 |
+
return NotImplemented
|
| 261 |
+
|
| 262 |
+
def __ge__(self, other):
|
| 263 |
+
if isinstance(other, UUID):
|
| 264 |
+
return self.int >= other.int
|
| 265 |
+
return NotImplemented
|
| 266 |
+
|
| 267 |
+
def __hash__(self):
|
| 268 |
+
return hash(self.int)
|
| 269 |
+
|
| 270 |
+
def __int__(self):
|
| 271 |
+
return self.int
|
| 272 |
+
|
| 273 |
+
def __repr__(self):
|
| 274 |
+
return '%s(%r)' % (self.__class__.__name__, str(self))
|
| 275 |
+
|
| 276 |
+
def __setattr__(self, name, value):
|
| 277 |
+
raise TypeError('UUID objects are immutable')
|
| 278 |
+
|
| 279 |
+
def __str__(self):
|
| 280 |
+
hex = '%032x' % self.int
|
| 281 |
+
return '%s-%s-%s-%s-%s' % (
|
| 282 |
+
hex[:8], hex[8:12], hex[12:16], hex[16:20], hex[20:])
|
| 283 |
+
|
| 284 |
+
@property
|
| 285 |
+
def bytes(self):
|
| 286 |
+
return self.int.to_bytes(16, 'big')
|
| 287 |
+
|
| 288 |
+
@property
|
| 289 |
+
def bytes_le(self):
|
| 290 |
+
bytes = self.bytes
|
| 291 |
+
return (bytes[4-1::-1] + bytes[6-1:4-1:-1] + bytes[8-1:6-1:-1] +
|
| 292 |
+
bytes[8:])
|
| 293 |
+
|
| 294 |
+
@property
|
| 295 |
+
def fields(self):
|
| 296 |
+
return (self.time_low, self.time_mid, self.time_hi_version,
|
| 297 |
+
self.clock_seq_hi_variant, self.clock_seq_low, self.node)
|
| 298 |
+
|
| 299 |
+
@property
|
| 300 |
+
def time_low(self):
|
| 301 |
+
return self.int >> 96
|
| 302 |
+
|
| 303 |
+
@property
|
| 304 |
+
def time_mid(self):
|
| 305 |
+
return (self.int >> 80) & 0xffff
|
| 306 |
+
|
| 307 |
+
@property
|
| 308 |
+
def time_hi_version(self):
|
| 309 |
+
return (self.int >> 64) & 0xffff
|
| 310 |
+
|
| 311 |
+
@property
|
| 312 |
+
def clock_seq_hi_variant(self):
|
| 313 |
+
return (self.int >> 56) & 0xff
|
| 314 |
+
|
| 315 |
+
@property
|
| 316 |
+
def clock_seq_low(self):
|
| 317 |
+
return (self.int >> 48) & 0xff
|
| 318 |
+
|
| 319 |
+
@property
|
| 320 |
+
def time(self):
|
| 321 |
+
return (((self.time_hi_version & 0x0fff) << 48) |
|
| 322 |
+
(self.time_mid << 32) | self.time_low)
|
| 323 |
+
|
| 324 |
+
@property
|
| 325 |
+
def clock_seq(self):
|
| 326 |
+
return (((self.clock_seq_hi_variant & 0x3f) << 8) |
|
| 327 |
+
self.clock_seq_low)
|
| 328 |
+
|
| 329 |
+
@property
|
| 330 |
+
def node(self):
|
| 331 |
+
return self.int & 0xffffffffffff
|
| 332 |
+
|
| 333 |
+
@property
|
| 334 |
+
def hex(self):
|
| 335 |
+
return '%032x' % self.int
|
| 336 |
+
|
| 337 |
+
@property
|
| 338 |
+
def urn(self):
|
| 339 |
+
return 'urn:uuid:' + str(self)
|
| 340 |
+
|
| 341 |
+
@property
|
| 342 |
+
def variant(self):
|
| 343 |
+
if not self.int & (0x8000 << 48):
|
| 344 |
+
return RESERVED_NCS
|
| 345 |
+
elif not self.int & (0x4000 << 48):
|
| 346 |
+
return RFC_4122
|
| 347 |
+
elif not self.int & (0x2000 << 48):
|
| 348 |
+
return RESERVED_MICROSOFT
|
| 349 |
+
else:
|
| 350 |
+
return RESERVED_FUTURE
|
| 351 |
+
|
| 352 |
+
@property
|
| 353 |
+
def version(self):
|
| 354 |
+
# The version bits are only meaningful for RFC 4122 UUIDs.
|
| 355 |
+
if self.variant == RFC_4122:
|
| 356 |
+
return int((self.int >> 76) & 0xf)
|
| 357 |
+
|
| 358 |
+
|
| 359 |
+
def _get_command_stdout(command, *args):
|
| 360 |
+
import io, os, shutil, subprocess
|
| 361 |
+
|
| 362 |
+
try:
|
| 363 |
+
path_dirs = os.environ.get('PATH', os.defpath).split(os.pathsep)
|
| 364 |
+
path_dirs.extend(['/sbin', '/usr/sbin'])
|
| 365 |
+
executable = shutil.which(command, path=os.pathsep.join(path_dirs))
|
| 366 |
+
if executable is None:
|
| 367 |
+
return None
|
| 368 |
+
# LC_ALL=C to ensure English output, stderr=DEVNULL to prevent output
|
| 369 |
+
# on stderr (Note: we don't have an example where the words we search
|
| 370 |
+
# for are actually localized, but in theory some system could do so.)
|
| 371 |
+
env = dict(os.environ)
|
| 372 |
+
env['LC_ALL'] = 'C'
|
| 373 |
+
# Empty strings will be quoted by popen so we should just ommit it
|
| 374 |
+
if args != ('',):
|
| 375 |
+
command = (executable, *args)
|
| 376 |
+
else:
|
| 377 |
+
command = (executable,)
|
| 378 |
+
proc = subprocess.Popen(command,
|
| 379 |
+
stdout=subprocess.PIPE,
|
| 380 |
+
stderr=subprocess.DEVNULL,
|
| 381 |
+
env=env)
|
| 382 |
+
if not proc:
|
| 383 |
+
return None
|
| 384 |
+
stdout, stderr = proc.communicate()
|
| 385 |
+
return io.BytesIO(stdout)
|
| 386 |
+
except (OSError, subprocess.SubprocessError):
|
| 387 |
+
return None
|
| 388 |
+
|
| 389 |
+
|
| 390 |
+
# For MAC (a.k.a. IEEE 802, or EUI-48) addresses, the second least significant
|
| 391 |
+
# bit of the first octet signifies whether the MAC address is universally (0)
|
| 392 |
+
# or locally (1) administered. Network cards from hardware manufacturers will
|
| 393 |
+
# always be universally administered to guarantee global uniqueness of the MAC
|
| 394 |
+
# address, but any particular machine may have other interfaces which are
|
| 395 |
+
# locally administered. An example of the latter is the bridge interface to
|
| 396 |
+
# the Touch Bar on MacBook Pros.
|
| 397 |
+
#
|
| 398 |
+
# This bit works out to be the 42nd bit counting from 1 being the least
|
| 399 |
+
# significant, or 1<<41. We'll prefer universally administered MAC addresses
|
| 400 |
+
# over locally administered ones since the former are globally unique, but
|
| 401 |
+
# we'll return the first of the latter found if that's all the machine has.
|
| 402 |
+
#
|
| 403 |
+
# See https://en.wikipedia.org/wiki/MAC_address#Universal_vs._local
|
| 404 |
+
|
| 405 |
+
def _is_universal(mac):
|
| 406 |
+
return not (mac & (1 << 41))
|
| 407 |
+
|
| 408 |
+
|
| 409 |
+
def _find_mac_near_keyword(command, args, keywords, get_word_index):
|
| 410 |
+
"""Searches a command's output for a MAC address near a keyword.
|
| 411 |
+
|
| 412 |
+
Each line of words in the output is case-insensitively searched for
|
| 413 |
+
any of the given keywords. Upon a match, get_word_index is invoked
|
| 414 |
+
to pick a word from the line, given the index of the match. For
|
| 415 |
+
example, lambda i: 0 would get the first word on the line, while
|
| 416 |
+
lambda i: i - 1 would get the word preceding the keyword.
|
| 417 |
+
"""
|
| 418 |
+
stdout = _get_command_stdout(command, args)
|
| 419 |
+
if stdout is None:
|
| 420 |
+
return None
|
| 421 |
+
|
| 422 |
+
first_local_mac = None
|
| 423 |
+
for line in stdout:
|
| 424 |
+
words = line.lower().rstrip().split()
|
| 425 |
+
for i in range(len(words)):
|
| 426 |
+
if words[i] in keywords:
|
| 427 |
+
try:
|
| 428 |
+
word = words[get_word_index(i)]
|
| 429 |
+
mac = int(word.replace(_MAC_DELIM, b''), 16)
|
| 430 |
+
except (ValueError, IndexError):
|
| 431 |
+
# Virtual interfaces, such as those provided by
|
| 432 |
+
# VPNs, do not have a colon-delimited MAC address
|
| 433 |
+
# as expected, but a 16-byte HWAddr separated by
|
| 434 |
+
# dashes. These should be ignored in favor of a
|
| 435 |
+
# real MAC address
|
| 436 |
+
pass
|
| 437 |
+
else:
|
| 438 |
+
if _is_universal(mac):
|
| 439 |
+
return mac
|
| 440 |
+
first_local_mac = first_local_mac or mac
|
| 441 |
+
return first_local_mac or None
|
| 442 |
+
|
| 443 |
+
|
| 444 |
+
def _parse_mac(word):
|
| 445 |
+
# Accept 'HH:HH:HH:HH:HH:HH' MAC address (ex: '52:54:00:9d:0e:67'),
|
| 446 |
+
# but reject IPv6 address (ex: 'fe80::5054:ff:fe9' or '123:2:3:4:5:6:7:8').
|
| 447 |
+
#
|
| 448 |
+
# Virtual interfaces, such as those provided by VPNs, do not have a
|
| 449 |
+
# colon-delimited MAC address as expected, but a 16-byte HWAddr separated
|
| 450 |
+
# by dashes. These should be ignored in favor of a real MAC address
|
| 451 |
+
parts = word.split(_MAC_DELIM)
|
| 452 |
+
if len(parts) != 6:
|
| 453 |
+
return
|
| 454 |
+
if _MAC_OMITS_LEADING_ZEROES:
|
| 455 |
+
# (Only) on AIX the macaddr value given is not prefixed by 0, e.g.
|
| 456 |
+
# en0 1500 link#2 fa.bc.de.f7.62.4 110854824 0 160133733 0 0
|
| 457 |
+
# not
|
| 458 |
+
# en0 1500 link#2 fa.bc.de.f7.62.04 110854824 0 160133733 0 0
|
| 459 |
+
if not all(1 <= len(part) <= 2 for part in parts):
|
| 460 |
+
return
|
| 461 |
+
hexstr = b''.join(part.rjust(2, b'0') for part in parts)
|
| 462 |
+
else:
|
| 463 |
+
if not all(len(part) == 2 for part in parts):
|
| 464 |
+
return
|
| 465 |
+
hexstr = b''.join(parts)
|
| 466 |
+
try:
|
| 467 |
+
return int(hexstr, 16)
|
| 468 |
+
except ValueError:
|
| 469 |
+
return
|
| 470 |
+
|
| 471 |
+
|
| 472 |
+
def _find_mac_under_heading(command, args, heading):
|
| 473 |
+
"""Looks for a MAC address under a heading in a command's output.
|
| 474 |
+
|
| 475 |
+
The first line of words in the output is searched for the given
|
| 476 |
+
heading. Words at the same word index as the heading in subsequent
|
| 477 |
+
lines are then examined to see if they look like MAC addresses.
|
| 478 |
+
"""
|
| 479 |
+
stdout = _get_command_stdout(command, args)
|
| 480 |
+
if stdout is None:
|
| 481 |
+
return None
|
| 482 |
+
|
| 483 |
+
keywords = stdout.readline().rstrip().split()
|
| 484 |
+
try:
|
| 485 |
+
column_index = keywords.index(heading)
|
| 486 |
+
except ValueError:
|
| 487 |
+
return None
|
| 488 |
+
|
| 489 |
+
first_local_mac = None
|
| 490 |
+
for line in stdout:
|
| 491 |
+
words = line.rstrip().split()
|
| 492 |
+
try:
|
| 493 |
+
word = words[column_index]
|
| 494 |
+
except IndexError:
|
| 495 |
+
continue
|
| 496 |
+
|
| 497 |
+
mac = _parse_mac(word)
|
| 498 |
+
if mac is None:
|
| 499 |
+
continue
|
| 500 |
+
if _is_universal(mac):
|
| 501 |
+
return mac
|
| 502 |
+
if first_local_mac is None:
|
| 503 |
+
first_local_mac = mac
|
| 504 |
+
|
| 505 |
+
return first_local_mac
|
| 506 |
+
|
| 507 |
+
|
| 508 |
+
# The following functions call external programs to 'get' a macaddr value to
|
| 509 |
+
# be used as basis for an uuid
|
| 510 |
+
def _ifconfig_getnode():
|
| 511 |
+
"""Get the hardware address on Unix by running ifconfig."""
|
| 512 |
+
# This works on Linux ('' or '-a'), Tru64 ('-av'), but not all Unixes.
|
| 513 |
+
keywords = (b'hwaddr', b'ether', b'address:', b'lladdr')
|
| 514 |
+
for args in ('', '-a', '-av'):
|
| 515 |
+
mac = _find_mac_near_keyword('ifconfig', args, keywords, lambda i: i+1)
|
| 516 |
+
if mac:
|
| 517 |
+
return mac
|
| 518 |
+
return None
|
| 519 |
+
|
| 520 |
+
def _ip_getnode():
|
| 521 |
+
"""Get the hardware address on Unix by running ip."""
|
| 522 |
+
# This works on Linux with iproute2.
|
| 523 |
+
mac = _find_mac_near_keyword('ip', 'link', [b'link/ether'], lambda i: i+1)
|
| 524 |
+
if mac:
|
| 525 |
+
return mac
|
| 526 |
+
return None
|
| 527 |
+
|
| 528 |
+
def _arp_getnode():
|
| 529 |
+
"""Get the hardware address on Unix by running arp."""
|
| 530 |
+
import os, socket
|
| 531 |
+
try:
|
| 532 |
+
ip_addr = socket.gethostbyname(socket.gethostname())
|
| 533 |
+
except OSError:
|
| 534 |
+
return None
|
| 535 |
+
|
| 536 |
+
# Try getting the MAC addr from arp based on our IP address (Solaris).
|
| 537 |
+
mac = _find_mac_near_keyword('arp', '-an', [os.fsencode(ip_addr)], lambda i: -1)
|
| 538 |
+
if mac:
|
| 539 |
+
return mac
|
| 540 |
+
|
| 541 |
+
# This works on OpenBSD
|
| 542 |
+
mac = _find_mac_near_keyword('arp', '-an', [os.fsencode(ip_addr)], lambda i: i+1)
|
| 543 |
+
if mac:
|
| 544 |
+
return mac
|
| 545 |
+
|
| 546 |
+
# This works on Linux, FreeBSD and NetBSD
|
| 547 |
+
mac = _find_mac_near_keyword('arp', '-an', [os.fsencode('(%s)' % ip_addr)],
|
| 548 |
+
lambda i: i+2)
|
| 549 |
+
# Return None instead of 0.
|
| 550 |
+
if mac:
|
| 551 |
+
return mac
|
| 552 |
+
return None
|
| 553 |
+
|
| 554 |
+
def _lanscan_getnode():
|
| 555 |
+
"""Get the hardware address on Unix by running lanscan."""
|
| 556 |
+
# This might work on HP-UX.
|
| 557 |
+
return _find_mac_near_keyword('lanscan', '-ai', [b'lan0'], lambda i: 0)
|
| 558 |
+
|
| 559 |
+
def _netstat_getnode():
|
| 560 |
+
"""Get the hardware address on Unix by running netstat."""
|
| 561 |
+
# This works on AIX and might work on Tru64 UNIX.
|
| 562 |
+
return _find_mac_under_heading('netstat', '-ian', b'Address')
|
| 563 |
+
|
| 564 |
+
def _ipconfig_getnode():
|
| 565 |
+
"""[DEPRECATED] Get the hardware address on Windows."""
|
| 566 |
+
# bpo-40501: UuidCreateSequential() is now the only supported approach
|
| 567 |
+
return _windll_getnode()
|
| 568 |
+
|
| 569 |
+
def _netbios_getnode():
|
| 570 |
+
"""[DEPRECATED] Get the hardware address on Windows."""
|
| 571 |
+
# bpo-40501: UuidCreateSequential() is now the only supported approach
|
| 572 |
+
return _windll_getnode()
|
| 573 |
+
|
| 574 |
+
|
| 575 |
+
# Import optional C extension at toplevel, to help disabling it when testing
|
| 576 |
+
try:
|
| 577 |
+
import _uuid
|
| 578 |
+
_generate_time_safe = getattr(_uuid, "generate_time_safe", None)
|
| 579 |
+
_UuidCreate = getattr(_uuid, "UuidCreate", None)
|
| 580 |
+
_has_uuid_generate_time_safe = _uuid.has_uuid_generate_time_safe
|
| 581 |
+
except ImportError:
|
| 582 |
+
_uuid = None
|
| 583 |
+
_generate_time_safe = None
|
| 584 |
+
_UuidCreate = None
|
| 585 |
+
_has_uuid_generate_time_safe = None
|
| 586 |
+
|
| 587 |
+
|
| 588 |
+
def _load_system_functions():
|
| 589 |
+
"""[DEPRECATED] Platform-specific functions loaded at import time"""
|
| 590 |
+
|
| 591 |
+
|
| 592 |
+
def _unix_getnode():
|
| 593 |
+
"""Get the hardware address on Unix using the _uuid extension module."""
|
| 594 |
+
if _generate_time_safe:
|
| 595 |
+
uuid_time, _ = _generate_time_safe()
|
| 596 |
+
return UUID(bytes=uuid_time).node
|
| 597 |
+
|
| 598 |
+
def _windll_getnode():
|
| 599 |
+
"""Get the hardware address on Windows using the _uuid extension module."""
|
| 600 |
+
if _UuidCreate:
|
| 601 |
+
uuid_bytes = _UuidCreate()
|
| 602 |
+
return UUID(bytes_le=uuid_bytes).node
|
| 603 |
+
|
| 604 |
+
def _random_getnode():
|
| 605 |
+
"""Get a random node ID."""
|
| 606 |
+
# RFC 4122, $4.1.6 says "For systems with no IEEE address, a randomly or
|
| 607 |
+
# pseudo-randomly generated value may be used; see Section 4.5. The
|
| 608 |
+
# multicast bit must be set in such addresses, in order that they will
|
| 609 |
+
# never conflict with addresses obtained from network cards."
|
| 610 |
+
#
|
| 611 |
+
# The "multicast bit" of a MAC address is defined to be "the least
|
| 612 |
+
# significant bit of the first octet". This works out to be the 41st bit
|
| 613 |
+
# counting from 1 being the least significant bit, or 1<<40.
|
| 614 |
+
#
|
| 615 |
+
# See https://en.wikipedia.org/wiki/MAC_address#Unicast_vs._multicast
|
| 616 |
+
import random
|
| 617 |
+
return random.getrandbits(48) | (1 << 40)
|
| 618 |
+
|
| 619 |
+
|
| 620 |
+
# _OS_GETTERS, when known, are targeted for a specific OS or platform.
|
| 621 |
+
# The order is by 'common practice' on the specified platform.
|
| 622 |
+
# Note: 'posix' and 'windows' _OS_GETTERS are prefixed by a dll/dlload() method
|
| 623 |
+
# which, when successful, means none of these "external" methods are called.
|
| 624 |
+
# _GETTERS is (also) used by test_uuid.py to SkipUnless(), e.g.,
|
| 625 |
+
# @unittest.skipUnless(_uuid._ifconfig_getnode in _uuid._GETTERS, ...)
|
| 626 |
+
if _LINUX:
|
| 627 |
+
_OS_GETTERS = [_ip_getnode, _ifconfig_getnode]
|
| 628 |
+
elif sys.platform == 'darwin':
|
| 629 |
+
_OS_GETTERS = [_ifconfig_getnode, _arp_getnode, _netstat_getnode]
|
| 630 |
+
elif sys.platform == 'win32':
|
| 631 |
+
# bpo-40201: _windll_getnode will always succeed, so these are not needed
|
| 632 |
+
_OS_GETTERS = []
|
| 633 |
+
elif _AIX:
|
| 634 |
+
_OS_GETTERS = [_netstat_getnode]
|
| 635 |
+
else:
|
| 636 |
+
_OS_GETTERS = [_ifconfig_getnode, _ip_getnode, _arp_getnode,
|
| 637 |
+
_netstat_getnode, _lanscan_getnode]
|
| 638 |
+
if os.name == 'posix':
|
| 639 |
+
_GETTERS = [_unix_getnode] + _OS_GETTERS
|
| 640 |
+
elif os.name == 'nt':
|
| 641 |
+
_GETTERS = [_windll_getnode] + _OS_GETTERS
|
| 642 |
+
else:
|
| 643 |
+
_GETTERS = _OS_GETTERS
|
| 644 |
+
|
| 645 |
+
_node = None
|
| 646 |
+
|
| 647 |
+
def getnode():
|
| 648 |
+
"""Get the hardware address as a 48-bit positive integer.
|
| 649 |
+
|
| 650 |
+
The first time this runs, it may launch a separate program, which could
|
| 651 |
+
be quite slow. If all attempts to obtain the hardware address fail, we
|
| 652 |
+
choose a random 48-bit number with its eighth bit set to 1 as recommended
|
| 653 |
+
in RFC 4122.
|
| 654 |
+
"""
|
| 655 |
+
global _node
|
| 656 |
+
if _node is not None:
|
| 657 |
+
return _node
|
| 658 |
+
|
| 659 |
+
for getter in _GETTERS + [_random_getnode]:
|
| 660 |
+
try:
|
| 661 |
+
_node = getter()
|
| 662 |
+
except:
|
| 663 |
+
continue
|
| 664 |
+
if (_node is not None) and (0 <= _node < (1 << 48)):
|
| 665 |
+
return _node
|
| 666 |
+
assert False, '_random_getnode() returned invalid value: {}'.format(_node)
|
| 667 |
+
|
| 668 |
+
|
| 669 |
+
_last_timestamp = None
|
| 670 |
+
|
| 671 |
+
def uuid1(node=None, clock_seq=None):
|
| 672 |
+
"""Generate a UUID from a host ID, sequence number, and the current time.
|
| 673 |
+
If 'node' is not given, getnode() is used to obtain the hardware
|
| 674 |
+
address. If 'clock_seq' is given, it is used as the sequence number;
|
| 675 |
+
otherwise a random 14-bit sequence number is chosen."""
|
| 676 |
+
|
| 677 |
+
# When the system provides a version-1 UUID generator, use it (but don't
|
| 678 |
+
# use UuidCreate here because its UUIDs don't conform to RFC 4122).
|
| 679 |
+
if _generate_time_safe is not None and node is clock_seq is None:
|
| 680 |
+
uuid_time, safely_generated = _generate_time_safe()
|
| 681 |
+
try:
|
| 682 |
+
is_safe = SafeUUID(safely_generated)
|
| 683 |
+
except ValueError:
|
| 684 |
+
is_safe = SafeUUID.unknown
|
| 685 |
+
return UUID(bytes=uuid_time, is_safe=is_safe)
|
| 686 |
+
|
| 687 |
+
global _last_timestamp
|
| 688 |
+
import time
|
| 689 |
+
nanoseconds = time.time_ns()
|
| 690 |
+
# 0x01b21dd213814000 is the number of 100-ns intervals between the
|
| 691 |
+
# UUID epoch 1582-10-15 00:00:00 and the Unix epoch 1970-01-01 00:00:00.
|
| 692 |
+
timestamp = nanoseconds // 100 + 0x01b21dd213814000
|
| 693 |
+
if _last_timestamp is not None and timestamp <= _last_timestamp:
|
| 694 |
+
timestamp = _last_timestamp + 1
|
| 695 |
+
_last_timestamp = timestamp
|
| 696 |
+
if clock_seq is None:
|
| 697 |
+
import random
|
| 698 |
+
clock_seq = random.getrandbits(14) # instead of stable storage
|
| 699 |
+
time_low = timestamp & 0xffffffff
|
| 700 |
+
time_mid = (timestamp >> 32) & 0xffff
|
| 701 |
+
time_hi_version = (timestamp >> 48) & 0x0fff
|
| 702 |
+
clock_seq_low = clock_seq & 0xff
|
| 703 |
+
clock_seq_hi_variant = (clock_seq >> 8) & 0x3f
|
| 704 |
+
if node is None:
|
| 705 |
+
node = getnode()
|
| 706 |
+
return UUID(fields=(time_low, time_mid, time_hi_version,
|
| 707 |
+
clock_seq_hi_variant, clock_seq_low, node), version=1)
|
| 708 |
+
|
| 709 |
+
def uuid3(namespace, name):
|
| 710 |
+
"""Generate a UUID from the MD5 hash of a namespace UUID and a name."""
|
| 711 |
+
from hashlib import md5
|
| 712 |
+
digest = md5(
|
| 713 |
+
namespace.bytes + bytes(name, "utf-8"),
|
| 714 |
+
usedforsecurity=False
|
| 715 |
+
).digest()
|
| 716 |
+
return UUID(bytes=digest[:16], version=3)
|
| 717 |
+
|
| 718 |
+
def uuid4():
|
| 719 |
+
"""Generate a random UUID."""
|
| 720 |
+
return UUID(bytes=os.urandom(16), version=4)
|
| 721 |
+
|
| 722 |
+
def uuid5(namespace, name):
|
| 723 |
+
"""Generate a UUID from the SHA-1 hash of a namespace UUID and a name."""
|
| 724 |
+
from hashlib import sha1
|
| 725 |
+
hash = sha1(namespace.bytes + bytes(name, "utf-8")).digest()
|
| 726 |
+
return UUID(bytes=hash[:16], version=5)
|
| 727 |
+
|
| 728 |
+
# The following standard UUIDs are for use with uuid3() or uuid5().
|
| 729 |
+
|
| 730 |
+
NAMESPACE_DNS = UUID('6ba7b810-9dad-11d1-80b4-00c04fd430c8')
|
| 731 |
+
NAMESPACE_URL = UUID('6ba7b811-9dad-11d1-80b4-00c04fd430c8')
|
| 732 |
+
NAMESPACE_OID = UUID('6ba7b812-9dad-11d1-80b4-00c04fd430c8')
|
| 733 |
+
NAMESPACE_X500 = UUID('6ba7b814-9dad-11d1-80b4-00c04fd430c8')
|
lib/python3.10/warnings.py
ADDED
|
@@ -0,0 +1,549 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Python part of the warnings subsystem."""
|
| 2 |
+
|
| 3 |
+
import sys
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
__all__ = ["warn", "warn_explicit", "showwarning",
|
| 7 |
+
"formatwarning", "filterwarnings", "simplefilter",
|
| 8 |
+
"resetwarnings", "catch_warnings"]
|
| 9 |
+
|
| 10 |
+
def showwarning(message, category, filename, lineno, file=None, line=None):
|
| 11 |
+
"""Hook to write a warning to a file; replace if you like."""
|
| 12 |
+
msg = WarningMessage(message, category, filename, lineno, file, line)
|
| 13 |
+
_showwarnmsg_impl(msg)
|
| 14 |
+
|
| 15 |
+
def formatwarning(message, category, filename, lineno, line=None):
|
| 16 |
+
"""Function to format a warning the standard way."""
|
| 17 |
+
msg = WarningMessage(message, category, filename, lineno, None, line)
|
| 18 |
+
return _formatwarnmsg_impl(msg)
|
| 19 |
+
|
| 20 |
+
def _showwarnmsg_impl(msg):
|
| 21 |
+
file = msg.file
|
| 22 |
+
if file is None:
|
| 23 |
+
file = sys.stderr
|
| 24 |
+
if file is None:
|
| 25 |
+
# sys.stderr is None when run with pythonw.exe:
|
| 26 |
+
# warnings get lost
|
| 27 |
+
return
|
| 28 |
+
text = _formatwarnmsg(msg)
|
| 29 |
+
try:
|
| 30 |
+
file.write(text)
|
| 31 |
+
except OSError:
|
| 32 |
+
# the file (probably stderr) is invalid - this warning gets lost.
|
| 33 |
+
pass
|
| 34 |
+
|
| 35 |
+
def _formatwarnmsg_impl(msg):
|
| 36 |
+
category = msg.category.__name__
|
| 37 |
+
s = f"{msg.filename}:{msg.lineno}: {category}: {msg.message}\n"
|
| 38 |
+
|
| 39 |
+
if msg.line is None:
|
| 40 |
+
try:
|
| 41 |
+
import linecache
|
| 42 |
+
line = linecache.getline(msg.filename, msg.lineno)
|
| 43 |
+
except Exception:
|
| 44 |
+
# When a warning is logged during Python shutdown, linecache
|
| 45 |
+
# and the import machinery don't work anymore
|
| 46 |
+
line = None
|
| 47 |
+
linecache = None
|
| 48 |
+
else:
|
| 49 |
+
line = msg.line
|
| 50 |
+
if line:
|
| 51 |
+
line = line.strip()
|
| 52 |
+
s += " %s\n" % line
|
| 53 |
+
|
| 54 |
+
if msg.source is not None:
|
| 55 |
+
try:
|
| 56 |
+
import tracemalloc
|
| 57 |
+
# Logging a warning should not raise a new exception:
|
| 58 |
+
# catch Exception, not only ImportError and RecursionError.
|
| 59 |
+
except Exception:
|
| 60 |
+
# don't suggest to enable tracemalloc if it's not available
|
| 61 |
+
tracing = True
|
| 62 |
+
tb = None
|
| 63 |
+
else:
|
| 64 |
+
tracing = tracemalloc.is_tracing()
|
| 65 |
+
try:
|
| 66 |
+
tb = tracemalloc.get_object_traceback(msg.source)
|
| 67 |
+
except Exception:
|
| 68 |
+
# When a warning is logged during Python shutdown, tracemalloc
|
| 69 |
+
# and the import machinery don't work anymore
|
| 70 |
+
tb = None
|
| 71 |
+
|
| 72 |
+
if tb is not None:
|
| 73 |
+
s += 'Object allocated at (most recent call last):\n'
|
| 74 |
+
for frame in tb:
|
| 75 |
+
s += (' File "%s", lineno %s\n'
|
| 76 |
+
% (frame.filename, frame.lineno))
|
| 77 |
+
|
| 78 |
+
try:
|
| 79 |
+
if linecache is not None:
|
| 80 |
+
line = linecache.getline(frame.filename, frame.lineno)
|
| 81 |
+
else:
|
| 82 |
+
line = None
|
| 83 |
+
except Exception:
|
| 84 |
+
line = None
|
| 85 |
+
if line:
|
| 86 |
+
line = line.strip()
|
| 87 |
+
s += ' %s\n' % line
|
| 88 |
+
elif not tracing:
|
| 89 |
+
s += (f'{category}: Enable tracemalloc to get the object '
|
| 90 |
+
f'allocation traceback\n')
|
| 91 |
+
return s
|
| 92 |
+
|
| 93 |
+
# Keep a reference to check if the function was replaced
|
| 94 |
+
_showwarning_orig = showwarning
|
| 95 |
+
|
| 96 |
+
def _showwarnmsg(msg):
|
| 97 |
+
"""Hook to write a warning to a file; replace if you like."""
|
| 98 |
+
try:
|
| 99 |
+
sw = showwarning
|
| 100 |
+
except NameError:
|
| 101 |
+
pass
|
| 102 |
+
else:
|
| 103 |
+
if sw is not _showwarning_orig:
|
| 104 |
+
# warnings.showwarning() was replaced
|
| 105 |
+
if not callable(sw):
|
| 106 |
+
raise TypeError("warnings.showwarning() must be set to a "
|
| 107 |
+
"function or method")
|
| 108 |
+
|
| 109 |
+
sw(msg.message, msg.category, msg.filename, msg.lineno,
|
| 110 |
+
msg.file, msg.line)
|
| 111 |
+
return
|
| 112 |
+
_showwarnmsg_impl(msg)
|
| 113 |
+
|
| 114 |
+
# Keep a reference to check if the function was replaced
|
| 115 |
+
_formatwarning_orig = formatwarning
|
| 116 |
+
|
| 117 |
+
def _formatwarnmsg(msg):
|
| 118 |
+
"""Function to format a warning the standard way."""
|
| 119 |
+
try:
|
| 120 |
+
fw = formatwarning
|
| 121 |
+
except NameError:
|
| 122 |
+
pass
|
| 123 |
+
else:
|
| 124 |
+
if fw is not _formatwarning_orig:
|
| 125 |
+
# warnings.formatwarning() was replaced
|
| 126 |
+
return fw(msg.message, msg.category,
|
| 127 |
+
msg.filename, msg.lineno, msg.line)
|
| 128 |
+
return _formatwarnmsg_impl(msg)
|
| 129 |
+
|
| 130 |
+
def filterwarnings(action, message="", category=Warning, module="", lineno=0,
|
| 131 |
+
append=False):
|
| 132 |
+
"""Insert an entry into the list of warnings filters (at the front).
|
| 133 |
+
|
| 134 |
+
'action' -- one of "error", "ignore", "always", "default", "module",
|
| 135 |
+
or "once"
|
| 136 |
+
'message' -- a regex that the warning message must match
|
| 137 |
+
'category' -- a class that the warning must be a subclass of
|
| 138 |
+
'module' -- a regex that the module name must match
|
| 139 |
+
'lineno' -- an integer line number, 0 matches all warnings
|
| 140 |
+
'append' -- if true, append to the list of filters
|
| 141 |
+
"""
|
| 142 |
+
assert action in ("error", "ignore", "always", "default", "module",
|
| 143 |
+
"once"), "invalid action: %r" % (action,)
|
| 144 |
+
assert isinstance(message, str), "message must be a string"
|
| 145 |
+
assert isinstance(category, type), "category must be a class"
|
| 146 |
+
assert issubclass(category, Warning), "category must be a Warning subclass"
|
| 147 |
+
assert isinstance(module, str), "module must be a string"
|
| 148 |
+
assert isinstance(lineno, int) and lineno >= 0, \
|
| 149 |
+
"lineno must be an int >= 0"
|
| 150 |
+
|
| 151 |
+
if message or module:
|
| 152 |
+
import re
|
| 153 |
+
|
| 154 |
+
if message:
|
| 155 |
+
message = re.compile(message, re.I)
|
| 156 |
+
else:
|
| 157 |
+
message = None
|
| 158 |
+
if module:
|
| 159 |
+
module = re.compile(module)
|
| 160 |
+
else:
|
| 161 |
+
module = None
|
| 162 |
+
|
| 163 |
+
_add_filter(action, message, category, module, lineno, append=append)
|
| 164 |
+
|
| 165 |
+
def simplefilter(action, category=Warning, lineno=0, append=False):
|
| 166 |
+
"""Insert a simple entry into the list of warnings filters (at the front).
|
| 167 |
+
|
| 168 |
+
A simple filter matches all modules and messages.
|
| 169 |
+
'action' -- one of "error", "ignore", "always", "default", "module",
|
| 170 |
+
or "once"
|
| 171 |
+
'category' -- a class that the warning must be a subclass of
|
| 172 |
+
'lineno' -- an integer line number, 0 matches all warnings
|
| 173 |
+
'append' -- if true, append to the list of filters
|
| 174 |
+
"""
|
| 175 |
+
assert action in ("error", "ignore", "always", "default", "module",
|
| 176 |
+
"once"), "invalid action: %r" % (action,)
|
| 177 |
+
assert isinstance(lineno, int) and lineno >= 0, \
|
| 178 |
+
"lineno must be an int >= 0"
|
| 179 |
+
_add_filter(action, None, category, None, lineno, append=append)
|
| 180 |
+
|
| 181 |
+
def _add_filter(*item, append):
|
| 182 |
+
# Remove possible duplicate filters, so new one will be placed
|
| 183 |
+
# in correct place. If append=True and duplicate exists, do nothing.
|
| 184 |
+
if not append:
|
| 185 |
+
try:
|
| 186 |
+
filters.remove(item)
|
| 187 |
+
except ValueError:
|
| 188 |
+
pass
|
| 189 |
+
filters.insert(0, item)
|
| 190 |
+
else:
|
| 191 |
+
if item not in filters:
|
| 192 |
+
filters.append(item)
|
| 193 |
+
_filters_mutated()
|
| 194 |
+
|
| 195 |
+
def resetwarnings():
|
| 196 |
+
"""Clear the list of warning filters, so that no filters are active."""
|
| 197 |
+
filters[:] = []
|
| 198 |
+
_filters_mutated()
|
| 199 |
+
|
| 200 |
+
class _OptionError(Exception):
|
| 201 |
+
"""Exception used by option processing helpers."""
|
| 202 |
+
pass
|
| 203 |
+
|
| 204 |
+
# Helper to process -W options passed via sys.warnoptions
|
| 205 |
+
def _processoptions(args):
|
| 206 |
+
for arg in args:
|
| 207 |
+
try:
|
| 208 |
+
_setoption(arg)
|
| 209 |
+
except _OptionError as msg:
|
| 210 |
+
print("Invalid -W option ignored:", msg, file=sys.stderr)
|
| 211 |
+
|
| 212 |
+
# Helper for _processoptions()
|
| 213 |
+
def _setoption(arg):
|
| 214 |
+
parts = arg.split(':')
|
| 215 |
+
if len(parts) > 5:
|
| 216 |
+
raise _OptionError("too many fields (max 5): %r" % (arg,))
|
| 217 |
+
while len(parts) < 5:
|
| 218 |
+
parts.append('')
|
| 219 |
+
action, message, category, module, lineno = [s.strip()
|
| 220 |
+
for s in parts]
|
| 221 |
+
action = _getaction(action)
|
| 222 |
+
category = _getcategory(category)
|
| 223 |
+
if message or module:
|
| 224 |
+
import re
|
| 225 |
+
if message:
|
| 226 |
+
message = re.escape(message)
|
| 227 |
+
if module:
|
| 228 |
+
module = re.escape(module) + r'\Z'
|
| 229 |
+
if lineno:
|
| 230 |
+
try:
|
| 231 |
+
lineno = int(lineno)
|
| 232 |
+
if lineno < 0:
|
| 233 |
+
raise ValueError
|
| 234 |
+
except (ValueError, OverflowError):
|
| 235 |
+
raise _OptionError("invalid lineno %r" % (lineno,)) from None
|
| 236 |
+
else:
|
| 237 |
+
lineno = 0
|
| 238 |
+
filterwarnings(action, message, category, module, lineno)
|
| 239 |
+
|
| 240 |
+
# Helper for _setoption()
|
| 241 |
+
def _getaction(action):
|
| 242 |
+
if not action:
|
| 243 |
+
return "default"
|
| 244 |
+
if action == "all": return "always" # Alias
|
| 245 |
+
for a in ('default', 'always', 'ignore', 'module', 'once', 'error'):
|
| 246 |
+
if a.startswith(action):
|
| 247 |
+
return a
|
| 248 |
+
raise _OptionError("invalid action: %r" % (action,))
|
| 249 |
+
|
| 250 |
+
# Helper for _setoption()
|
| 251 |
+
def _getcategory(category):
|
| 252 |
+
if not category:
|
| 253 |
+
return Warning
|
| 254 |
+
if '.' not in category:
|
| 255 |
+
import builtins as m
|
| 256 |
+
klass = category
|
| 257 |
+
else:
|
| 258 |
+
module, _, klass = category.rpartition('.')
|
| 259 |
+
try:
|
| 260 |
+
m = __import__(module, None, None, [klass])
|
| 261 |
+
except ImportError:
|
| 262 |
+
raise _OptionError("invalid module name: %r" % (module,)) from None
|
| 263 |
+
try:
|
| 264 |
+
cat = getattr(m, klass)
|
| 265 |
+
except AttributeError:
|
| 266 |
+
raise _OptionError("unknown warning category: %r" % (category,)) from None
|
| 267 |
+
if not issubclass(cat, Warning):
|
| 268 |
+
raise _OptionError("invalid warning category: %r" % (category,))
|
| 269 |
+
return cat
|
| 270 |
+
|
| 271 |
+
|
| 272 |
+
def _is_internal_frame(frame):
|
| 273 |
+
"""Signal whether the frame is an internal CPython implementation detail."""
|
| 274 |
+
filename = frame.f_code.co_filename
|
| 275 |
+
return 'importlib' in filename and '_bootstrap' in filename
|
| 276 |
+
|
| 277 |
+
|
| 278 |
+
def _next_external_frame(frame):
|
| 279 |
+
"""Find the next frame that doesn't involve CPython internals."""
|
| 280 |
+
frame = frame.f_back
|
| 281 |
+
while frame is not None and _is_internal_frame(frame):
|
| 282 |
+
frame = frame.f_back
|
| 283 |
+
return frame
|
| 284 |
+
|
| 285 |
+
|
| 286 |
+
# Code typically replaced by _warnings
|
| 287 |
+
def warn(message, category=None, stacklevel=1, source=None):
|
| 288 |
+
"""Issue a warning, or maybe ignore it or raise an exception."""
|
| 289 |
+
# Check if message is already a Warning object
|
| 290 |
+
if isinstance(message, Warning):
|
| 291 |
+
category = message.__class__
|
| 292 |
+
# Check category argument
|
| 293 |
+
if category is None:
|
| 294 |
+
category = UserWarning
|
| 295 |
+
if not (isinstance(category, type) and issubclass(category, Warning)):
|
| 296 |
+
raise TypeError("category must be a Warning subclass, "
|
| 297 |
+
"not '{:s}'".format(type(category).__name__))
|
| 298 |
+
# Get context information
|
| 299 |
+
try:
|
| 300 |
+
if stacklevel <= 1 or _is_internal_frame(sys._getframe(1)):
|
| 301 |
+
# If frame is too small to care or if the warning originated in
|
| 302 |
+
# internal code, then do not try to hide any frames.
|
| 303 |
+
frame = sys._getframe(stacklevel)
|
| 304 |
+
else:
|
| 305 |
+
frame = sys._getframe(1)
|
| 306 |
+
# Look for one frame less since the above line starts us off.
|
| 307 |
+
for x in range(stacklevel-1):
|
| 308 |
+
frame = _next_external_frame(frame)
|
| 309 |
+
if frame is None:
|
| 310 |
+
raise ValueError
|
| 311 |
+
except ValueError:
|
| 312 |
+
globals = sys.__dict__
|
| 313 |
+
filename = "sys"
|
| 314 |
+
lineno = 1
|
| 315 |
+
else:
|
| 316 |
+
globals = frame.f_globals
|
| 317 |
+
filename = frame.f_code.co_filename
|
| 318 |
+
lineno = frame.f_lineno
|
| 319 |
+
if '__name__' in globals:
|
| 320 |
+
module = globals['__name__']
|
| 321 |
+
else:
|
| 322 |
+
module = "<string>"
|
| 323 |
+
registry = globals.setdefault("__warningregistry__", {})
|
| 324 |
+
warn_explicit(message, category, filename, lineno, module, registry,
|
| 325 |
+
globals, source)
|
| 326 |
+
|
| 327 |
+
def warn_explicit(message, category, filename, lineno,
|
| 328 |
+
module=None, registry=None, module_globals=None,
|
| 329 |
+
source=None):
|
| 330 |
+
lineno = int(lineno)
|
| 331 |
+
if module is None:
|
| 332 |
+
module = filename or "<unknown>"
|
| 333 |
+
if module[-3:].lower() == ".py":
|
| 334 |
+
module = module[:-3] # XXX What about leading pathname?
|
| 335 |
+
if registry is None:
|
| 336 |
+
registry = {}
|
| 337 |
+
if registry.get('version', 0) != _filters_version:
|
| 338 |
+
registry.clear()
|
| 339 |
+
registry['version'] = _filters_version
|
| 340 |
+
if isinstance(message, Warning):
|
| 341 |
+
text = str(message)
|
| 342 |
+
category = message.__class__
|
| 343 |
+
else:
|
| 344 |
+
text = message
|
| 345 |
+
message = category(message)
|
| 346 |
+
key = (text, category, lineno)
|
| 347 |
+
# Quick test for common case
|
| 348 |
+
if registry.get(key):
|
| 349 |
+
return
|
| 350 |
+
# Search the filters
|
| 351 |
+
for item in filters:
|
| 352 |
+
action, msg, cat, mod, ln = item
|
| 353 |
+
if ((msg is None or msg.match(text)) and
|
| 354 |
+
issubclass(category, cat) and
|
| 355 |
+
(mod is None or mod.match(module)) and
|
| 356 |
+
(ln == 0 or lineno == ln)):
|
| 357 |
+
break
|
| 358 |
+
else:
|
| 359 |
+
action = defaultaction
|
| 360 |
+
# Early exit actions
|
| 361 |
+
if action == "ignore":
|
| 362 |
+
return
|
| 363 |
+
|
| 364 |
+
# Prime the linecache for formatting, in case the
|
| 365 |
+
# "file" is actually in a zipfile or something.
|
| 366 |
+
import linecache
|
| 367 |
+
linecache.getlines(filename, module_globals)
|
| 368 |
+
|
| 369 |
+
if action == "error":
|
| 370 |
+
raise message
|
| 371 |
+
# Other actions
|
| 372 |
+
if action == "once":
|
| 373 |
+
registry[key] = 1
|
| 374 |
+
oncekey = (text, category)
|
| 375 |
+
if onceregistry.get(oncekey):
|
| 376 |
+
return
|
| 377 |
+
onceregistry[oncekey] = 1
|
| 378 |
+
elif action == "always":
|
| 379 |
+
pass
|
| 380 |
+
elif action == "module":
|
| 381 |
+
registry[key] = 1
|
| 382 |
+
altkey = (text, category, 0)
|
| 383 |
+
if registry.get(altkey):
|
| 384 |
+
return
|
| 385 |
+
registry[altkey] = 1
|
| 386 |
+
elif action == "default":
|
| 387 |
+
registry[key] = 1
|
| 388 |
+
else:
|
| 389 |
+
# Unrecognized actions are errors
|
| 390 |
+
raise RuntimeError(
|
| 391 |
+
"Unrecognized action (%r) in warnings.filters:\n %s" %
|
| 392 |
+
(action, item))
|
| 393 |
+
# Print message and context
|
| 394 |
+
msg = WarningMessage(message, category, filename, lineno, source)
|
| 395 |
+
_showwarnmsg(msg)
|
| 396 |
+
|
| 397 |
+
|
| 398 |
+
class WarningMessage(object):
|
| 399 |
+
|
| 400 |
+
_WARNING_DETAILS = ("message", "category", "filename", "lineno", "file",
|
| 401 |
+
"line", "source")
|
| 402 |
+
|
| 403 |
+
def __init__(self, message, category, filename, lineno, file=None,
|
| 404 |
+
line=None, source=None):
|
| 405 |
+
self.message = message
|
| 406 |
+
self.category = category
|
| 407 |
+
self.filename = filename
|
| 408 |
+
self.lineno = lineno
|
| 409 |
+
self.file = file
|
| 410 |
+
self.line = line
|
| 411 |
+
self.source = source
|
| 412 |
+
self._category_name = category.__name__ if category else None
|
| 413 |
+
|
| 414 |
+
def __str__(self):
|
| 415 |
+
return ("{message : %r, category : %r, filename : %r, lineno : %s, "
|
| 416 |
+
"line : %r}" % (self.message, self._category_name,
|
| 417 |
+
self.filename, self.lineno, self.line))
|
| 418 |
+
|
| 419 |
+
|
| 420 |
+
class catch_warnings(object):
|
| 421 |
+
|
| 422 |
+
"""A context manager that copies and restores the warnings filter upon
|
| 423 |
+
exiting the context.
|
| 424 |
+
|
| 425 |
+
The 'record' argument specifies whether warnings should be captured by a
|
| 426 |
+
custom implementation of warnings.showwarning() and be appended to a list
|
| 427 |
+
returned by the context manager. Otherwise None is returned by the context
|
| 428 |
+
manager. The objects appended to the list are arguments whose attributes
|
| 429 |
+
mirror the arguments to showwarning().
|
| 430 |
+
|
| 431 |
+
The 'module' argument is to specify an alternative module to the module
|
| 432 |
+
named 'warnings' and imported under that name. This argument is only useful
|
| 433 |
+
when testing the warnings module itself.
|
| 434 |
+
|
| 435 |
+
"""
|
| 436 |
+
|
| 437 |
+
def __init__(self, *, record=False, module=None):
|
| 438 |
+
"""Specify whether to record warnings and if an alternative module
|
| 439 |
+
should be used other than sys.modules['warnings'].
|
| 440 |
+
|
| 441 |
+
For compatibility with Python 3.0, please consider all arguments to be
|
| 442 |
+
keyword-only.
|
| 443 |
+
|
| 444 |
+
"""
|
| 445 |
+
self._record = record
|
| 446 |
+
self._module = sys.modules['warnings'] if module is None else module
|
| 447 |
+
self._entered = False
|
| 448 |
+
|
| 449 |
+
def __repr__(self):
|
| 450 |
+
args = []
|
| 451 |
+
if self._record:
|
| 452 |
+
args.append("record=True")
|
| 453 |
+
if self._module is not sys.modules['warnings']:
|
| 454 |
+
args.append("module=%r" % self._module)
|
| 455 |
+
name = type(self).__name__
|
| 456 |
+
return "%s(%s)" % (name, ", ".join(args))
|
| 457 |
+
|
| 458 |
+
def __enter__(self):
|
| 459 |
+
if self._entered:
|
| 460 |
+
raise RuntimeError("Cannot enter %r twice" % self)
|
| 461 |
+
self._entered = True
|
| 462 |
+
self._filters = self._module.filters
|
| 463 |
+
self._module.filters = self._filters[:]
|
| 464 |
+
self._module._filters_mutated()
|
| 465 |
+
self._showwarning = self._module.showwarning
|
| 466 |
+
self._showwarnmsg_impl = self._module._showwarnmsg_impl
|
| 467 |
+
if self._record:
|
| 468 |
+
log = []
|
| 469 |
+
self._module._showwarnmsg_impl = log.append
|
| 470 |
+
# Reset showwarning() to the default implementation to make sure
|
| 471 |
+
# that _showwarnmsg() calls _showwarnmsg_impl()
|
| 472 |
+
self._module.showwarning = self._module._showwarning_orig
|
| 473 |
+
return log
|
| 474 |
+
else:
|
| 475 |
+
return None
|
| 476 |
+
|
| 477 |
+
def __exit__(self, *exc_info):
|
| 478 |
+
if not self._entered:
|
| 479 |
+
raise RuntimeError("Cannot exit %r without entering first" % self)
|
| 480 |
+
self._module.filters = self._filters
|
| 481 |
+
self._module._filters_mutated()
|
| 482 |
+
self._module.showwarning = self._showwarning
|
| 483 |
+
self._module._showwarnmsg_impl = self._showwarnmsg_impl
|
| 484 |
+
|
| 485 |
+
|
| 486 |
+
# Private utility function called by _PyErr_WarnUnawaitedCoroutine
|
| 487 |
+
def _warn_unawaited_coroutine(coro):
|
| 488 |
+
msg_lines = [
|
| 489 |
+
f"coroutine '{coro.__qualname__}' was never awaited\n"
|
| 490 |
+
]
|
| 491 |
+
if coro.cr_origin is not None:
|
| 492 |
+
import linecache, traceback
|
| 493 |
+
def extract():
|
| 494 |
+
for filename, lineno, funcname in reversed(coro.cr_origin):
|
| 495 |
+
line = linecache.getline(filename, lineno)
|
| 496 |
+
yield (filename, lineno, funcname, line)
|
| 497 |
+
msg_lines.append("Coroutine created at (most recent call last)\n")
|
| 498 |
+
msg_lines += traceback.format_list(list(extract()))
|
| 499 |
+
msg = "".join(msg_lines).rstrip("\n")
|
| 500 |
+
# Passing source= here means that if the user happens to have tracemalloc
|
| 501 |
+
# enabled and tracking where the coroutine was created, the warning will
|
| 502 |
+
# contain that traceback. This does mean that if they have *both*
|
| 503 |
+
# coroutine origin tracking *and* tracemalloc enabled, they'll get two
|
| 504 |
+
# partially-redundant tracebacks. If we wanted to be clever we could
|
| 505 |
+
# probably detect this case and avoid it, but for now we don't bother.
|
| 506 |
+
warn(msg, category=RuntimeWarning, stacklevel=2, source=coro)
|
| 507 |
+
|
| 508 |
+
|
| 509 |
+
# filters contains a sequence of filter 5-tuples
|
| 510 |
+
# The components of the 5-tuple are:
|
| 511 |
+
# - an action: error, ignore, always, default, module, or once
|
| 512 |
+
# - a compiled regex that must match the warning message
|
| 513 |
+
# - a class representing the warning category
|
| 514 |
+
# - a compiled regex that must match the module that is being warned
|
| 515 |
+
# - a line number for the line being warning, or 0 to mean any line
|
| 516 |
+
# If either if the compiled regexs are None, match anything.
|
| 517 |
+
try:
|
| 518 |
+
from _warnings import (filters, _defaultaction, _onceregistry,
|
| 519 |
+
warn, warn_explicit, _filters_mutated)
|
| 520 |
+
defaultaction = _defaultaction
|
| 521 |
+
onceregistry = _onceregistry
|
| 522 |
+
_warnings_defaults = True
|
| 523 |
+
except ImportError:
|
| 524 |
+
filters = []
|
| 525 |
+
defaultaction = "default"
|
| 526 |
+
onceregistry = {}
|
| 527 |
+
|
| 528 |
+
_filters_version = 1
|
| 529 |
+
|
| 530 |
+
def _filters_mutated():
|
| 531 |
+
global _filters_version
|
| 532 |
+
_filters_version += 1
|
| 533 |
+
|
| 534 |
+
_warnings_defaults = False
|
| 535 |
+
|
| 536 |
+
|
| 537 |
+
# Module initialization
|
| 538 |
+
_processoptions(sys.warnoptions)
|
| 539 |
+
if not _warnings_defaults:
|
| 540 |
+
# Several warning categories are ignored by default in regular builds
|
| 541 |
+
if not hasattr(sys, 'gettotalrefcount'):
|
| 542 |
+
filterwarnings("default", category=DeprecationWarning,
|
| 543 |
+
module="__main__", append=1)
|
| 544 |
+
simplefilter("ignore", category=DeprecationWarning, append=1)
|
| 545 |
+
simplefilter("ignore", category=PendingDeprecationWarning, append=1)
|
| 546 |
+
simplefilter("ignore", category=ImportWarning, append=1)
|
| 547 |
+
simplefilter("ignore", category=ResourceWarning, append=1)
|
| 548 |
+
|
| 549 |
+
del _warnings_defaults
|
lib/python3.10/wave.py
ADDED
|
@@ -0,0 +1,513 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Stuff to parse WAVE files.
|
| 2 |
+
|
| 3 |
+
Usage.
|
| 4 |
+
|
| 5 |
+
Reading WAVE files:
|
| 6 |
+
f = wave.open(file, 'r')
|
| 7 |
+
where file is either the name of a file or an open file pointer.
|
| 8 |
+
The open file pointer must have methods read(), seek(), and close().
|
| 9 |
+
When the setpos() and rewind() methods are not used, the seek()
|
| 10 |
+
method is not necessary.
|
| 11 |
+
|
| 12 |
+
This returns an instance of a class with the following public methods:
|
| 13 |
+
getnchannels() -- returns number of audio channels (1 for
|
| 14 |
+
mono, 2 for stereo)
|
| 15 |
+
getsampwidth() -- returns sample width in bytes
|
| 16 |
+
getframerate() -- returns sampling frequency
|
| 17 |
+
getnframes() -- returns number of audio frames
|
| 18 |
+
getcomptype() -- returns compression type ('NONE' for linear samples)
|
| 19 |
+
getcompname() -- returns human-readable version of
|
| 20 |
+
compression type ('not compressed' linear samples)
|
| 21 |
+
getparams() -- returns a namedtuple consisting of all of the
|
| 22 |
+
above in the above order
|
| 23 |
+
getmarkers() -- returns None (for compatibility with the
|
| 24 |
+
aifc module)
|
| 25 |
+
getmark(id) -- raises an error since the mark does not
|
| 26 |
+
exist (for compatibility with the aifc module)
|
| 27 |
+
readframes(n) -- returns at most n frames of audio
|
| 28 |
+
rewind() -- rewind to the beginning of the audio stream
|
| 29 |
+
setpos(pos) -- seek to the specified position
|
| 30 |
+
tell() -- return the current position
|
| 31 |
+
close() -- close the instance (make it unusable)
|
| 32 |
+
The position returned by tell() and the position given to setpos()
|
| 33 |
+
are compatible and have nothing to do with the actual position in the
|
| 34 |
+
file.
|
| 35 |
+
The close() method is called automatically when the class instance
|
| 36 |
+
is destroyed.
|
| 37 |
+
|
| 38 |
+
Writing WAVE files:
|
| 39 |
+
f = wave.open(file, 'w')
|
| 40 |
+
where file is either the name of a file or an open file pointer.
|
| 41 |
+
The open file pointer must have methods write(), tell(), seek(), and
|
| 42 |
+
close().
|
| 43 |
+
|
| 44 |
+
This returns an instance of a class with the following public methods:
|
| 45 |
+
setnchannels(n) -- set the number of channels
|
| 46 |
+
setsampwidth(n) -- set the sample width
|
| 47 |
+
setframerate(n) -- set the frame rate
|
| 48 |
+
setnframes(n) -- set the number of frames
|
| 49 |
+
setcomptype(type, name)
|
| 50 |
+
-- set the compression type and the
|
| 51 |
+
human-readable compression type
|
| 52 |
+
setparams(tuple)
|
| 53 |
+
-- set all parameters at once
|
| 54 |
+
tell() -- return current position in output file
|
| 55 |
+
writeframesraw(data)
|
| 56 |
+
-- write audio frames without patching up the
|
| 57 |
+
file header
|
| 58 |
+
writeframes(data)
|
| 59 |
+
-- write audio frames and patch up the file header
|
| 60 |
+
close() -- patch up the file header and close the
|
| 61 |
+
output file
|
| 62 |
+
You should set the parameters before the first writeframesraw or
|
| 63 |
+
writeframes. The total number of frames does not need to be set,
|
| 64 |
+
but when it is set to the correct value, the header does not have to
|
| 65 |
+
be patched up.
|
| 66 |
+
It is best to first set all parameters, perhaps possibly the
|
| 67 |
+
compression type, and then write audio frames using writeframesraw.
|
| 68 |
+
When all frames have been written, either call writeframes(b'') or
|
| 69 |
+
close() to patch up the sizes in the header.
|
| 70 |
+
The close() method is called automatically when the class instance
|
| 71 |
+
is destroyed.
|
| 72 |
+
"""
|
| 73 |
+
|
| 74 |
+
from chunk import Chunk
|
| 75 |
+
from collections import namedtuple
|
| 76 |
+
import audioop
|
| 77 |
+
import builtins
|
| 78 |
+
import struct
|
| 79 |
+
import sys
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
__all__ = ["open", "Error", "Wave_read", "Wave_write"]
|
| 83 |
+
|
| 84 |
+
class Error(Exception):
|
| 85 |
+
pass
|
| 86 |
+
|
| 87 |
+
WAVE_FORMAT_PCM = 0x0001
|
| 88 |
+
|
| 89 |
+
_array_fmts = None, 'b', 'h', None, 'i'
|
| 90 |
+
|
| 91 |
+
_wave_params = namedtuple('_wave_params',
|
| 92 |
+
'nchannels sampwidth framerate nframes comptype compname')
|
| 93 |
+
|
| 94 |
+
class Wave_read:
|
| 95 |
+
"""Variables used in this class:
|
| 96 |
+
|
| 97 |
+
These variables are available to the user though appropriate
|
| 98 |
+
methods of this class:
|
| 99 |
+
_file -- the open file with methods read(), close(), and seek()
|
| 100 |
+
set through the __init__() method
|
| 101 |
+
_nchannels -- the number of audio channels
|
| 102 |
+
available through the getnchannels() method
|
| 103 |
+
_nframes -- the number of audio frames
|
| 104 |
+
available through the getnframes() method
|
| 105 |
+
_sampwidth -- the number of bytes per audio sample
|
| 106 |
+
available through the getsampwidth() method
|
| 107 |
+
_framerate -- the sampling frequency
|
| 108 |
+
available through the getframerate() method
|
| 109 |
+
_comptype -- the AIFF-C compression type ('NONE' if AIFF)
|
| 110 |
+
available through the getcomptype() method
|
| 111 |
+
_compname -- the human-readable AIFF-C compression type
|
| 112 |
+
available through the getcomptype() method
|
| 113 |
+
_soundpos -- the position in the audio stream
|
| 114 |
+
available through the tell() method, set through the
|
| 115 |
+
setpos() method
|
| 116 |
+
|
| 117 |
+
These variables are used internally only:
|
| 118 |
+
_fmt_chunk_read -- 1 iff the FMT chunk has been read
|
| 119 |
+
_data_seek_needed -- 1 iff positioned correctly in audio
|
| 120 |
+
file for readframes()
|
| 121 |
+
_data_chunk -- instantiation of a chunk class for the DATA chunk
|
| 122 |
+
_framesize -- size of one frame in the file
|
| 123 |
+
"""
|
| 124 |
+
|
| 125 |
+
def initfp(self, file):
|
| 126 |
+
self._convert = None
|
| 127 |
+
self._soundpos = 0
|
| 128 |
+
self._file = Chunk(file, bigendian = 0)
|
| 129 |
+
if self._file.getname() != b'RIFF':
|
| 130 |
+
raise Error('file does not start with RIFF id')
|
| 131 |
+
if self._file.read(4) != b'WAVE':
|
| 132 |
+
raise Error('not a WAVE file')
|
| 133 |
+
self._fmt_chunk_read = 0
|
| 134 |
+
self._data_chunk = None
|
| 135 |
+
while 1:
|
| 136 |
+
self._data_seek_needed = 1
|
| 137 |
+
try:
|
| 138 |
+
chunk = Chunk(self._file, bigendian = 0)
|
| 139 |
+
except EOFError:
|
| 140 |
+
break
|
| 141 |
+
chunkname = chunk.getname()
|
| 142 |
+
if chunkname == b'fmt ':
|
| 143 |
+
self._read_fmt_chunk(chunk)
|
| 144 |
+
self._fmt_chunk_read = 1
|
| 145 |
+
elif chunkname == b'data':
|
| 146 |
+
if not self._fmt_chunk_read:
|
| 147 |
+
raise Error('data chunk before fmt chunk')
|
| 148 |
+
self._data_chunk = chunk
|
| 149 |
+
self._nframes = chunk.chunksize // self._framesize
|
| 150 |
+
self._data_seek_needed = 0
|
| 151 |
+
break
|
| 152 |
+
chunk.skip()
|
| 153 |
+
if not self._fmt_chunk_read or not self._data_chunk:
|
| 154 |
+
raise Error('fmt chunk and/or data chunk missing')
|
| 155 |
+
|
| 156 |
+
def __init__(self, f):
|
| 157 |
+
self._i_opened_the_file = None
|
| 158 |
+
if isinstance(f, str):
|
| 159 |
+
f = builtins.open(f, 'rb')
|
| 160 |
+
self._i_opened_the_file = f
|
| 161 |
+
# else, assume it is an open file object already
|
| 162 |
+
try:
|
| 163 |
+
self.initfp(f)
|
| 164 |
+
except:
|
| 165 |
+
if self._i_opened_the_file:
|
| 166 |
+
f.close()
|
| 167 |
+
raise
|
| 168 |
+
|
| 169 |
+
def __del__(self):
|
| 170 |
+
self.close()
|
| 171 |
+
|
| 172 |
+
def __enter__(self):
|
| 173 |
+
return self
|
| 174 |
+
|
| 175 |
+
def __exit__(self, *args):
|
| 176 |
+
self.close()
|
| 177 |
+
|
| 178 |
+
#
|
| 179 |
+
# User visible methods.
|
| 180 |
+
#
|
| 181 |
+
def getfp(self):
|
| 182 |
+
return self._file
|
| 183 |
+
|
| 184 |
+
def rewind(self):
|
| 185 |
+
self._data_seek_needed = 1
|
| 186 |
+
self._soundpos = 0
|
| 187 |
+
|
| 188 |
+
def close(self):
|
| 189 |
+
self._file = None
|
| 190 |
+
file = self._i_opened_the_file
|
| 191 |
+
if file:
|
| 192 |
+
self._i_opened_the_file = None
|
| 193 |
+
file.close()
|
| 194 |
+
|
| 195 |
+
def tell(self):
|
| 196 |
+
return self._soundpos
|
| 197 |
+
|
| 198 |
+
def getnchannels(self):
|
| 199 |
+
return self._nchannels
|
| 200 |
+
|
| 201 |
+
def getnframes(self):
|
| 202 |
+
return self._nframes
|
| 203 |
+
|
| 204 |
+
def getsampwidth(self):
|
| 205 |
+
return self._sampwidth
|
| 206 |
+
|
| 207 |
+
def getframerate(self):
|
| 208 |
+
return self._framerate
|
| 209 |
+
|
| 210 |
+
def getcomptype(self):
|
| 211 |
+
return self._comptype
|
| 212 |
+
|
| 213 |
+
def getcompname(self):
|
| 214 |
+
return self._compname
|
| 215 |
+
|
| 216 |
+
def getparams(self):
|
| 217 |
+
return _wave_params(self.getnchannels(), self.getsampwidth(),
|
| 218 |
+
self.getframerate(), self.getnframes(),
|
| 219 |
+
self.getcomptype(), self.getcompname())
|
| 220 |
+
|
| 221 |
+
def getmarkers(self):
|
| 222 |
+
return None
|
| 223 |
+
|
| 224 |
+
def getmark(self, id):
|
| 225 |
+
raise Error('no marks')
|
| 226 |
+
|
| 227 |
+
def setpos(self, pos):
|
| 228 |
+
if pos < 0 or pos > self._nframes:
|
| 229 |
+
raise Error('position not in range')
|
| 230 |
+
self._soundpos = pos
|
| 231 |
+
self._data_seek_needed = 1
|
| 232 |
+
|
| 233 |
+
def readframes(self, nframes):
|
| 234 |
+
if self._data_seek_needed:
|
| 235 |
+
self._data_chunk.seek(0, 0)
|
| 236 |
+
pos = self._soundpos * self._framesize
|
| 237 |
+
if pos:
|
| 238 |
+
self._data_chunk.seek(pos, 0)
|
| 239 |
+
self._data_seek_needed = 0
|
| 240 |
+
if nframes == 0:
|
| 241 |
+
return b''
|
| 242 |
+
data = self._data_chunk.read(nframes * self._framesize)
|
| 243 |
+
if self._sampwidth != 1 and sys.byteorder == 'big':
|
| 244 |
+
data = audioop.byteswap(data, self._sampwidth)
|
| 245 |
+
if self._convert and data:
|
| 246 |
+
data = self._convert(data)
|
| 247 |
+
self._soundpos = self._soundpos + len(data) // (self._nchannels * self._sampwidth)
|
| 248 |
+
return data
|
| 249 |
+
|
| 250 |
+
#
|
| 251 |
+
# Internal methods.
|
| 252 |
+
#
|
| 253 |
+
|
| 254 |
+
def _read_fmt_chunk(self, chunk):
|
| 255 |
+
try:
|
| 256 |
+
wFormatTag, self._nchannels, self._framerate, dwAvgBytesPerSec, wBlockAlign = struct.unpack_from('<HHLLH', chunk.read(14))
|
| 257 |
+
except struct.error:
|
| 258 |
+
raise EOFError from None
|
| 259 |
+
if wFormatTag == WAVE_FORMAT_PCM:
|
| 260 |
+
try:
|
| 261 |
+
sampwidth = struct.unpack_from('<H', chunk.read(2))[0]
|
| 262 |
+
except struct.error:
|
| 263 |
+
raise EOFError from None
|
| 264 |
+
self._sampwidth = (sampwidth + 7) // 8
|
| 265 |
+
if not self._sampwidth:
|
| 266 |
+
raise Error('bad sample width')
|
| 267 |
+
else:
|
| 268 |
+
raise Error('unknown format: %r' % (wFormatTag,))
|
| 269 |
+
if not self._nchannels:
|
| 270 |
+
raise Error('bad # of channels')
|
| 271 |
+
self._framesize = self._nchannels * self._sampwidth
|
| 272 |
+
self._comptype = 'NONE'
|
| 273 |
+
self._compname = 'not compressed'
|
| 274 |
+
|
| 275 |
+
class Wave_write:
|
| 276 |
+
"""Variables used in this class:
|
| 277 |
+
|
| 278 |
+
These variables are user settable through appropriate methods
|
| 279 |
+
of this class:
|
| 280 |
+
_file -- the open file with methods write(), close(), tell(), seek()
|
| 281 |
+
set through the __init__() method
|
| 282 |
+
_comptype -- the AIFF-C compression type ('NONE' in AIFF)
|
| 283 |
+
set through the setcomptype() or setparams() method
|
| 284 |
+
_compname -- the human-readable AIFF-C compression type
|
| 285 |
+
set through the setcomptype() or setparams() method
|
| 286 |
+
_nchannels -- the number of audio channels
|
| 287 |
+
set through the setnchannels() or setparams() method
|
| 288 |
+
_sampwidth -- the number of bytes per audio sample
|
| 289 |
+
set through the setsampwidth() or setparams() method
|
| 290 |
+
_framerate -- the sampling frequency
|
| 291 |
+
set through the setframerate() or setparams() method
|
| 292 |
+
_nframes -- the number of audio frames written to the header
|
| 293 |
+
set through the setnframes() or setparams() method
|
| 294 |
+
|
| 295 |
+
These variables are used internally only:
|
| 296 |
+
_datalength -- the size of the audio samples written to the header
|
| 297 |
+
_nframeswritten -- the number of frames actually written
|
| 298 |
+
_datawritten -- the size of the audio samples actually written
|
| 299 |
+
"""
|
| 300 |
+
|
| 301 |
+
def __init__(self, f):
|
| 302 |
+
self._i_opened_the_file = None
|
| 303 |
+
if isinstance(f, str):
|
| 304 |
+
f = builtins.open(f, 'wb')
|
| 305 |
+
self._i_opened_the_file = f
|
| 306 |
+
try:
|
| 307 |
+
self.initfp(f)
|
| 308 |
+
except:
|
| 309 |
+
if self._i_opened_the_file:
|
| 310 |
+
f.close()
|
| 311 |
+
raise
|
| 312 |
+
|
| 313 |
+
def initfp(self, file):
|
| 314 |
+
self._file = file
|
| 315 |
+
self._convert = None
|
| 316 |
+
self._nchannels = 0
|
| 317 |
+
self._sampwidth = 0
|
| 318 |
+
self._framerate = 0
|
| 319 |
+
self._nframes = 0
|
| 320 |
+
self._nframeswritten = 0
|
| 321 |
+
self._datawritten = 0
|
| 322 |
+
self._datalength = 0
|
| 323 |
+
self._headerwritten = False
|
| 324 |
+
|
| 325 |
+
def __del__(self):
|
| 326 |
+
self.close()
|
| 327 |
+
|
| 328 |
+
def __enter__(self):
|
| 329 |
+
return self
|
| 330 |
+
|
| 331 |
+
def __exit__(self, *args):
|
| 332 |
+
self.close()
|
| 333 |
+
|
| 334 |
+
#
|
| 335 |
+
# User visible methods.
|
| 336 |
+
#
|
| 337 |
+
def setnchannels(self, nchannels):
|
| 338 |
+
if self._datawritten:
|
| 339 |
+
raise Error('cannot change parameters after starting to write')
|
| 340 |
+
if nchannels < 1:
|
| 341 |
+
raise Error('bad # of channels')
|
| 342 |
+
self._nchannels = nchannels
|
| 343 |
+
|
| 344 |
+
def getnchannels(self):
|
| 345 |
+
if not self._nchannels:
|
| 346 |
+
raise Error('number of channels not set')
|
| 347 |
+
return self._nchannels
|
| 348 |
+
|
| 349 |
+
def setsampwidth(self, sampwidth):
|
| 350 |
+
if self._datawritten:
|
| 351 |
+
raise Error('cannot change parameters after starting to write')
|
| 352 |
+
if sampwidth < 1 or sampwidth > 4:
|
| 353 |
+
raise Error('bad sample width')
|
| 354 |
+
self._sampwidth = sampwidth
|
| 355 |
+
|
| 356 |
+
def getsampwidth(self):
|
| 357 |
+
if not self._sampwidth:
|
| 358 |
+
raise Error('sample width not set')
|
| 359 |
+
return self._sampwidth
|
| 360 |
+
|
| 361 |
+
def setframerate(self, framerate):
|
| 362 |
+
if self._datawritten:
|
| 363 |
+
raise Error('cannot change parameters after starting to write')
|
| 364 |
+
if framerate <= 0:
|
| 365 |
+
raise Error('bad frame rate')
|
| 366 |
+
self._framerate = int(round(framerate))
|
| 367 |
+
|
| 368 |
+
def getframerate(self):
|
| 369 |
+
if not self._framerate:
|
| 370 |
+
raise Error('frame rate not set')
|
| 371 |
+
return self._framerate
|
| 372 |
+
|
| 373 |
+
def setnframes(self, nframes):
|
| 374 |
+
if self._datawritten:
|
| 375 |
+
raise Error('cannot change parameters after starting to write')
|
| 376 |
+
self._nframes = nframes
|
| 377 |
+
|
| 378 |
+
def getnframes(self):
|
| 379 |
+
return self._nframeswritten
|
| 380 |
+
|
| 381 |
+
def setcomptype(self, comptype, compname):
|
| 382 |
+
if self._datawritten:
|
| 383 |
+
raise Error('cannot change parameters after starting to write')
|
| 384 |
+
if comptype not in ('NONE',):
|
| 385 |
+
raise Error('unsupported compression type')
|
| 386 |
+
self._comptype = comptype
|
| 387 |
+
self._compname = compname
|
| 388 |
+
|
| 389 |
+
def getcomptype(self):
|
| 390 |
+
return self._comptype
|
| 391 |
+
|
| 392 |
+
def getcompname(self):
|
| 393 |
+
return self._compname
|
| 394 |
+
|
| 395 |
+
def setparams(self, params):
|
| 396 |
+
nchannels, sampwidth, framerate, nframes, comptype, compname = params
|
| 397 |
+
if self._datawritten:
|
| 398 |
+
raise Error('cannot change parameters after starting to write')
|
| 399 |
+
self.setnchannels(nchannels)
|
| 400 |
+
self.setsampwidth(sampwidth)
|
| 401 |
+
self.setframerate(framerate)
|
| 402 |
+
self.setnframes(nframes)
|
| 403 |
+
self.setcomptype(comptype, compname)
|
| 404 |
+
|
| 405 |
+
def getparams(self):
|
| 406 |
+
if not self._nchannels or not self._sampwidth or not self._framerate:
|
| 407 |
+
raise Error('not all parameters set')
|
| 408 |
+
return _wave_params(self._nchannels, self._sampwidth, self._framerate,
|
| 409 |
+
self._nframes, self._comptype, self._compname)
|
| 410 |
+
|
| 411 |
+
def setmark(self, id, pos, name):
|
| 412 |
+
raise Error('setmark() not supported')
|
| 413 |
+
|
| 414 |
+
def getmark(self, id):
|
| 415 |
+
raise Error('no marks')
|
| 416 |
+
|
| 417 |
+
def getmarkers(self):
|
| 418 |
+
return None
|
| 419 |
+
|
| 420 |
+
def tell(self):
|
| 421 |
+
return self._nframeswritten
|
| 422 |
+
|
| 423 |
+
def writeframesraw(self, data):
|
| 424 |
+
if not isinstance(data, (bytes, bytearray)):
|
| 425 |
+
data = memoryview(data).cast('B')
|
| 426 |
+
self._ensure_header_written(len(data))
|
| 427 |
+
nframes = len(data) // (self._sampwidth * self._nchannels)
|
| 428 |
+
if self._convert:
|
| 429 |
+
data = self._convert(data)
|
| 430 |
+
if self._sampwidth != 1 and sys.byteorder == 'big':
|
| 431 |
+
data = audioop.byteswap(data, self._sampwidth)
|
| 432 |
+
self._file.write(data)
|
| 433 |
+
self._datawritten += len(data)
|
| 434 |
+
self._nframeswritten = self._nframeswritten + nframes
|
| 435 |
+
|
| 436 |
+
def writeframes(self, data):
|
| 437 |
+
self.writeframesraw(data)
|
| 438 |
+
if self._datalength != self._datawritten:
|
| 439 |
+
self._patchheader()
|
| 440 |
+
|
| 441 |
+
def close(self):
|
| 442 |
+
try:
|
| 443 |
+
if self._file:
|
| 444 |
+
self._ensure_header_written(0)
|
| 445 |
+
if self._datalength != self._datawritten:
|
| 446 |
+
self._patchheader()
|
| 447 |
+
self._file.flush()
|
| 448 |
+
finally:
|
| 449 |
+
self._file = None
|
| 450 |
+
file = self._i_opened_the_file
|
| 451 |
+
if file:
|
| 452 |
+
self._i_opened_the_file = None
|
| 453 |
+
file.close()
|
| 454 |
+
|
| 455 |
+
#
|
| 456 |
+
# Internal methods.
|
| 457 |
+
#
|
| 458 |
+
|
| 459 |
+
def _ensure_header_written(self, datasize):
|
| 460 |
+
if not self._headerwritten:
|
| 461 |
+
if not self._nchannels:
|
| 462 |
+
raise Error('# channels not specified')
|
| 463 |
+
if not self._sampwidth:
|
| 464 |
+
raise Error('sample width not specified')
|
| 465 |
+
if not self._framerate:
|
| 466 |
+
raise Error('sampling rate not specified')
|
| 467 |
+
self._write_header(datasize)
|
| 468 |
+
|
| 469 |
+
def _write_header(self, initlength):
|
| 470 |
+
assert not self._headerwritten
|
| 471 |
+
self._file.write(b'RIFF')
|
| 472 |
+
if not self._nframes:
|
| 473 |
+
self._nframes = initlength // (self._nchannels * self._sampwidth)
|
| 474 |
+
self._datalength = self._nframes * self._nchannels * self._sampwidth
|
| 475 |
+
try:
|
| 476 |
+
self._form_length_pos = self._file.tell()
|
| 477 |
+
except (AttributeError, OSError):
|
| 478 |
+
self._form_length_pos = None
|
| 479 |
+
self._file.write(struct.pack('<L4s4sLHHLLHH4s',
|
| 480 |
+
36 + self._datalength, b'WAVE', b'fmt ', 16,
|
| 481 |
+
WAVE_FORMAT_PCM, self._nchannels, self._framerate,
|
| 482 |
+
self._nchannels * self._framerate * self._sampwidth,
|
| 483 |
+
self._nchannels * self._sampwidth,
|
| 484 |
+
self._sampwidth * 8, b'data'))
|
| 485 |
+
if self._form_length_pos is not None:
|
| 486 |
+
self._data_length_pos = self._file.tell()
|
| 487 |
+
self._file.write(struct.pack('<L', self._datalength))
|
| 488 |
+
self._headerwritten = True
|
| 489 |
+
|
| 490 |
+
def _patchheader(self):
|
| 491 |
+
assert self._headerwritten
|
| 492 |
+
if self._datawritten == self._datalength:
|
| 493 |
+
return
|
| 494 |
+
curpos = self._file.tell()
|
| 495 |
+
self._file.seek(self._form_length_pos, 0)
|
| 496 |
+
self._file.write(struct.pack('<L', 36 + self._datawritten))
|
| 497 |
+
self._file.seek(self._data_length_pos, 0)
|
| 498 |
+
self._file.write(struct.pack('<L', self._datawritten))
|
| 499 |
+
self._file.seek(curpos, 0)
|
| 500 |
+
self._datalength = self._datawritten
|
| 501 |
+
|
| 502 |
+
def open(f, mode=None):
|
| 503 |
+
if mode is None:
|
| 504 |
+
if hasattr(f, 'mode'):
|
| 505 |
+
mode = f.mode
|
| 506 |
+
else:
|
| 507 |
+
mode = 'rb'
|
| 508 |
+
if mode in ('r', 'rb'):
|
| 509 |
+
return Wave_read(f)
|
| 510 |
+
elif mode in ('w', 'wb'):
|
| 511 |
+
return Wave_write(f)
|
| 512 |
+
else:
|
| 513 |
+
raise Error("mode must be 'r', 'rb', 'w', or 'wb'")
|
lib/python3.10/weakref.py
ADDED
|
@@ -0,0 +1,675 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Weak reference support for Python.
|
| 2 |
+
|
| 3 |
+
This module is an implementation of PEP 205:
|
| 4 |
+
|
| 5 |
+
https://www.python.org/dev/peps/pep-0205/
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
# Naming convention: Variables named "wr" are weak reference objects;
|
| 9 |
+
# they are called this instead of "ref" to avoid name collisions with
|
| 10 |
+
# the module-global ref() function imported from _weakref.
|
| 11 |
+
|
| 12 |
+
from _weakref import (
|
| 13 |
+
getweakrefcount,
|
| 14 |
+
getweakrefs,
|
| 15 |
+
ref,
|
| 16 |
+
proxy,
|
| 17 |
+
CallableProxyType,
|
| 18 |
+
ProxyType,
|
| 19 |
+
ReferenceType,
|
| 20 |
+
_remove_dead_weakref)
|
| 21 |
+
|
| 22 |
+
from _weakrefset import WeakSet, _IterationGuard
|
| 23 |
+
|
| 24 |
+
import _collections_abc # Import after _weakref to avoid circular import.
|
| 25 |
+
import sys
|
| 26 |
+
import itertools
|
| 27 |
+
|
| 28 |
+
ProxyTypes = (ProxyType, CallableProxyType)
|
| 29 |
+
|
| 30 |
+
__all__ = ["ref", "proxy", "getweakrefcount", "getweakrefs",
|
| 31 |
+
"WeakKeyDictionary", "ReferenceType", "ProxyType",
|
| 32 |
+
"CallableProxyType", "ProxyTypes", "WeakValueDictionary",
|
| 33 |
+
"WeakSet", "WeakMethod", "finalize"]
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
_collections_abc.Set.register(WeakSet)
|
| 37 |
+
_collections_abc.MutableSet.register(WeakSet)
|
| 38 |
+
|
| 39 |
+
class WeakMethod(ref):
|
| 40 |
+
"""
|
| 41 |
+
A custom `weakref.ref` subclass which simulates a weak reference to
|
| 42 |
+
a bound method, working around the lifetime problem of bound methods.
|
| 43 |
+
"""
|
| 44 |
+
|
| 45 |
+
__slots__ = "_func_ref", "_meth_type", "_alive", "__weakref__"
|
| 46 |
+
|
| 47 |
+
def __new__(cls, meth, callback=None):
|
| 48 |
+
try:
|
| 49 |
+
obj = meth.__self__
|
| 50 |
+
func = meth.__func__
|
| 51 |
+
except AttributeError:
|
| 52 |
+
raise TypeError("argument should be a bound method, not {}"
|
| 53 |
+
.format(type(meth))) from None
|
| 54 |
+
def _cb(arg):
|
| 55 |
+
# The self-weakref trick is needed to avoid creating a reference
|
| 56 |
+
# cycle.
|
| 57 |
+
self = self_wr()
|
| 58 |
+
if self._alive:
|
| 59 |
+
self._alive = False
|
| 60 |
+
if callback is not None:
|
| 61 |
+
callback(self)
|
| 62 |
+
self = ref.__new__(cls, obj, _cb)
|
| 63 |
+
self._func_ref = ref(func, _cb)
|
| 64 |
+
self._meth_type = type(meth)
|
| 65 |
+
self._alive = True
|
| 66 |
+
self_wr = ref(self)
|
| 67 |
+
return self
|
| 68 |
+
|
| 69 |
+
def __call__(self):
|
| 70 |
+
obj = super().__call__()
|
| 71 |
+
func = self._func_ref()
|
| 72 |
+
if obj is None or func is None:
|
| 73 |
+
return None
|
| 74 |
+
return self._meth_type(func, obj)
|
| 75 |
+
|
| 76 |
+
def __eq__(self, other):
|
| 77 |
+
if isinstance(other, WeakMethod):
|
| 78 |
+
if not self._alive or not other._alive:
|
| 79 |
+
return self is other
|
| 80 |
+
return ref.__eq__(self, other) and self._func_ref == other._func_ref
|
| 81 |
+
return NotImplemented
|
| 82 |
+
|
| 83 |
+
def __ne__(self, other):
|
| 84 |
+
if isinstance(other, WeakMethod):
|
| 85 |
+
if not self._alive or not other._alive:
|
| 86 |
+
return self is not other
|
| 87 |
+
return ref.__ne__(self, other) or self._func_ref != other._func_ref
|
| 88 |
+
return NotImplemented
|
| 89 |
+
|
| 90 |
+
__hash__ = ref.__hash__
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
class WeakValueDictionary(_collections_abc.MutableMapping):
|
| 94 |
+
"""Mapping class that references values weakly.
|
| 95 |
+
|
| 96 |
+
Entries in the dictionary will be discarded when no strong
|
| 97 |
+
reference to the value exists anymore
|
| 98 |
+
"""
|
| 99 |
+
# We inherit the constructor without worrying about the input
|
| 100 |
+
# dictionary; since it uses our .update() method, we get the right
|
| 101 |
+
# checks (if the other dictionary is a WeakValueDictionary,
|
| 102 |
+
# objects are unwrapped on the way out, and we always wrap on the
|
| 103 |
+
# way in).
|
| 104 |
+
|
| 105 |
+
def __init__(self, other=(), /, **kw):
|
| 106 |
+
def remove(wr, selfref=ref(self), _atomic_removal=_remove_dead_weakref):
|
| 107 |
+
self = selfref()
|
| 108 |
+
if self is not None:
|
| 109 |
+
if self._iterating:
|
| 110 |
+
self._pending_removals.append(wr.key)
|
| 111 |
+
else:
|
| 112 |
+
# Atomic removal is necessary since this function
|
| 113 |
+
# can be called asynchronously by the GC
|
| 114 |
+
_atomic_removal(self.data, wr.key)
|
| 115 |
+
self._remove = remove
|
| 116 |
+
# A list of keys to be removed
|
| 117 |
+
self._pending_removals = []
|
| 118 |
+
self._iterating = set()
|
| 119 |
+
self.data = {}
|
| 120 |
+
self.update(other, **kw)
|
| 121 |
+
|
| 122 |
+
def _commit_removals(self, _atomic_removal=_remove_dead_weakref):
|
| 123 |
+
pop = self._pending_removals.pop
|
| 124 |
+
d = self.data
|
| 125 |
+
# We shouldn't encounter any KeyError, because this method should
|
| 126 |
+
# always be called *before* mutating the dict.
|
| 127 |
+
while True:
|
| 128 |
+
try:
|
| 129 |
+
key = pop()
|
| 130 |
+
except IndexError:
|
| 131 |
+
return
|
| 132 |
+
_atomic_removal(d, key)
|
| 133 |
+
|
| 134 |
+
def __getitem__(self, key):
|
| 135 |
+
if self._pending_removals:
|
| 136 |
+
self._commit_removals()
|
| 137 |
+
o = self.data[key]()
|
| 138 |
+
if o is None:
|
| 139 |
+
raise KeyError(key)
|
| 140 |
+
else:
|
| 141 |
+
return o
|
| 142 |
+
|
| 143 |
+
def __delitem__(self, key):
|
| 144 |
+
if self._pending_removals:
|
| 145 |
+
self._commit_removals()
|
| 146 |
+
del self.data[key]
|
| 147 |
+
|
| 148 |
+
def __len__(self):
|
| 149 |
+
if self._pending_removals:
|
| 150 |
+
self._commit_removals()
|
| 151 |
+
return len(self.data)
|
| 152 |
+
|
| 153 |
+
def __contains__(self, key):
|
| 154 |
+
if self._pending_removals:
|
| 155 |
+
self._commit_removals()
|
| 156 |
+
try:
|
| 157 |
+
o = self.data[key]()
|
| 158 |
+
except KeyError:
|
| 159 |
+
return False
|
| 160 |
+
return o is not None
|
| 161 |
+
|
| 162 |
+
def __repr__(self):
|
| 163 |
+
return "<%s at %#x>" % (self.__class__.__name__, id(self))
|
| 164 |
+
|
| 165 |
+
def __setitem__(self, key, value):
|
| 166 |
+
if self._pending_removals:
|
| 167 |
+
self._commit_removals()
|
| 168 |
+
self.data[key] = KeyedRef(value, self._remove, key)
|
| 169 |
+
|
| 170 |
+
def copy(self):
|
| 171 |
+
if self._pending_removals:
|
| 172 |
+
self._commit_removals()
|
| 173 |
+
new = WeakValueDictionary()
|
| 174 |
+
with _IterationGuard(self):
|
| 175 |
+
for key, wr in self.data.items():
|
| 176 |
+
o = wr()
|
| 177 |
+
if o is not None:
|
| 178 |
+
new[key] = o
|
| 179 |
+
return new
|
| 180 |
+
|
| 181 |
+
__copy__ = copy
|
| 182 |
+
|
| 183 |
+
def __deepcopy__(self, memo):
|
| 184 |
+
from copy import deepcopy
|
| 185 |
+
if self._pending_removals:
|
| 186 |
+
self._commit_removals()
|
| 187 |
+
new = self.__class__()
|
| 188 |
+
with _IterationGuard(self):
|
| 189 |
+
for key, wr in self.data.items():
|
| 190 |
+
o = wr()
|
| 191 |
+
if o is not None:
|
| 192 |
+
new[deepcopy(key, memo)] = o
|
| 193 |
+
return new
|
| 194 |
+
|
| 195 |
+
def get(self, key, default=None):
|
| 196 |
+
if self._pending_removals:
|
| 197 |
+
self._commit_removals()
|
| 198 |
+
try:
|
| 199 |
+
wr = self.data[key]
|
| 200 |
+
except KeyError:
|
| 201 |
+
return default
|
| 202 |
+
else:
|
| 203 |
+
o = wr()
|
| 204 |
+
if o is None:
|
| 205 |
+
# This should only happen
|
| 206 |
+
return default
|
| 207 |
+
else:
|
| 208 |
+
return o
|
| 209 |
+
|
| 210 |
+
def items(self):
|
| 211 |
+
if self._pending_removals:
|
| 212 |
+
self._commit_removals()
|
| 213 |
+
with _IterationGuard(self):
|
| 214 |
+
for k, wr in self.data.items():
|
| 215 |
+
v = wr()
|
| 216 |
+
if v is not None:
|
| 217 |
+
yield k, v
|
| 218 |
+
|
| 219 |
+
def keys(self):
|
| 220 |
+
if self._pending_removals:
|
| 221 |
+
self._commit_removals()
|
| 222 |
+
with _IterationGuard(self):
|
| 223 |
+
for k, wr in self.data.items():
|
| 224 |
+
if wr() is not None:
|
| 225 |
+
yield k
|
| 226 |
+
|
| 227 |
+
__iter__ = keys
|
| 228 |
+
|
| 229 |
+
def itervaluerefs(self):
|
| 230 |
+
"""Return an iterator that yields the weak references to the values.
|
| 231 |
+
|
| 232 |
+
The references are not guaranteed to be 'live' at the time
|
| 233 |
+
they are used, so the result of calling the references needs
|
| 234 |
+
to be checked before being used. This can be used to avoid
|
| 235 |
+
creating references that will cause the garbage collector to
|
| 236 |
+
keep the values around longer than needed.
|
| 237 |
+
|
| 238 |
+
"""
|
| 239 |
+
if self._pending_removals:
|
| 240 |
+
self._commit_removals()
|
| 241 |
+
with _IterationGuard(self):
|
| 242 |
+
yield from self.data.values()
|
| 243 |
+
|
| 244 |
+
def values(self):
|
| 245 |
+
if self._pending_removals:
|
| 246 |
+
self._commit_removals()
|
| 247 |
+
with _IterationGuard(self):
|
| 248 |
+
for wr in self.data.values():
|
| 249 |
+
obj = wr()
|
| 250 |
+
if obj is not None:
|
| 251 |
+
yield obj
|
| 252 |
+
|
| 253 |
+
def popitem(self):
|
| 254 |
+
if self._pending_removals:
|
| 255 |
+
self._commit_removals()
|
| 256 |
+
while True:
|
| 257 |
+
key, wr = self.data.popitem()
|
| 258 |
+
o = wr()
|
| 259 |
+
if o is not None:
|
| 260 |
+
return key, o
|
| 261 |
+
|
| 262 |
+
def pop(self, key, *args):
|
| 263 |
+
if self._pending_removals:
|
| 264 |
+
self._commit_removals()
|
| 265 |
+
try:
|
| 266 |
+
o = self.data.pop(key)()
|
| 267 |
+
except KeyError:
|
| 268 |
+
o = None
|
| 269 |
+
if o is None:
|
| 270 |
+
if args:
|
| 271 |
+
return args[0]
|
| 272 |
+
else:
|
| 273 |
+
raise KeyError(key)
|
| 274 |
+
else:
|
| 275 |
+
return o
|
| 276 |
+
|
| 277 |
+
def setdefault(self, key, default=None):
|
| 278 |
+
try:
|
| 279 |
+
o = self.data[key]()
|
| 280 |
+
except KeyError:
|
| 281 |
+
o = None
|
| 282 |
+
if o is None:
|
| 283 |
+
if self._pending_removals:
|
| 284 |
+
self._commit_removals()
|
| 285 |
+
self.data[key] = KeyedRef(default, self._remove, key)
|
| 286 |
+
return default
|
| 287 |
+
else:
|
| 288 |
+
return o
|
| 289 |
+
|
| 290 |
+
def update(self, other=None, /, **kwargs):
|
| 291 |
+
if self._pending_removals:
|
| 292 |
+
self._commit_removals()
|
| 293 |
+
d = self.data
|
| 294 |
+
if other is not None:
|
| 295 |
+
if not hasattr(other, "items"):
|
| 296 |
+
other = dict(other)
|
| 297 |
+
for key, o in other.items():
|
| 298 |
+
d[key] = KeyedRef(o, self._remove, key)
|
| 299 |
+
for key, o in kwargs.items():
|
| 300 |
+
d[key] = KeyedRef(o, self._remove, key)
|
| 301 |
+
|
| 302 |
+
def valuerefs(self):
|
| 303 |
+
"""Return a list of weak references to the values.
|
| 304 |
+
|
| 305 |
+
The references are not guaranteed to be 'live' at the time
|
| 306 |
+
they are used, so the result of calling the references needs
|
| 307 |
+
to be checked before being used. This can be used to avoid
|
| 308 |
+
creating references that will cause the garbage collector to
|
| 309 |
+
keep the values around longer than needed.
|
| 310 |
+
|
| 311 |
+
"""
|
| 312 |
+
if self._pending_removals:
|
| 313 |
+
self._commit_removals()
|
| 314 |
+
return list(self.data.values())
|
| 315 |
+
|
| 316 |
+
def __ior__(self, other):
|
| 317 |
+
self.update(other)
|
| 318 |
+
return self
|
| 319 |
+
|
| 320 |
+
def __or__(self, other):
|
| 321 |
+
if isinstance(other, _collections_abc.Mapping):
|
| 322 |
+
c = self.copy()
|
| 323 |
+
c.update(other)
|
| 324 |
+
return c
|
| 325 |
+
return NotImplemented
|
| 326 |
+
|
| 327 |
+
def __ror__(self, other):
|
| 328 |
+
if isinstance(other, _collections_abc.Mapping):
|
| 329 |
+
c = self.__class__()
|
| 330 |
+
c.update(other)
|
| 331 |
+
c.update(self)
|
| 332 |
+
return c
|
| 333 |
+
return NotImplemented
|
| 334 |
+
|
| 335 |
+
|
| 336 |
+
class KeyedRef(ref):
|
| 337 |
+
"""Specialized reference that includes a key corresponding to the value.
|
| 338 |
+
|
| 339 |
+
This is used in the WeakValueDictionary to avoid having to create
|
| 340 |
+
a function object for each key stored in the mapping. A shared
|
| 341 |
+
callback object can use the 'key' attribute of a KeyedRef instead
|
| 342 |
+
of getting a reference to the key from an enclosing scope.
|
| 343 |
+
|
| 344 |
+
"""
|
| 345 |
+
|
| 346 |
+
__slots__ = "key",
|
| 347 |
+
|
| 348 |
+
def __new__(type, ob, callback, key):
|
| 349 |
+
self = ref.__new__(type, ob, callback)
|
| 350 |
+
self.key = key
|
| 351 |
+
return self
|
| 352 |
+
|
| 353 |
+
def __init__(self, ob, callback, key):
|
| 354 |
+
super().__init__(ob, callback)
|
| 355 |
+
|
| 356 |
+
|
| 357 |
+
class WeakKeyDictionary(_collections_abc.MutableMapping):
|
| 358 |
+
""" Mapping class that references keys weakly.
|
| 359 |
+
|
| 360 |
+
Entries in the dictionary will be discarded when there is no
|
| 361 |
+
longer a strong reference to the key. This can be used to
|
| 362 |
+
associate additional data with an object owned by other parts of
|
| 363 |
+
an application without adding attributes to those objects. This
|
| 364 |
+
can be especially useful with objects that override attribute
|
| 365 |
+
accesses.
|
| 366 |
+
"""
|
| 367 |
+
|
| 368 |
+
def __init__(self, dict=None):
|
| 369 |
+
self.data = {}
|
| 370 |
+
def remove(k, selfref=ref(self)):
|
| 371 |
+
self = selfref()
|
| 372 |
+
if self is not None:
|
| 373 |
+
if self._iterating:
|
| 374 |
+
self._pending_removals.append(k)
|
| 375 |
+
else:
|
| 376 |
+
try:
|
| 377 |
+
del self.data[k]
|
| 378 |
+
except KeyError:
|
| 379 |
+
pass
|
| 380 |
+
self._remove = remove
|
| 381 |
+
# A list of dead weakrefs (keys to be removed)
|
| 382 |
+
self._pending_removals = []
|
| 383 |
+
self._iterating = set()
|
| 384 |
+
self._dirty_len = False
|
| 385 |
+
if dict is not None:
|
| 386 |
+
self.update(dict)
|
| 387 |
+
|
| 388 |
+
def _commit_removals(self):
|
| 389 |
+
# NOTE: We don't need to call this method before mutating the dict,
|
| 390 |
+
# because a dead weakref never compares equal to a live weakref,
|
| 391 |
+
# even if they happened to refer to equal objects.
|
| 392 |
+
# However, it means keys may already have been removed.
|
| 393 |
+
pop = self._pending_removals.pop
|
| 394 |
+
d = self.data
|
| 395 |
+
while True:
|
| 396 |
+
try:
|
| 397 |
+
key = pop()
|
| 398 |
+
except IndexError:
|
| 399 |
+
return
|
| 400 |
+
|
| 401 |
+
try:
|
| 402 |
+
del d[key]
|
| 403 |
+
except KeyError:
|
| 404 |
+
pass
|
| 405 |
+
|
| 406 |
+
def _scrub_removals(self):
|
| 407 |
+
d = self.data
|
| 408 |
+
self._pending_removals = [k for k in self._pending_removals if k in d]
|
| 409 |
+
self._dirty_len = False
|
| 410 |
+
|
| 411 |
+
def __delitem__(self, key):
|
| 412 |
+
self._dirty_len = True
|
| 413 |
+
del self.data[ref(key)]
|
| 414 |
+
|
| 415 |
+
def __getitem__(self, key):
|
| 416 |
+
return self.data[ref(key)]
|
| 417 |
+
|
| 418 |
+
def __len__(self):
|
| 419 |
+
if self._dirty_len and self._pending_removals:
|
| 420 |
+
# self._pending_removals may still contain keys which were
|
| 421 |
+
# explicitly removed, we have to scrub them (see issue #21173).
|
| 422 |
+
self._scrub_removals()
|
| 423 |
+
return len(self.data) - len(self._pending_removals)
|
| 424 |
+
|
| 425 |
+
def __repr__(self):
|
| 426 |
+
return "<%s at %#x>" % (self.__class__.__name__, id(self))
|
| 427 |
+
|
| 428 |
+
def __setitem__(self, key, value):
|
| 429 |
+
self.data[ref(key, self._remove)] = value
|
| 430 |
+
|
| 431 |
+
def copy(self):
|
| 432 |
+
new = WeakKeyDictionary()
|
| 433 |
+
with _IterationGuard(self):
|
| 434 |
+
for key, value in self.data.items():
|
| 435 |
+
o = key()
|
| 436 |
+
if o is not None:
|
| 437 |
+
new[o] = value
|
| 438 |
+
return new
|
| 439 |
+
|
| 440 |
+
__copy__ = copy
|
| 441 |
+
|
| 442 |
+
def __deepcopy__(self, memo):
|
| 443 |
+
from copy import deepcopy
|
| 444 |
+
new = self.__class__()
|
| 445 |
+
with _IterationGuard(self):
|
| 446 |
+
for key, value in self.data.items():
|
| 447 |
+
o = key()
|
| 448 |
+
if o is not None:
|
| 449 |
+
new[o] = deepcopy(value, memo)
|
| 450 |
+
return new
|
| 451 |
+
|
| 452 |
+
def get(self, key, default=None):
|
| 453 |
+
return self.data.get(ref(key),default)
|
| 454 |
+
|
| 455 |
+
def __contains__(self, key):
|
| 456 |
+
try:
|
| 457 |
+
wr = ref(key)
|
| 458 |
+
except TypeError:
|
| 459 |
+
return False
|
| 460 |
+
return wr in self.data
|
| 461 |
+
|
| 462 |
+
def items(self):
|
| 463 |
+
with _IterationGuard(self):
|
| 464 |
+
for wr, value in self.data.items():
|
| 465 |
+
key = wr()
|
| 466 |
+
if key is not None:
|
| 467 |
+
yield key, value
|
| 468 |
+
|
| 469 |
+
def keys(self):
|
| 470 |
+
with _IterationGuard(self):
|
| 471 |
+
for wr in self.data:
|
| 472 |
+
obj = wr()
|
| 473 |
+
if obj is not None:
|
| 474 |
+
yield obj
|
| 475 |
+
|
| 476 |
+
__iter__ = keys
|
| 477 |
+
|
| 478 |
+
def values(self):
|
| 479 |
+
with _IterationGuard(self):
|
| 480 |
+
for wr, value in self.data.items():
|
| 481 |
+
if wr() is not None:
|
| 482 |
+
yield value
|
| 483 |
+
|
| 484 |
+
def keyrefs(self):
|
| 485 |
+
"""Return a list of weak references to the keys.
|
| 486 |
+
|
| 487 |
+
The references are not guaranteed to be 'live' at the time
|
| 488 |
+
they are used, so the result of calling the references needs
|
| 489 |
+
to be checked before being used. This can be used to avoid
|
| 490 |
+
creating references that will cause the garbage collector to
|
| 491 |
+
keep the keys around longer than needed.
|
| 492 |
+
|
| 493 |
+
"""
|
| 494 |
+
return list(self.data)
|
| 495 |
+
|
| 496 |
+
def popitem(self):
|
| 497 |
+
self._dirty_len = True
|
| 498 |
+
while True:
|
| 499 |
+
key, value = self.data.popitem()
|
| 500 |
+
o = key()
|
| 501 |
+
if o is not None:
|
| 502 |
+
return o, value
|
| 503 |
+
|
| 504 |
+
def pop(self, key, *args):
|
| 505 |
+
self._dirty_len = True
|
| 506 |
+
return self.data.pop(ref(key), *args)
|
| 507 |
+
|
| 508 |
+
def setdefault(self, key, default=None):
|
| 509 |
+
return self.data.setdefault(ref(key, self._remove),default)
|
| 510 |
+
|
| 511 |
+
def update(self, dict=None, /, **kwargs):
|
| 512 |
+
d = self.data
|
| 513 |
+
if dict is not None:
|
| 514 |
+
if not hasattr(dict, "items"):
|
| 515 |
+
dict = type({})(dict)
|
| 516 |
+
for key, value in dict.items():
|
| 517 |
+
d[ref(key, self._remove)] = value
|
| 518 |
+
if len(kwargs):
|
| 519 |
+
self.update(kwargs)
|
| 520 |
+
|
| 521 |
+
def __ior__(self, other):
|
| 522 |
+
self.update(other)
|
| 523 |
+
return self
|
| 524 |
+
|
| 525 |
+
def __or__(self, other):
|
| 526 |
+
if isinstance(other, _collections_abc.Mapping):
|
| 527 |
+
c = self.copy()
|
| 528 |
+
c.update(other)
|
| 529 |
+
return c
|
| 530 |
+
return NotImplemented
|
| 531 |
+
|
| 532 |
+
def __ror__(self, other):
|
| 533 |
+
if isinstance(other, _collections_abc.Mapping):
|
| 534 |
+
c = self.__class__()
|
| 535 |
+
c.update(other)
|
| 536 |
+
c.update(self)
|
| 537 |
+
return c
|
| 538 |
+
return NotImplemented
|
| 539 |
+
|
| 540 |
+
|
| 541 |
+
class finalize:
|
| 542 |
+
"""Class for finalization of weakrefable objects
|
| 543 |
+
|
| 544 |
+
finalize(obj, func, *args, **kwargs) returns a callable finalizer
|
| 545 |
+
object which will be called when obj is garbage collected. The
|
| 546 |
+
first time the finalizer is called it evaluates func(*arg, **kwargs)
|
| 547 |
+
and returns the result. After this the finalizer is dead, and
|
| 548 |
+
calling it just returns None.
|
| 549 |
+
|
| 550 |
+
When the program exits any remaining finalizers for which the
|
| 551 |
+
atexit attribute is true will be run in reverse order of creation.
|
| 552 |
+
By default atexit is true.
|
| 553 |
+
"""
|
| 554 |
+
|
| 555 |
+
# Finalizer objects don't have any state of their own. They are
|
| 556 |
+
# just used as keys to lookup _Info objects in the registry. This
|
| 557 |
+
# ensures that they cannot be part of a ref-cycle.
|
| 558 |
+
|
| 559 |
+
__slots__ = ()
|
| 560 |
+
_registry = {}
|
| 561 |
+
_shutdown = False
|
| 562 |
+
_index_iter = itertools.count()
|
| 563 |
+
_dirty = False
|
| 564 |
+
_registered_with_atexit = False
|
| 565 |
+
|
| 566 |
+
class _Info:
|
| 567 |
+
__slots__ = ("weakref", "func", "args", "kwargs", "atexit", "index")
|
| 568 |
+
|
| 569 |
+
def __init__(self, obj, func, /, *args, **kwargs):
|
| 570 |
+
if not self._registered_with_atexit:
|
| 571 |
+
# We may register the exit function more than once because
|
| 572 |
+
# of a thread race, but that is harmless
|
| 573 |
+
import atexit
|
| 574 |
+
atexit.register(self._exitfunc)
|
| 575 |
+
finalize._registered_with_atexit = True
|
| 576 |
+
info = self._Info()
|
| 577 |
+
info.weakref = ref(obj, self)
|
| 578 |
+
info.func = func
|
| 579 |
+
info.args = args
|
| 580 |
+
info.kwargs = kwargs or None
|
| 581 |
+
info.atexit = True
|
| 582 |
+
info.index = next(self._index_iter)
|
| 583 |
+
self._registry[self] = info
|
| 584 |
+
finalize._dirty = True
|
| 585 |
+
|
| 586 |
+
def __call__(self, _=None):
|
| 587 |
+
"""If alive then mark as dead and return func(*args, **kwargs);
|
| 588 |
+
otherwise return None"""
|
| 589 |
+
info = self._registry.pop(self, None)
|
| 590 |
+
if info and not self._shutdown:
|
| 591 |
+
return info.func(*info.args, **(info.kwargs or {}))
|
| 592 |
+
|
| 593 |
+
def detach(self):
|
| 594 |
+
"""If alive then mark as dead and return (obj, func, args, kwargs);
|
| 595 |
+
otherwise return None"""
|
| 596 |
+
info = self._registry.get(self)
|
| 597 |
+
obj = info and info.weakref()
|
| 598 |
+
if obj is not None and self._registry.pop(self, None):
|
| 599 |
+
return (obj, info.func, info.args, info.kwargs or {})
|
| 600 |
+
|
| 601 |
+
def peek(self):
|
| 602 |
+
"""If alive then return (obj, func, args, kwargs);
|
| 603 |
+
otherwise return None"""
|
| 604 |
+
info = self._registry.get(self)
|
| 605 |
+
obj = info and info.weakref()
|
| 606 |
+
if obj is not None:
|
| 607 |
+
return (obj, info.func, info.args, info.kwargs or {})
|
| 608 |
+
|
| 609 |
+
@property
|
| 610 |
+
def alive(self):
|
| 611 |
+
"""Whether finalizer is alive"""
|
| 612 |
+
return self in self._registry
|
| 613 |
+
|
| 614 |
+
@property
|
| 615 |
+
def atexit(self):
|
| 616 |
+
"""Whether finalizer should be called at exit"""
|
| 617 |
+
info = self._registry.get(self)
|
| 618 |
+
return bool(info) and info.atexit
|
| 619 |
+
|
| 620 |
+
@atexit.setter
|
| 621 |
+
def atexit(self, value):
|
| 622 |
+
info = self._registry.get(self)
|
| 623 |
+
if info:
|
| 624 |
+
info.atexit = bool(value)
|
| 625 |
+
|
| 626 |
+
def __repr__(self):
|
| 627 |
+
info = self._registry.get(self)
|
| 628 |
+
obj = info and info.weakref()
|
| 629 |
+
if obj is None:
|
| 630 |
+
return '<%s object at %#x; dead>' % (type(self).__name__, id(self))
|
| 631 |
+
else:
|
| 632 |
+
return '<%s object at %#x; for %r at %#x>' % \
|
| 633 |
+
(type(self).__name__, id(self), type(obj).__name__, id(obj))
|
| 634 |
+
|
| 635 |
+
@classmethod
|
| 636 |
+
def _select_for_exit(cls):
|
| 637 |
+
# Return live finalizers marked for exit, oldest first
|
| 638 |
+
L = [(f,i) for (f,i) in cls._registry.items() if i.atexit]
|
| 639 |
+
L.sort(key=lambda item:item[1].index)
|
| 640 |
+
return [f for (f,i) in L]
|
| 641 |
+
|
| 642 |
+
@classmethod
|
| 643 |
+
def _exitfunc(cls):
|
| 644 |
+
# At shutdown invoke finalizers for which atexit is true.
|
| 645 |
+
# This is called once all other non-daemonic threads have been
|
| 646 |
+
# joined.
|
| 647 |
+
reenable_gc = False
|
| 648 |
+
try:
|
| 649 |
+
if cls._registry:
|
| 650 |
+
import gc
|
| 651 |
+
if gc.isenabled():
|
| 652 |
+
reenable_gc = True
|
| 653 |
+
gc.disable()
|
| 654 |
+
pending = None
|
| 655 |
+
while True:
|
| 656 |
+
if pending is None or finalize._dirty:
|
| 657 |
+
pending = cls._select_for_exit()
|
| 658 |
+
finalize._dirty = False
|
| 659 |
+
if not pending:
|
| 660 |
+
break
|
| 661 |
+
f = pending.pop()
|
| 662 |
+
try:
|
| 663 |
+
# gc is disabled, so (assuming no daemonic
|
| 664 |
+
# threads) the following is the only line in
|
| 665 |
+
# this function which might trigger creation
|
| 666 |
+
# of a new finalizer
|
| 667 |
+
f()
|
| 668 |
+
except Exception:
|
| 669 |
+
sys.excepthook(*sys.exc_info())
|
| 670 |
+
assert f not in cls._registry
|
| 671 |
+
finally:
|
| 672 |
+
# prevent any more finalizers from executing during shutdown
|
| 673 |
+
finalize._shutdown = True
|
| 674 |
+
if reenable_gc:
|
| 675 |
+
gc.enable()
|
lib/sqlite3.44.2/pkgIndex.tcl
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- tcl -*-
|
| 2 |
+
# Tcl package index file, version 1.1
|
| 3 |
+
#
|
| 4 |
+
# Note sqlite*3* init specifically
|
| 5 |
+
#
|
| 6 |
+
if {[package vsatisfies [package provide Tcl] 9.0-]} {
|
| 7 |
+
package ifneeded sqlite3 3.44.2 \
|
| 8 |
+
[list load [file join $dir libtcl9sqlite3.44.2.so] Sqlite3]
|
| 9 |
+
} else {
|
| 10 |
+
package ifneeded sqlite3 3.44.2 \
|
| 11 |
+
[list load [file join $dir libsqlite3.44.2.so] Sqlite3]
|
| 12 |
+
}
|
lib/tcl8.6/auto.tcl
ADDED
|
@@ -0,0 +1,648 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# auto.tcl --
|
| 2 |
+
#
|
| 3 |
+
# utility procs formerly in init.tcl dealing with auto execution of commands
|
| 4 |
+
# and can be auto loaded themselves.
|
| 5 |
+
#
|
| 6 |
+
# Copyright (c) 1991-1993 The Regents of the University of California.
|
| 7 |
+
# Copyright (c) 1994-1998 Sun Microsystems, Inc.
|
| 8 |
+
#
|
| 9 |
+
# See the file "license.terms" for information on usage and redistribution of
|
| 10 |
+
# this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
| 11 |
+
#
|
| 12 |
+
|
| 13 |
+
# auto_reset --
|
| 14 |
+
#
|
| 15 |
+
# Destroy all cached information for auto-loading and auto-execution, so that
|
| 16 |
+
# the information gets recomputed the next time it's needed. Also delete any
|
| 17 |
+
# commands that are listed in the auto-load index.
|
| 18 |
+
#
|
| 19 |
+
# Arguments:
|
| 20 |
+
# None.
|
| 21 |
+
|
| 22 |
+
proc auto_reset {} {
|
| 23 |
+
global auto_execs auto_index auto_path
|
| 24 |
+
if {[array exists auto_index]} {
|
| 25 |
+
foreach cmdName [array names auto_index] {
|
| 26 |
+
set fqcn [namespace which $cmdName]
|
| 27 |
+
if {$fqcn eq ""} {
|
| 28 |
+
continue
|
| 29 |
+
}
|
| 30 |
+
rename $fqcn {}
|
| 31 |
+
}
|
| 32 |
+
}
|
| 33 |
+
unset -nocomplain auto_execs auto_index ::tcl::auto_oldpath
|
| 34 |
+
if {[catch {llength $auto_path}]} {
|
| 35 |
+
set auto_path [list [info library]]
|
| 36 |
+
} elseif {[info library] ni $auto_path} {
|
| 37 |
+
lappend auto_path [info library]
|
| 38 |
+
}
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
# tcl_findLibrary --
|
| 42 |
+
#
|
| 43 |
+
# This is a utility for extensions that searches for a library directory
|
| 44 |
+
# using a canonical searching algorithm. A side effect is to source the
|
| 45 |
+
# initialization script and set a global library variable.
|
| 46 |
+
#
|
| 47 |
+
# Arguments:
|
| 48 |
+
# basename Prefix of the directory name, (e.g., "tk")
|
| 49 |
+
# version Version number of the package, (e.g., "8.0")
|
| 50 |
+
# patch Patchlevel of the package, (e.g., "8.0.3")
|
| 51 |
+
# initScript Initialization script to source (e.g., tk.tcl)
|
| 52 |
+
# enVarName environment variable to honor (e.g., TK_LIBRARY)
|
| 53 |
+
# varName Global variable to set when done (e.g., tk_library)
|
| 54 |
+
|
| 55 |
+
proc tcl_findLibrary {basename version patch initScript enVarName varName} {
|
| 56 |
+
upvar #0 $varName the_library
|
| 57 |
+
global auto_path env tcl_platform
|
| 58 |
+
|
| 59 |
+
set dirs {}
|
| 60 |
+
set errors {}
|
| 61 |
+
|
| 62 |
+
# The C application may have hardwired a path, which we honor
|
| 63 |
+
|
| 64 |
+
if {[info exists the_library] && $the_library ne ""} {
|
| 65 |
+
lappend dirs $the_library
|
| 66 |
+
} else {
|
| 67 |
+
# Do the canonical search
|
| 68 |
+
|
| 69 |
+
# 1. From an environment variable, if it exists. Placing this first
|
| 70 |
+
# gives the end-user ultimate control to work-around any bugs, or
|
| 71 |
+
# to customize.
|
| 72 |
+
|
| 73 |
+
if {[info exists env($enVarName)]} {
|
| 74 |
+
lappend dirs $env($enVarName)
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
# 2. In the package script directory registered within the
|
| 78 |
+
# configuration of the package itself.
|
| 79 |
+
|
| 80 |
+
catch {
|
| 81 |
+
lappend dirs [::${basename}::pkgconfig get scriptdir,runtime]
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
# 3. Relative to auto_path directories. This checks relative to the
|
| 85 |
+
# Tcl library as well as allowing loading of libraries added to the
|
| 86 |
+
# auto_path that is not relative to the core library or binary paths.
|
| 87 |
+
foreach d $auto_path {
|
| 88 |
+
lappend dirs [file join $d $basename$version]
|
| 89 |
+
if {$tcl_platform(platform) eq "unix"
|
| 90 |
+
&& $tcl_platform(os) eq "Darwin"} {
|
| 91 |
+
# 4. On MacOSX, check the Resources/Scripts subdir too
|
| 92 |
+
lappend dirs [file join $d $basename$version Resources Scripts]
|
| 93 |
+
}
|
| 94 |
+
}
|
| 95 |
+
|
| 96 |
+
# 3. Various locations relative to the executable
|
| 97 |
+
# ../lib/foo1.0 (From bin directory in install hierarchy)
|
| 98 |
+
# ../../lib/foo1.0 (From bin/arch directory in install hierarchy)
|
| 99 |
+
# ../library (From unix directory in build hierarchy)
|
| 100 |
+
#
|
| 101 |
+
# Remaining locations are out of date (when relevant, they ought to be
|
| 102 |
+
# covered by the $::auto_path seach above) and disabled.
|
| 103 |
+
#
|
| 104 |
+
# ../../library (From unix/arch directory in build hierarchy)
|
| 105 |
+
# ../../foo1.0.1/library
|
| 106 |
+
# (From unix directory in parallel build hierarchy)
|
| 107 |
+
# ../../../foo1.0.1/library
|
| 108 |
+
# (From unix/arch directory in parallel build hierarchy)
|
| 109 |
+
|
| 110 |
+
set parentDir [file dirname [file dirname [info nameofexecutable]]]
|
| 111 |
+
set grandParentDir [file dirname $parentDir]
|
| 112 |
+
lappend dirs [file join $parentDir lib $basename$version]
|
| 113 |
+
lappend dirs [file join $grandParentDir lib $basename$version]
|
| 114 |
+
lappend dirs [file join $parentDir library]
|
| 115 |
+
if {0} {
|
| 116 |
+
lappend dirs [file join $grandParentDir library]
|
| 117 |
+
lappend dirs [file join $grandParentDir $basename$patch library]
|
| 118 |
+
lappend dirs [file join [file dirname $grandParentDir] \
|
| 119 |
+
$basename$patch library]
|
| 120 |
+
}
|
| 121 |
+
}
|
| 122 |
+
# make $dirs unique, preserving order
|
| 123 |
+
array set seen {}
|
| 124 |
+
foreach i $dirs {
|
| 125 |
+
# Make sure $i is unique under normalization. Avoid repeated [source].
|
| 126 |
+
if {[interp issafe]} {
|
| 127 |
+
# Safe interps have no [file normalize].
|
| 128 |
+
set norm $i
|
| 129 |
+
} else {
|
| 130 |
+
set norm [file normalize $i]
|
| 131 |
+
}
|
| 132 |
+
if {[info exists seen($norm)]} {
|
| 133 |
+
continue
|
| 134 |
+
}
|
| 135 |
+
set seen($norm) {}
|
| 136 |
+
|
| 137 |
+
set the_library $i
|
| 138 |
+
set file [file join $i $initScript]
|
| 139 |
+
|
| 140 |
+
# source everything when in a safe interpreter because we have a
|
| 141 |
+
# source command, but no file exists command
|
| 142 |
+
|
| 143 |
+
if {[interp issafe] || [file exists $file]} {
|
| 144 |
+
if {![catch {uplevel #0 [list source $file]} msg opts]} {
|
| 145 |
+
return
|
| 146 |
+
}
|
| 147 |
+
append errors "$file: $msg\n"
|
| 148 |
+
append errors [dict get $opts -errorinfo]\n
|
| 149 |
+
}
|
| 150 |
+
}
|
| 151 |
+
unset -nocomplain the_library
|
| 152 |
+
set msg "Can't find a usable $initScript in the following directories: \n"
|
| 153 |
+
append msg " $dirs\n\n"
|
| 154 |
+
append msg "$errors\n\n"
|
| 155 |
+
append msg "This probably means that $basename wasn't installed properly.\n"
|
| 156 |
+
error $msg
|
| 157 |
+
}
|
| 158 |
+
|
| 159 |
+
|
| 160 |
+
# ----------------------------------------------------------------------
|
| 161 |
+
# auto_mkindex
|
| 162 |
+
# ----------------------------------------------------------------------
|
| 163 |
+
# The following procedures are used to generate the tclIndex file from Tcl
|
| 164 |
+
# source files. They use a special safe interpreter to parse Tcl source
|
| 165 |
+
# files, writing out index entries as "proc" commands are encountered. This
|
| 166 |
+
# implementation won't work in a safe interpreter, since a safe interpreter
|
| 167 |
+
# can't create the special parser and mess with its commands.
|
| 168 |
+
|
| 169 |
+
if {[interp issafe]} {
|
| 170 |
+
return ;# Stop sourcing the file here
|
| 171 |
+
}
|
| 172 |
+
|
| 173 |
+
# auto_mkindex --
|
| 174 |
+
# Regenerate a tclIndex file from Tcl source files. Takes as argument the
|
| 175 |
+
# name of the directory in which the tclIndex file is to be placed, followed
|
| 176 |
+
# by any number of glob patterns to use in that directory to locate all of the
|
| 177 |
+
# relevant files.
|
| 178 |
+
#
|
| 179 |
+
# Arguments:
|
| 180 |
+
# dir - Name of the directory in which to create an index.
|
| 181 |
+
|
| 182 |
+
# args - Any number of additional arguments giving the names of files
|
| 183 |
+
# within dir. If no additional are given auto_mkindex will look
|
| 184 |
+
# for *.tcl.
|
| 185 |
+
|
| 186 |
+
proc auto_mkindex {dir args} {
|
| 187 |
+
if {[interp issafe]} {
|
| 188 |
+
error "can't generate index within safe interpreter"
|
| 189 |
+
}
|
| 190 |
+
|
| 191 |
+
set oldDir [pwd]
|
| 192 |
+
cd $dir
|
| 193 |
+
|
| 194 |
+
append index "# Tcl autoload index file, version 2.0\n"
|
| 195 |
+
append index "# This file is generated by the \"auto_mkindex\" command\n"
|
| 196 |
+
append index "# and sourced to set up indexing information for one or\n"
|
| 197 |
+
append index "# more commands. Typically each line is a command that\n"
|
| 198 |
+
append index "# sets an element in the auto_index array, where the\n"
|
| 199 |
+
append index "# element name is the name of a command and the value is\n"
|
| 200 |
+
append index "# a script that loads the command.\n\n"
|
| 201 |
+
if {![llength $args]} {
|
| 202 |
+
set args *.tcl
|
| 203 |
+
}
|
| 204 |
+
|
| 205 |
+
auto_mkindex_parser::init
|
| 206 |
+
foreach file [lsort [glob -- {*}$args]] {
|
| 207 |
+
try {
|
| 208 |
+
append index [auto_mkindex_parser::mkindex $file]
|
| 209 |
+
} on error {msg opts} {
|
| 210 |
+
cd $oldDir
|
| 211 |
+
return -options $opts $msg
|
| 212 |
+
}
|
| 213 |
+
}
|
| 214 |
+
auto_mkindex_parser::cleanup
|
| 215 |
+
|
| 216 |
+
set fid [open "tclIndex" w]
|
| 217 |
+
puts -nonewline $fid $index
|
| 218 |
+
close $fid
|
| 219 |
+
cd $oldDir
|
| 220 |
+
}
|
| 221 |
+
|
| 222 |
+
# Original version of auto_mkindex that just searches the source code for
|
| 223 |
+
# "proc" at the beginning of the line.
|
| 224 |
+
|
| 225 |
+
proc auto_mkindex_old {dir args} {
|
| 226 |
+
set oldDir [pwd]
|
| 227 |
+
cd $dir
|
| 228 |
+
set dir [pwd]
|
| 229 |
+
append index "# Tcl autoload index file, version 2.0\n"
|
| 230 |
+
append index "# This file is generated by the \"auto_mkindex\" command\n"
|
| 231 |
+
append index "# and sourced to set up indexing information for one or\n"
|
| 232 |
+
append index "# more commands. Typically each line is a command that\n"
|
| 233 |
+
append index "# sets an element in the auto_index array, where the\n"
|
| 234 |
+
append index "# element name is the name of a command and the value is\n"
|
| 235 |
+
append index "# a script that loads the command.\n\n"
|
| 236 |
+
if {![llength $args]} {
|
| 237 |
+
set args *.tcl
|
| 238 |
+
}
|
| 239 |
+
foreach file [lsort [glob -- {*}$args]] {
|
| 240 |
+
set f ""
|
| 241 |
+
set error [catch {
|
| 242 |
+
set f [open $file]
|
| 243 |
+
fconfigure $f -eofchar "\x1A {}"
|
| 244 |
+
while {[gets $f line] >= 0} {
|
| 245 |
+
if {[regexp {^proc[ ]+([^ ]*)} $line match procName]} {
|
| 246 |
+
set procName [lindex [auto_qualify $procName "::"] 0]
|
| 247 |
+
append index "set [list auto_index($procName)]"
|
| 248 |
+
append index " \[list source \[file join \$dir [list $file]\]\]\n"
|
| 249 |
+
}
|
| 250 |
+
}
|
| 251 |
+
close $f
|
| 252 |
+
} msg opts]
|
| 253 |
+
if {$error} {
|
| 254 |
+
catch {close $f}
|
| 255 |
+
cd $oldDir
|
| 256 |
+
return -options $opts $msg
|
| 257 |
+
}
|
| 258 |
+
}
|
| 259 |
+
set f ""
|
| 260 |
+
set error [catch {
|
| 261 |
+
set f [open tclIndex w]
|
| 262 |
+
puts -nonewline $f $index
|
| 263 |
+
close $f
|
| 264 |
+
cd $oldDir
|
| 265 |
+
} msg opts]
|
| 266 |
+
if {$error} {
|
| 267 |
+
catch {close $f}
|
| 268 |
+
cd $oldDir
|
| 269 |
+
error $msg $info $code
|
| 270 |
+
return -options $opts $msg
|
| 271 |
+
}
|
| 272 |
+
}
|
| 273 |
+
|
| 274 |
+
# Create a safe interpreter that can be used to parse Tcl source files
|
| 275 |
+
# generate a tclIndex file for autoloading. This interp contains commands for
|
| 276 |
+
# things that need index entries. Each time a command is executed, it writes
|
| 277 |
+
# an entry out to the index file.
|
| 278 |
+
|
| 279 |
+
namespace eval auto_mkindex_parser {
|
| 280 |
+
variable parser "" ;# parser used to build index
|
| 281 |
+
variable index "" ;# maintains index as it is built
|
| 282 |
+
variable scriptFile "" ;# name of file being processed
|
| 283 |
+
variable contextStack "" ;# stack of namespace scopes
|
| 284 |
+
variable imports "" ;# keeps track of all imported cmds
|
| 285 |
+
variable initCommands ;# list of commands that create aliases
|
| 286 |
+
if {![info exists initCommands]} {
|
| 287 |
+
set initCommands [list]
|
| 288 |
+
}
|
| 289 |
+
|
| 290 |
+
proc init {} {
|
| 291 |
+
variable parser
|
| 292 |
+
variable initCommands
|
| 293 |
+
|
| 294 |
+
if {![interp issafe]} {
|
| 295 |
+
set parser [interp create -safe]
|
| 296 |
+
$parser hide info
|
| 297 |
+
$parser hide rename
|
| 298 |
+
$parser hide proc
|
| 299 |
+
$parser hide namespace
|
| 300 |
+
$parser hide eval
|
| 301 |
+
$parser hide puts
|
| 302 |
+
foreach ns [$parser invokehidden namespace children ::] {
|
| 303 |
+
# MUST NOT DELETE "::tcl" OR BAD THINGS HAPPEN!
|
| 304 |
+
if {$ns eq "::tcl"} continue
|
| 305 |
+
$parser invokehidden namespace delete $ns
|
| 306 |
+
}
|
| 307 |
+
foreach cmd [$parser invokehidden info commands ::*] {
|
| 308 |
+
$parser invokehidden rename $cmd {}
|
| 309 |
+
}
|
| 310 |
+
$parser invokehidden proc unknown {args} {}
|
| 311 |
+
|
| 312 |
+
# We'll need access to the "namespace" command within the
|
| 313 |
+
# interp. Put it back, but move it out of the way.
|
| 314 |
+
|
| 315 |
+
$parser expose namespace
|
| 316 |
+
$parser invokehidden rename namespace _%@namespace
|
| 317 |
+
$parser expose eval
|
| 318 |
+
$parser invokehidden rename eval _%@eval
|
| 319 |
+
|
| 320 |
+
# Install all the registered pseudo-command implementations
|
| 321 |
+
|
| 322 |
+
foreach cmd $initCommands {
|
| 323 |
+
eval $cmd
|
| 324 |
+
}
|
| 325 |
+
}
|
| 326 |
+
}
|
| 327 |
+
proc cleanup {} {
|
| 328 |
+
variable parser
|
| 329 |
+
interp delete $parser
|
| 330 |
+
unset parser
|
| 331 |
+
}
|
| 332 |
+
}
|
| 333 |
+
|
| 334 |
+
# auto_mkindex_parser::mkindex --
|
| 335 |
+
#
|
| 336 |
+
# Used by the "auto_mkindex" command to create a "tclIndex" file for the given
|
| 337 |
+
# Tcl source file. Executes the commands in the file, and handles things like
|
| 338 |
+
# the "proc" command by adding an entry for the index file. Returns a string
|
| 339 |
+
# that represents the index file.
|
| 340 |
+
#
|
| 341 |
+
# Arguments:
|
| 342 |
+
# file Name of Tcl source file to be indexed.
|
| 343 |
+
|
| 344 |
+
proc auto_mkindex_parser::mkindex {file} {
|
| 345 |
+
variable parser
|
| 346 |
+
variable index
|
| 347 |
+
variable scriptFile
|
| 348 |
+
variable contextStack
|
| 349 |
+
variable imports
|
| 350 |
+
|
| 351 |
+
set scriptFile $file
|
| 352 |
+
|
| 353 |
+
set fid [open $file]
|
| 354 |
+
fconfigure $fid -eofchar "\x1A {}"
|
| 355 |
+
set contents [read $fid]
|
| 356 |
+
close $fid
|
| 357 |
+
|
| 358 |
+
# There is one problem with sourcing files into the safe interpreter:
|
| 359 |
+
# references like "$x" will fail since code is not really being executed
|
| 360 |
+
# and variables do not really exist. To avoid this, we replace all $ with
|
| 361 |
+
# \0 (literally, the null char) later, when getting proc names we will
|
| 362 |
+
# have to reverse this replacement, in case there were any $ in the proc
|
| 363 |
+
# name. This will cause a problem if somebody actually tries to have a \0
|
| 364 |
+
# in their proc name. Too bad for them.
|
| 365 |
+
set contents [string map [list \$ \0] $contents]
|
| 366 |
+
|
| 367 |
+
set index ""
|
| 368 |
+
set contextStack ""
|
| 369 |
+
set imports ""
|
| 370 |
+
|
| 371 |
+
$parser eval $contents
|
| 372 |
+
|
| 373 |
+
foreach name $imports {
|
| 374 |
+
catch {$parser eval [list _%@namespace forget $name]}
|
| 375 |
+
}
|
| 376 |
+
return $index
|
| 377 |
+
}
|
| 378 |
+
|
| 379 |
+
# auto_mkindex_parser::hook command
|
| 380 |
+
#
|
| 381 |
+
# Registers a Tcl command to evaluate when initializing the child interpreter
|
| 382 |
+
# used by the mkindex parser. The command is evaluated in the parent
|
| 383 |
+
# interpreter, and can use the variable auto_mkindex_parser::parser to get to
|
| 384 |
+
# the child
|
| 385 |
+
|
| 386 |
+
proc auto_mkindex_parser::hook {cmd} {
|
| 387 |
+
variable initCommands
|
| 388 |
+
|
| 389 |
+
lappend initCommands $cmd
|
| 390 |
+
}
|
| 391 |
+
|
| 392 |
+
# auto_mkindex_parser::slavehook command
|
| 393 |
+
#
|
| 394 |
+
# Registers a Tcl command to evaluate when initializing the child interpreter
|
| 395 |
+
# used by the mkindex parser. The command is evaluated in the child
|
| 396 |
+
# interpreter.
|
| 397 |
+
|
| 398 |
+
proc auto_mkindex_parser::slavehook {cmd} {
|
| 399 |
+
variable initCommands
|
| 400 |
+
|
| 401 |
+
# The $parser variable is defined to be the name of the child interpreter
|
| 402 |
+
# when this command is used later.
|
| 403 |
+
|
| 404 |
+
lappend initCommands "\$parser eval [list $cmd]"
|
| 405 |
+
}
|
| 406 |
+
|
| 407 |
+
# auto_mkindex_parser::command --
|
| 408 |
+
#
|
| 409 |
+
# Registers a new command with the "auto_mkindex_parser" interpreter that
|
| 410 |
+
# parses Tcl files. These commands are fake versions of things like the
|
| 411 |
+
# "proc" command. When you execute them, they simply write out an entry to a
|
| 412 |
+
# "tclIndex" file for auto-loading.
|
| 413 |
+
#
|
| 414 |
+
# This procedure allows extensions to register their own commands with the
|
| 415 |
+
# auto_mkindex facility. For example, a package like [incr Tcl] might
|
| 416 |
+
# register a "class" command so that class definitions could be added to a
|
| 417 |
+
# "tclIndex" file for auto-loading.
|
| 418 |
+
#
|
| 419 |
+
# Arguments:
|
| 420 |
+
# name Name of command recognized in Tcl files.
|
| 421 |
+
# arglist Argument list for command.
|
| 422 |
+
# body Implementation of command to handle indexing.
|
| 423 |
+
|
| 424 |
+
proc auto_mkindex_parser::command {name arglist body} {
|
| 425 |
+
hook [list auto_mkindex_parser::commandInit $name $arglist $body]
|
| 426 |
+
}
|
| 427 |
+
|
| 428 |
+
# auto_mkindex_parser::commandInit --
|
| 429 |
+
#
|
| 430 |
+
# This does the actual work set up by auto_mkindex_parser::command. This is
|
| 431 |
+
# called when the interpreter used by the parser is created.
|
| 432 |
+
#
|
| 433 |
+
# Arguments:
|
| 434 |
+
# name Name of command recognized in Tcl files.
|
| 435 |
+
# arglist Argument list for command.
|
| 436 |
+
# body Implementation of command to handle indexing.
|
| 437 |
+
|
| 438 |
+
proc auto_mkindex_parser::commandInit {name arglist body} {
|
| 439 |
+
variable parser
|
| 440 |
+
|
| 441 |
+
set ns [namespace qualifiers $name]
|
| 442 |
+
set tail [namespace tail $name]
|
| 443 |
+
if {$ns eq ""} {
|
| 444 |
+
set fakeName [namespace current]::_%@fake_$tail
|
| 445 |
+
} else {
|
| 446 |
+
set fakeName [namespace current]::[string map {:: _} _%@fake_$name]
|
| 447 |
+
}
|
| 448 |
+
proc $fakeName $arglist $body
|
| 449 |
+
|
| 450 |
+
# YUK! Tcl won't let us alias fully qualified command names, so we can't
|
| 451 |
+
# handle names like "::itcl::class". Instead, we have to build procs with
|
| 452 |
+
# the fully qualified names, and have the procs point to the aliases.
|
| 453 |
+
|
| 454 |
+
if {[string match *::* $name]} {
|
| 455 |
+
set exportCmd [list _%@namespace export [namespace tail $name]]
|
| 456 |
+
$parser eval [list _%@namespace eval $ns $exportCmd]
|
| 457 |
+
|
| 458 |
+
# The following proc definition does not work if you want to tolerate
|
| 459 |
+
# space or something else diabolical in the procedure name, (i.e.,
|
| 460 |
+
# space in $alias). The following does not work:
|
| 461 |
+
# "_%@eval {$alias} \$args"
|
| 462 |
+
# because $alias gets concat'ed to $args. The following does not work
|
| 463 |
+
# because $cmd is somehow undefined
|
| 464 |
+
# "set cmd {$alias} \; _%@eval {\$cmd} \$args"
|
| 465 |
+
# A gold star to someone that can make test autoMkindex-3.3 work
|
| 466 |
+
# properly
|
| 467 |
+
|
| 468 |
+
set alias [namespace tail $fakeName]
|
| 469 |
+
$parser invokehidden proc $name {args} "_%@eval {$alias} \$args"
|
| 470 |
+
$parser alias $alias $fakeName
|
| 471 |
+
} else {
|
| 472 |
+
$parser alias $name $fakeName
|
| 473 |
+
}
|
| 474 |
+
return
|
| 475 |
+
}
|
| 476 |
+
|
| 477 |
+
# auto_mkindex_parser::fullname --
|
| 478 |
+
#
|
| 479 |
+
# Used by commands like "proc" within the auto_mkindex parser. Returns the
|
| 480 |
+
# qualified namespace name for the "name" argument. If the "name" does not
|
| 481 |
+
# start with "::", elements are added from the current namespace stack to
|
| 482 |
+
# produce a qualified name. Then, the name is examined to see whether or not
|
| 483 |
+
# it should really be qualified. If the name has more than the leading "::",
|
| 484 |
+
# it is returned as a fully qualified name. Otherwise, it is returned as a
|
| 485 |
+
# simple name. That way, the Tcl autoloader will recognize it properly.
|
| 486 |
+
#
|
| 487 |
+
# Arguments:
|
| 488 |
+
# name - Name that is being added to index.
|
| 489 |
+
|
| 490 |
+
proc auto_mkindex_parser::fullname {name} {
|
| 491 |
+
variable contextStack
|
| 492 |
+
|
| 493 |
+
if {![string match ::* $name]} {
|
| 494 |
+
foreach ns $contextStack {
|
| 495 |
+
set name "${ns}::$name"
|
| 496 |
+
if {[string match ::* $name]} {
|
| 497 |
+
break
|
| 498 |
+
}
|
| 499 |
+
}
|
| 500 |
+
}
|
| 501 |
+
|
| 502 |
+
if {[namespace qualifiers $name] eq ""} {
|
| 503 |
+
set name [namespace tail $name]
|
| 504 |
+
} elseif {![string match ::* $name]} {
|
| 505 |
+
set name "::$name"
|
| 506 |
+
}
|
| 507 |
+
|
| 508 |
+
# Earlier, mkindex replaced all $'s with \0. Now, we have to reverse that
|
| 509 |
+
# replacement.
|
| 510 |
+
return [string map [list \0 \$] $name]
|
| 511 |
+
}
|
| 512 |
+
|
| 513 |
+
# auto_mkindex_parser::indexEntry --
|
| 514 |
+
#
|
| 515 |
+
# Used by commands like "proc" within the auto_mkindex parser to add a
|
| 516 |
+
# correctly-quoted entry to the index. This is shared code so it is done
|
| 517 |
+
# *right*, in one place.
|
| 518 |
+
#
|
| 519 |
+
# Arguments:
|
| 520 |
+
# name - Name that is being added to index.
|
| 521 |
+
|
| 522 |
+
proc auto_mkindex_parser::indexEntry {name} {
|
| 523 |
+
variable index
|
| 524 |
+
variable scriptFile
|
| 525 |
+
|
| 526 |
+
# We convert all metacharacters to their backslashed form, and pre-split
|
| 527 |
+
# the file name that we know about (which will be a proper list, and so
|
| 528 |
+
# correctly quoted).
|
| 529 |
+
|
| 530 |
+
set name [string range [list \}[fullname $name]] 2 end]
|
| 531 |
+
set filenameParts [file split $scriptFile]
|
| 532 |
+
|
| 533 |
+
append index [format \
|
| 534 |
+
{set auto_index(%s) [list source [file join $dir %s]]%s} \
|
| 535 |
+
$name $filenameParts \n]
|
| 536 |
+
return
|
| 537 |
+
}
|
| 538 |
+
|
| 539 |
+
if {[llength $::auto_mkindex_parser::initCommands]} {
|
| 540 |
+
return
|
| 541 |
+
}
|
| 542 |
+
|
| 543 |
+
# Register all of the procedures for the auto_mkindex parser that will build
|
| 544 |
+
# the "tclIndex" file.
|
| 545 |
+
|
| 546 |
+
# AUTO MKINDEX: proc name arglist body
|
| 547 |
+
# Adds an entry to the auto index list for the given procedure name.
|
| 548 |
+
|
| 549 |
+
auto_mkindex_parser::command proc {name args} {
|
| 550 |
+
indexEntry $name
|
| 551 |
+
}
|
| 552 |
+
|
| 553 |
+
# Conditionally add support for Tcl byte code files. There are some tricky
|
| 554 |
+
# details here. First, we need to get the tbcload library initialized in the
|
| 555 |
+
# current interpreter. We cannot load tbcload into the child until we have
|
| 556 |
+
# done so because it needs access to the tcl_patchLevel variable. Second,
|
| 557 |
+
# because the package index file may defer loading the library until we invoke
|
| 558 |
+
# a command, we need to explicitly invoke auto_load to force it to be loaded.
|
| 559 |
+
# This should be a noop if the package has already been loaded
|
| 560 |
+
|
| 561 |
+
auto_mkindex_parser::hook {
|
| 562 |
+
try {
|
| 563 |
+
package require tbcload
|
| 564 |
+
} on error {} {
|
| 565 |
+
# OK, don't have it so do nothing
|
| 566 |
+
} on ok {} {
|
| 567 |
+
if {[namespace which -command tbcload::bcproc] eq ""} {
|
| 568 |
+
auto_load tbcload::bcproc
|
| 569 |
+
}
|
| 570 |
+
load {} tbcload $auto_mkindex_parser::parser
|
| 571 |
+
|
| 572 |
+
# AUTO MKINDEX: tbcload::bcproc name arglist body
|
| 573 |
+
# Adds an entry to the auto index list for the given precompiled
|
| 574 |
+
# procedure name.
|
| 575 |
+
|
| 576 |
+
auto_mkindex_parser::commandInit tbcload::bcproc {name args} {
|
| 577 |
+
indexEntry $name
|
| 578 |
+
}
|
| 579 |
+
}
|
| 580 |
+
}
|
| 581 |
+
|
| 582 |
+
# AUTO MKINDEX: namespace eval name command ?arg arg...?
|
| 583 |
+
# Adds the namespace name onto the context stack and evaluates the associated
|
| 584 |
+
# body of commands.
|
| 585 |
+
#
|
| 586 |
+
# AUTO MKINDEX: namespace import ?-force? pattern ?pattern...?
|
| 587 |
+
# Performs the "import" action in the parser interpreter. This is important
|
| 588 |
+
# for any commands contained in a namespace that affect the index. For
|
| 589 |
+
# example, a script may say "itcl::class ...", or it may import "itcl::*" and
|
| 590 |
+
# then say "class ...". This procedure does the import operation, but keeps
|
| 591 |
+
# track of imported patterns so we can remove the imports later.
|
| 592 |
+
|
| 593 |
+
auto_mkindex_parser::command namespace {op args} {
|
| 594 |
+
switch -- $op {
|
| 595 |
+
eval {
|
| 596 |
+
variable parser
|
| 597 |
+
variable contextStack
|
| 598 |
+
|
| 599 |
+
set name [lindex $args 0]
|
| 600 |
+
set args [lrange $args 1 end]
|
| 601 |
+
|
| 602 |
+
set contextStack [linsert $contextStack 0 $name]
|
| 603 |
+
$parser eval [list _%@namespace eval $name] $args
|
| 604 |
+
set contextStack [lrange $contextStack 1 end]
|
| 605 |
+
}
|
| 606 |
+
import {
|
| 607 |
+
variable parser
|
| 608 |
+
variable imports
|
| 609 |
+
foreach pattern $args {
|
| 610 |
+
if {$pattern ne "-force"} {
|
| 611 |
+
lappend imports $pattern
|
| 612 |
+
}
|
| 613 |
+
}
|
| 614 |
+
catch {$parser eval "_%@namespace import $args"}
|
| 615 |
+
}
|
| 616 |
+
ensemble {
|
| 617 |
+
variable parser
|
| 618 |
+
variable contextStack
|
| 619 |
+
if {[lindex $args 0] eq "create"} {
|
| 620 |
+
set name ::[join [lreverse $contextStack] ::]
|
| 621 |
+
catch {
|
| 622 |
+
set name [dict get [lrange $args 1 end] -command]
|
| 623 |
+
if {![string match ::* $name]} {
|
| 624 |
+
set name ::[join [lreverse $contextStack] ::]$name
|
| 625 |
+
}
|
| 626 |
+
regsub -all ::+ $name :: name
|
| 627 |
+
}
|
| 628 |
+
# create artificial proc to force an entry in the tclIndex
|
| 629 |
+
$parser eval [list ::proc $name {} {}]
|
| 630 |
+
}
|
| 631 |
+
}
|
| 632 |
+
}
|
| 633 |
+
}
|
| 634 |
+
|
| 635 |
+
# AUTO MKINDEX: oo::class create name ?definition?
|
| 636 |
+
# Adds an entry to the auto index list for the given class name.
|
| 637 |
+
auto_mkindex_parser::command oo::class {op name {body ""}} {
|
| 638 |
+
if {$op eq "create"} {
|
| 639 |
+
indexEntry $name
|
| 640 |
+
}
|
| 641 |
+
}
|
| 642 |
+
auto_mkindex_parser::command class {op name {body ""}} {
|
| 643 |
+
if {$op eq "create"} {
|
| 644 |
+
indexEntry $name
|
| 645 |
+
}
|
| 646 |
+
}
|
| 647 |
+
|
| 648 |
+
return
|
lib/tcl8.6/clock.tcl
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
lib/tcl8.6/history.tcl
ADDED
|
@@ -0,0 +1,335 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# history.tcl --
|
| 2 |
+
#
|
| 3 |
+
# Implementation of the history command.
|
| 4 |
+
#
|
| 5 |
+
# Copyright (c) 1997 Sun Microsystems, Inc.
|
| 6 |
+
#
|
| 7 |
+
# See the file "license.terms" for information on usage and redistribution of
|
| 8 |
+
# this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
| 9 |
+
#
|
| 10 |
+
|
| 11 |
+
# The tcl::history array holds the history list and some additional
|
| 12 |
+
# bookkeeping variables.
|
| 13 |
+
#
|
| 14 |
+
# nextid the index used for the next history list item.
|
| 15 |
+
# keep the max size of the history list
|
| 16 |
+
# oldest the index of the oldest item in the history.
|
| 17 |
+
|
| 18 |
+
namespace eval ::tcl {
|
| 19 |
+
variable history
|
| 20 |
+
if {![info exists history]} {
|
| 21 |
+
array set history {
|
| 22 |
+
nextid 0
|
| 23 |
+
keep 20
|
| 24 |
+
oldest -20
|
| 25 |
+
}
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
namespace ensemble create -command ::tcl::history -map {
|
| 29 |
+
add ::tcl::HistAdd
|
| 30 |
+
change ::tcl::HistChange
|
| 31 |
+
clear ::tcl::HistClear
|
| 32 |
+
event ::tcl::HistEvent
|
| 33 |
+
info ::tcl::HistInfo
|
| 34 |
+
keep ::tcl::HistKeep
|
| 35 |
+
nextid ::tcl::HistNextID
|
| 36 |
+
redo ::tcl::HistRedo
|
| 37 |
+
}
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
# history --
|
| 41 |
+
#
|
| 42 |
+
# This is the main history command. See the man page for its interface.
|
| 43 |
+
# This does some argument checking and calls the helper ensemble in the
|
| 44 |
+
# tcl namespace.
|
| 45 |
+
|
| 46 |
+
proc ::history {args} {
|
| 47 |
+
# If no command given, we're doing 'history info'. Can't be done with an
|
| 48 |
+
# ensemble unknown handler, as those don't fire when no subcommand is
|
| 49 |
+
# given at all.
|
| 50 |
+
|
| 51 |
+
if {![llength $args]} {
|
| 52 |
+
set args info
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
# Tricky stuff needed to make stack and errors come out right!
|
| 56 |
+
tailcall apply {arglist {tailcall ::tcl::history {*}$arglist} ::tcl} $args
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
# (unnamed) --
|
| 60 |
+
#
|
| 61 |
+
# Callback when [::history] is destroyed. Destroys the implementation.
|
| 62 |
+
#
|
| 63 |
+
# Parameters:
|
| 64 |
+
# oldName what the command was called.
|
| 65 |
+
# newName what the command is now called (an empty string).
|
| 66 |
+
# op the operation (= delete).
|
| 67 |
+
#
|
| 68 |
+
# Results:
|
| 69 |
+
# none
|
| 70 |
+
#
|
| 71 |
+
# Side Effects:
|
| 72 |
+
# The implementation of the [::history] command ceases to exist.
|
| 73 |
+
|
| 74 |
+
trace add command ::history delete [list apply {{oldName newName op} {
|
| 75 |
+
variable history
|
| 76 |
+
unset -nocomplain history
|
| 77 |
+
foreach c [info procs ::tcl::Hist*] {
|
| 78 |
+
rename $c {}
|
| 79 |
+
}
|
| 80 |
+
rename ::tcl::history {}
|
| 81 |
+
} ::tcl}]
|
| 82 |
+
|
| 83 |
+
# tcl::HistAdd --
|
| 84 |
+
#
|
| 85 |
+
# Add an item to the history, and optionally eval it at the global scope
|
| 86 |
+
#
|
| 87 |
+
# Parameters:
|
| 88 |
+
# event the command to add
|
| 89 |
+
# exec (optional) a substring of "exec" causes the command to
|
| 90 |
+
# be evaled.
|
| 91 |
+
# Results:
|
| 92 |
+
# If executing, then the results of the command are returned
|
| 93 |
+
#
|
| 94 |
+
# Side Effects:
|
| 95 |
+
# Adds to the history list
|
| 96 |
+
|
| 97 |
+
proc ::tcl::HistAdd {event {exec {}}} {
|
| 98 |
+
variable history
|
| 99 |
+
|
| 100 |
+
if {
|
| 101 |
+
[prefix longest {exec {}} $exec] eq ""
|
| 102 |
+
&& [llength [info level 0]] == 3
|
| 103 |
+
} then {
|
| 104 |
+
return -code error "bad argument \"$exec\": should be \"exec\""
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
# Do not add empty commands to the history
|
| 108 |
+
if {[string trim $event] eq ""} {
|
| 109 |
+
return ""
|
| 110 |
+
}
|
| 111 |
+
|
| 112 |
+
# Maintain the history
|
| 113 |
+
set history([incr history(nextid)]) $event
|
| 114 |
+
unset -nocomplain history([incr history(oldest)])
|
| 115 |
+
|
| 116 |
+
# Only execute if 'exec' (or non-empty prefix of it) given
|
| 117 |
+
if {$exec eq ""} {
|
| 118 |
+
return ""
|
| 119 |
+
}
|
| 120 |
+
tailcall eval $event
|
| 121 |
+
}
|
| 122 |
+
|
| 123 |
+
# tcl::HistKeep --
|
| 124 |
+
#
|
| 125 |
+
# Set or query the limit on the length of the history list
|
| 126 |
+
#
|
| 127 |
+
# Parameters:
|
| 128 |
+
# limit (optional) the length of the history list
|
| 129 |
+
#
|
| 130 |
+
# Results:
|
| 131 |
+
# If no limit is specified, the current limit is returned
|
| 132 |
+
#
|
| 133 |
+
# Side Effects:
|
| 134 |
+
# Updates history(keep) if a limit is specified
|
| 135 |
+
|
| 136 |
+
proc ::tcl::HistKeep {{count {}}} {
|
| 137 |
+
variable history
|
| 138 |
+
if {[llength [info level 0]] == 1} {
|
| 139 |
+
return $history(keep)
|
| 140 |
+
}
|
| 141 |
+
if {![string is integer -strict $count] || ($count < 0)} {
|
| 142 |
+
return -code error "illegal keep count \"$count\""
|
| 143 |
+
}
|
| 144 |
+
set oldold $history(oldest)
|
| 145 |
+
set history(oldest) [expr {$history(nextid) - $count}]
|
| 146 |
+
for {} {$oldold <= $history(oldest)} {incr oldold} {
|
| 147 |
+
unset -nocomplain history($oldold)
|
| 148 |
+
}
|
| 149 |
+
set history(keep) $count
|
| 150 |
+
}
|
| 151 |
+
|
| 152 |
+
# tcl::HistClear --
|
| 153 |
+
#
|
| 154 |
+
# Erase the history list
|
| 155 |
+
#
|
| 156 |
+
# Parameters:
|
| 157 |
+
# none
|
| 158 |
+
#
|
| 159 |
+
# Results:
|
| 160 |
+
# none
|
| 161 |
+
#
|
| 162 |
+
# Side Effects:
|
| 163 |
+
# Resets the history array, except for the keep limit
|
| 164 |
+
|
| 165 |
+
proc ::tcl::HistClear {} {
|
| 166 |
+
variable history
|
| 167 |
+
set keep $history(keep)
|
| 168 |
+
unset history
|
| 169 |
+
array set history [list \
|
| 170 |
+
nextid 0 \
|
| 171 |
+
keep $keep \
|
| 172 |
+
oldest -$keep \
|
| 173 |
+
]
|
| 174 |
+
}
|
| 175 |
+
|
| 176 |
+
# tcl::HistInfo --
|
| 177 |
+
#
|
| 178 |
+
# Return a pretty-printed version of the history list
|
| 179 |
+
#
|
| 180 |
+
# Parameters:
|
| 181 |
+
# num (optional) the length of the history list to return
|
| 182 |
+
#
|
| 183 |
+
# Results:
|
| 184 |
+
# A formatted history list
|
| 185 |
+
|
| 186 |
+
proc ::tcl::HistInfo {{count {}}} {
|
| 187 |
+
variable history
|
| 188 |
+
if {[llength [info level 0]] == 1} {
|
| 189 |
+
set count [expr {$history(keep) + 1}]
|
| 190 |
+
} elseif {![string is integer -strict $count]} {
|
| 191 |
+
return -code error "bad integer \"$count\""
|
| 192 |
+
}
|
| 193 |
+
set result {}
|
| 194 |
+
set newline ""
|
| 195 |
+
for {set i [expr {$history(nextid) - $count + 1}]} \
|
| 196 |
+
{$i <= $history(nextid)} {incr i} {
|
| 197 |
+
if {![info exists history($i)]} {
|
| 198 |
+
continue
|
| 199 |
+
}
|
| 200 |
+
set cmd [string map [list \n \n\t] [string trimright $history($i) \ \n]]
|
| 201 |
+
append result $newline[format "%6d %s" $i $cmd]
|
| 202 |
+
set newline \n
|
| 203 |
+
}
|
| 204 |
+
return $result
|
| 205 |
+
}
|
| 206 |
+
|
| 207 |
+
# tcl::HistRedo --
|
| 208 |
+
#
|
| 209 |
+
# Fetch the previous or specified event, execute it, and then replace
|
| 210 |
+
# the current history item with that event.
|
| 211 |
+
#
|
| 212 |
+
# Parameters:
|
| 213 |
+
# event (optional) index of history item to redo. Defaults to -1,
|
| 214 |
+
# which means the previous event.
|
| 215 |
+
#
|
| 216 |
+
# Results:
|
| 217 |
+
# Those of the command being redone.
|
| 218 |
+
#
|
| 219 |
+
# Side Effects:
|
| 220 |
+
# Replaces the current history list item with the one being redone.
|
| 221 |
+
|
| 222 |
+
proc ::tcl::HistRedo {{event -1}} {
|
| 223 |
+
variable history
|
| 224 |
+
|
| 225 |
+
set i [HistIndex $event]
|
| 226 |
+
if {$i == $history(nextid)} {
|
| 227 |
+
return -code error "cannot redo the current event"
|
| 228 |
+
}
|
| 229 |
+
set cmd $history($i)
|
| 230 |
+
HistChange $cmd 0
|
| 231 |
+
tailcall eval $cmd
|
| 232 |
+
}
|
| 233 |
+
|
| 234 |
+
# tcl::HistIndex --
|
| 235 |
+
#
|
| 236 |
+
# Map from an event specifier to an index in the history list.
|
| 237 |
+
#
|
| 238 |
+
# Parameters:
|
| 239 |
+
# event index of history item to redo.
|
| 240 |
+
# If this is a positive number, it is used directly.
|
| 241 |
+
# If it is a negative number, then it counts back to a previous
|
| 242 |
+
# event, where -1 is the most recent event.
|
| 243 |
+
# A string can be matched, either by being the prefix of a
|
| 244 |
+
# command or by matching a command with string match.
|
| 245 |
+
#
|
| 246 |
+
# Results:
|
| 247 |
+
# The index into history, or an error if the index didn't match.
|
| 248 |
+
|
| 249 |
+
proc ::tcl::HistIndex {event} {
|
| 250 |
+
variable history
|
| 251 |
+
if {![string is integer -strict $event]} {
|
| 252 |
+
for {set i [expr {$history(nextid)-1}]} {[info exists history($i)]} \
|
| 253 |
+
{incr i -1} {
|
| 254 |
+
if {[string match $event* $history($i)]} {
|
| 255 |
+
return $i
|
| 256 |
+
}
|
| 257 |
+
if {[string match $event $history($i)]} {
|
| 258 |
+
return $i
|
| 259 |
+
}
|
| 260 |
+
}
|
| 261 |
+
return -code error "no event matches \"$event\""
|
| 262 |
+
} elseif {$event <= 0} {
|
| 263 |
+
set i [expr {$history(nextid) + $event}]
|
| 264 |
+
} else {
|
| 265 |
+
set i $event
|
| 266 |
+
}
|
| 267 |
+
if {$i <= $history(oldest)} {
|
| 268 |
+
return -code error "event \"$event\" is too far in the past"
|
| 269 |
+
}
|
| 270 |
+
if {$i > $history(nextid)} {
|
| 271 |
+
return -code error "event \"$event\" hasn't occurred yet"
|
| 272 |
+
}
|
| 273 |
+
return $i
|
| 274 |
+
}
|
| 275 |
+
|
| 276 |
+
# tcl::HistEvent --
|
| 277 |
+
#
|
| 278 |
+
# Map from an event specifier to the value in the history list.
|
| 279 |
+
#
|
| 280 |
+
# Parameters:
|
| 281 |
+
# event index of history item to redo. See index for a description of
|
| 282 |
+
# possible event patterns.
|
| 283 |
+
#
|
| 284 |
+
# Results:
|
| 285 |
+
# The value from the history list.
|
| 286 |
+
|
| 287 |
+
proc ::tcl::HistEvent {{event -1}} {
|
| 288 |
+
variable history
|
| 289 |
+
set i [HistIndex $event]
|
| 290 |
+
if {![info exists history($i)]} {
|
| 291 |
+
return ""
|
| 292 |
+
}
|
| 293 |
+
return [string trimright $history($i) \ \n]
|
| 294 |
+
}
|
| 295 |
+
|
| 296 |
+
# tcl::HistChange --
|
| 297 |
+
#
|
| 298 |
+
# Replace a value in the history list.
|
| 299 |
+
#
|
| 300 |
+
# Parameters:
|
| 301 |
+
# newValue The new value to put into the history list.
|
| 302 |
+
# event (optional) index of history item to redo. See index for a
|
| 303 |
+
# description of possible event patterns. This defaults to 0,
|
| 304 |
+
# which specifies the current event.
|
| 305 |
+
#
|
| 306 |
+
# Side Effects:
|
| 307 |
+
# Changes the history list.
|
| 308 |
+
|
| 309 |
+
proc ::tcl::HistChange {newValue {event 0}} {
|
| 310 |
+
variable history
|
| 311 |
+
set i [HistIndex $event]
|
| 312 |
+
set history($i) $newValue
|
| 313 |
+
}
|
| 314 |
+
|
| 315 |
+
# tcl::HistNextID --
|
| 316 |
+
#
|
| 317 |
+
# Returns the number of the next history event.
|
| 318 |
+
#
|
| 319 |
+
# Parameters:
|
| 320 |
+
# None.
|
| 321 |
+
#
|
| 322 |
+
# Side Effects:
|
| 323 |
+
# None.
|
| 324 |
+
|
| 325 |
+
proc ::tcl::HistNextID {} {
|
| 326 |
+
variable history
|
| 327 |
+
return [expr {$history(nextid) + 1}]
|
| 328 |
+
}
|
| 329 |
+
|
| 330 |
+
return
|
| 331 |
+
|
| 332 |
+
# Local Variables:
|
| 333 |
+
# mode: tcl
|
| 334 |
+
# fill-column: 78
|
| 335 |
+
# End:
|
lib/tcl8.6/init.tcl
ADDED
|
@@ -0,0 +1,827 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# init.tcl --
|
| 2 |
+
#
|
| 3 |
+
# Default system startup file for Tcl-based applications. Defines
|
| 4 |
+
# "unknown" procedure and auto-load facilities.
|
| 5 |
+
#
|
| 6 |
+
# Copyright (c) 1991-1993 The Regents of the University of California.
|
| 7 |
+
# Copyright (c) 1994-1996 Sun Microsystems, Inc.
|
| 8 |
+
# Copyright (c) 1998-1999 Scriptics Corporation.
|
| 9 |
+
# Copyright (c) 2004 Kevin B. Kenny. All rights reserved.
|
| 10 |
+
#
|
| 11 |
+
# See the file "license.terms" for information on usage and redistribution
|
| 12 |
+
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
| 13 |
+
#
|
| 14 |
+
|
| 15 |
+
# This test intentionally written in pre-7.5 Tcl
|
| 16 |
+
if {[info commands package] == ""} {
|
| 17 |
+
error "version mismatch: library\nscripts expect Tcl version 7.5b1 or later but the loaded version is\nonly [info patchlevel]"
|
| 18 |
+
}
|
| 19 |
+
package require -exact Tcl 8.6.14
|
| 20 |
+
|
| 21 |
+
# Compute the auto path to use in this interpreter.
|
| 22 |
+
# The values on the path come from several locations:
|
| 23 |
+
#
|
| 24 |
+
# The environment variable TCLLIBPATH
|
| 25 |
+
#
|
| 26 |
+
# tcl_library, which is the directory containing this init.tcl script.
|
| 27 |
+
# [tclInit] (Tcl_Init()) searches around for the directory containing this
|
| 28 |
+
# init.tcl and defines tcl_library to that location before sourcing it.
|
| 29 |
+
#
|
| 30 |
+
# The parent directory of tcl_library. Adding the parent
|
| 31 |
+
# means that packages in peer directories will be found automatically.
|
| 32 |
+
#
|
| 33 |
+
# Also add the directory ../lib relative to the directory where the
|
| 34 |
+
# executable is located. This is meant to find binary packages for the
|
| 35 |
+
# same architecture as the current executable.
|
| 36 |
+
#
|
| 37 |
+
# tcl_pkgPath, which is set by the platform-specific initialization routines
|
| 38 |
+
# On UNIX it is compiled in
|
| 39 |
+
# On Windows, it is not used
|
| 40 |
+
#
|
| 41 |
+
# (Ticket 41c9857bdd) In a safe interpreter, this file does not set
|
| 42 |
+
# ::auto_path (other than to {} if it is undefined). The caller, typically
|
| 43 |
+
# a Safe Base command, is responsible for setting ::auto_path.
|
| 44 |
+
|
| 45 |
+
if {![info exists auto_path]} {
|
| 46 |
+
if {[info exists env(TCLLIBPATH)] && (![interp issafe])} {
|
| 47 |
+
set auto_path $env(TCLLIBPATH)
|
| 48 |
+
} else {
|
| 49 |
+
set auto_path ""
|
| 50 |
+
}
|
| 51 |
+
}
|
| 52 |
+
namespace eval tcl {
|
| 53 |
+
if {![interp issafe]} {
|
| 54 |
+
variable Dir
|
| 55 |
+
foreach Dir [list $::tcl_library [file dirname $::tcl_library]] {
|
| 56 |
+
if {$Dir ni $::auto_path} {
|
| 57 |
+
lappend ::auto_path $Dir
|
| 58 |
+
}
|
| 59 |
+
}
|
| 60 |
+
set Dir [file join [file dirname [file dirname \
|
| 61 |
+
[info nameofexecutable]]] lib]
|
| 62 |
+
if {$Dir ni $::auto_path} {
|
| 63 |
+
lappend ::auto_path $Dir
|
| 64 |
+
}
|
| 65 |
+
if {[info exists ::tcl_pkgPath]} { catch {
|
| 66 |
+
foreach Dir $::tcl_pkgPath {
|
| 67 |
+
if {$Dir ni $::auto_path} {
|
| 68 |
+
lappend ::auto_path $Dir
|
| 69 |
+
}
|
| 70 |
+
}
|
| 71 |
+
}}
|
| 72 |
+
|
| 73 |
+
variable Path [encoding dirs]
|
| 74 |
+
set Dir [file join $::tcl_library encoding]
|
| 75 |
+
if {$Dir ni $Path} {
|
| 76 |
+
lappend Path $Dir
|
| 77 |
+
encoding dirs $Path
|
| 78 |
+
}
|
| 79 |
+
unset Dir Path
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
# TIP #255 min and max functions
|
| 83 |
+
namespace eval mathfunc {
|
| 84 |
+
proc min {args} {
|
| 85 |
+
if {![llength $args]} {
|
| 86 |
+
return -code error \
|
| 87 |
+
"not enough arguments to math function \"min\""
|
| 88 |
+
}
|
| 89 |
+
set val Inf
|
| 90 |
+
foreach arg $args {
|
| 91 |
+
# This will handle forcing the numeric value without
|
| 92 |
+
# ruining the internal type of a numeric object
|
| 93 |
+
if {[catch {expr {double($arg)}} err]} {
|
| 94 |
+
return -code error $err
|
| 95 |
+
}
|
| 96 |
+
if {$arg < $val} {set val $arg}
|
| 97 |
+
}
|
| 98 |
+
return $val
|
| 99 |
+
}
|
| 100 |
+
proc max {args} {
|
| 101 |
+
if {![llength $args]} {
|
| 102 |
+
return -code error \
|
| 103 |
+
"not enough arguments to math function \"max\""
|
| 104 |
+
}
|
| 105 |
+
set val -Inf
|
| 106 |
+
foreach arg $args {
|
| 107 |
+
# This will handle forcing the numeric value without
|
| 108 |
+
# ruining the internal type of a numeric object
|
| 109 |
+
if {[catch {expr {double($arg)}} err]} {
|
| 110 |
+
return -code error $err
|
| 111 |
+
}
|
| 112 |
+
if {$arg > $val} {set val $arg}
|
| 113 |
+
}
|
| 114 |
+
return $val
|
| 115 |
+
}
|
| 116 |
+
namespace export min max
|
| 117 |
+
}
|
| 118 |
+
}
|
| 119 |
+
|
| 120 |
+
# Windows specific end of initialization
|
| 121 |
+
|
| 122 |
+
if {(![interp issafe]) && ($tcl_platform(platform) eq "windows")} {
|
| 123 |
+
namespace eval tcl {
|
| 124 |
+
proc EnvTraceProc {lo n1 n2 op} {
|
| 125 |
+
global env
|
| 126 |
+
set x $env($n2)
|
| 127 |
+
set env($lo) $x
|
| 128 |
+
set env([string toupper $lo]) $x
|
| 129 |
+
}
|
| 130 |
+
proc InitWinEnv {} {
|
| 131 |
+
global env tcl_platform
|
| 132 |
+
foreach p [array names env] {
|
| 133 |
+
set u [string toupper $p]
|
| 134 |
+
if {$u ne $p} {
|
| 135 |
+
switch -- $u {
|
| 136 |
+
COMSPEC -
|
| 137 |
+
PATH {
|
| 138 |
+
set temp $env($p)
|
| 139 |
+
unset env($p)
|
| 140 |
+
set env($u) $temp
|
| 141 |
+
trace add variable env($p) write \
|
| 142 |
+
[namespace code [list EnvTraceProc $p]]
|
| 143 |
+
trace add variable env($u) write \
|
| 144 |
+
[namespace code [list EnvTraceProc $p]]
|
| 145 |
+
}
|
| 146 |
+
}
|
| 147 |
+
}
|
| 148 |
+
}
|
| 149 |
+
if {![info exists env(COMSPEC)]} {
|
| 150 |
+
set env(COMSPEC) cmd.exe
|
| 151 |
+
}
|
| 152 |
+
}
|
| 153 |
+
InitWinEnv
|
| 154 |
+
}
|
| 155 |
+
}
|
| 156 |
+
|
| 157 |
+
# Setup the unknown package handler
|
| 158 |
+
|
| 159 |
+
|
| 160 |
+
if {[interp issafe]} {
|
| 161 |
+
package unknown {::tcl::tm::UnknownHandler ::tclPkgUnknown}
|
| 162 |
+
} else {
|
| 163 |
+
# Set up search for Tcl Modules (TIP #189).
|
| 164 |
+
# and setup platform specific unknown package handlers
|
| 165 |
+
if {$tcl_platform(os) eq "Darwin"
|
| 166 |
+
&& $tcl_platform(platform) eq "unix"} {
|
| 167 |
+
package unknown {::tcl::tm::UnknownHandler \
|
| 168 |
+
{::tcl::MacOSXPkgUnknown ::tclPkgUnknown}}
|
| 169 |
+
} else {
|
| 170 |
+
package unknown {::tcl::tm::UnknownHandler ::tclPkgUnknown}
|
| 171 |
+
}
|
| 172 |
+
|
| 173 |
+
# Set up the 'clock' ensemble
|
| 174 |
+
|
| 175 |
+
namespace eval ::tcl::clock [list variable TclLibDir $::tcl_library]
|
| 176 |
+
|
| 177 |
+
proc ::tcl::initClock {} {
|
| 178 |
+
# Auto-loading stubs for 'clock.tcl'
|
| 179 |
+
|
| 180 |
+
foreach cmd {add format scan} {
|
| 181 |
+
proc ::tcl::clock::$cmd args {
|
| 182 |
+
variable TclLibDir
|
| 183 |
+
source -encoding utf-8 [file join $TclLibDir clock.tcl]
|
| 184 |
+
return [uplevel 1 [info level 0]]
|
| 185 |
+
}
|
| 186 |
+
}
|
| 187 |
+
|
| 188 |
+
rename ::tcl::initClock {}
|
| 189 |
+
}
|
| 190 |
+
::tcl::initClock
|
| 191 |
+
}
|
| 192 |
+
|
| 193 |
+
# Conditionalize for presence of exec.
|
| 194 |
+
|
| 195 |
+
if {[namespace which -command exec] eq ""} {
|
| 196 |
+
|
| 197 |
+
# Some machines do not have exec. Also, on all
|
| 198 |
+
# platforms, safe interpreters do not have exec.
|
| 199 |
+
|
| 200 |
+
set auto_noexec 1
|
| 201 |
+
}
|
| 202 |
+
|
| 203 |
+
# Define a log command (which can be overwritten to log errors
|
| 204 |
+
# differently, specially when stderr is not available)
|
| 205 |
+
|
| 206 |
+
if {[namespace which -command tclLog] eq ""} {
|
| 207 |
+
proc tclLog {string} {
|
| 208 |
+
catch {puts stderr $string}
|
| 209 |
+
}
|
| 210 |
+
}
|
| 211 |
+
|
| 212 |
+
# unknown --
|
| 213 |
+
# This procedure is called when a Tcl command is invoked that doesn't
|
| 214 |
+
# exist in the interpreter. It takes the following steps to make the
|
| 215 |
+
# command available:
|
| 216 |
+
#
|
| 217 |
+
# 1. See if the autoload facility can locate the command in a
|
| 218 |
+
# Tcl script file. If so, load it and execute it.
|
| 219 |
+
# 2. If the command was invoked interactively at top-level:
|
| 220 |
+
# (a) see if the command exists as an executable UNIX program.
|
| 221 |
+
# If so, "exec" the command.
|
| 222 |
+
# (b) see if the command requests csh-like history substitution
|
| 223 |
+
# in one of the common forms !!, !<number>, or ^old^new. If
|
| 224 |
+
# so, emulate csh's history substitution.
|
| 225 |
+
# (c) see if the command is a unique abbreviation for another
|
| 226 |
+
# command. If so, invoke the command.
|
| 227 |
+
#
|
| 228 |
+
# Arguments:
|
| 229 |
+
# args - A list whose elements are the words of the original
|
| 230 |
+
# command, including the command name.
|
| 231 |
+
|
| 232 |
+
proc unknown args {
|
| 233 |
+
variable ::tcl::UnknownPending
|
| 234 |
+
global auto_noexec auto_noload env tcl_interactive errorInfo errorCode
|
| 235 |
+
|
| 236 |
+
if {[info exists errorInfo]} {
|
| 237 |
+
set savedErrorInfo $errorInfo
|
| 238 |
+
}
|
| 239 |
+
if {[info exists errorCode]} {
|
| 240 |
+
set savedErrorCode $errorCode
|
| 241 |
+
}
|
| 242 |
+
|
| 243 |
+
set name [lindex $args 0]
|
| 244 |
+
if {![info exists auto_noload]} {
|
| 245 |
+
#
|
| 246 |
+
# Make sure we're not trying to load the same proc twice.
|
| 247 |
+
#
|
| 248 |
+
if {[info exists UnknownPending($name)]} {
|
| 249 |
+
return -code error "self-referential recursion\
|
| 250 |
+
in \"unknown\" for command \"$name\""
|
| 251 |
+
}
|
| 252 |
+
set UnknownPending($name) pending
|
| 253 |
+
set ret [catch {
|
| 254 |
+
auto_load $name [uplevel 1 {::namespace current}]
|
| 255 |
+
} msg opts]
|
| 256 |
+
unset UnknownPending($name)
|
| 257 |
+
if {$ret != 0} {
|
| 258 |
+
dict append opts -errorinfo "\n (autoloading \"$name\")"
|
| 259 |
+
return -options $opts $msg
|
| 260 |
+
}
|
| 261 |
+
if {![array size UnknownPending]} {
|
| 262 |
+
unset UnknownPending
|
| 263 |
+
}
|
| 264 |
+
if {$msg} {
|
| 265 |
+
if {[info exists savedErrorCode]} {
|
| 266 |
+
set ::errorCode $savedErrorCode
|
| 267 |
+
} else {
|
| 268 |
+
unset -nocomplain ::errorCode
|
| 269 |
+
}
|
| 270 |
+
if {[info exists savedErrorInfo]} {
|
| 271 |
+
set errorInfo $savedErrorInfo
|
| 272 |
+
} else {
|
| 273 |
+
unset -nocomplain errorInfo
|
| 274 |
+
}
|
| 275 |
+
set code [catch {uplevel 1 $args} msg opts]
|
| 276 |
+
if {$code == 1} {
|
| 277 |
+
#
|
| 278 |
+
# Compute stack trace contribution from the [uplevel].
|
| 279 |
+
# Note the dependence on how Tcl_AddErrorInfo, etc.
|
| 280 |
+
# construct the stack trace.
|
| 281 |
+
#
|
| 282 |
+
set errInfo [dict get $opts -errorinfo]
|
| 283 |
+
set errCode [dict get $opts -errorcode]
|
| 284 |
+
set cinfo $args
|
| 285 |
+
if {[string bytelength $cinfo] > 150} {
|
| 286 |
+
set cinfo [string range $cinfo 0 150]
|
| 287 |
+
while {[string bytelength $cinfo] > 150} {
|
| 288 |
+
set cinfo [string range $cinfo 0 end-1]
|
| 289 |
+
}
|
| 290 |
+
append cinfo ...
|
| 291 |
+
}
|
| 292 |
+
set tail "\n (\"uplevel\" body line 1)\n invoked\
|
| 293 |
+
from within\n\"uplevel 1 \$args\""
|
| 294 |
+
set expect "$msg\n while executing\n\"$cinfo\"$tail"
|
| 295 |
+
if {$errInfo eq $expect} {
|
| 296 |
+
#
|
| 297 |
+
# The stack has only the eval from the expanded command
|
| 298 |
+
# Do not generate any stack trace here.
|
| 299 |
+
#
|
| 300 |
+
dict unset opts -errorinfo
|
| 301 |
+
dict incr opts -level
|
| 302 |
+
return -options $opts $msg
|
| 303 |
+
}
|
| 304 |
+
#
|
| 305 |
+
# Stack trace is nested, trim off just the contribution
|
| 306 |
+
# from the extra "eval" of $args due to the "catch" above.
|
| 307 |
+
#
|
| 308 |
+
set last [string last $tail $errInfo]
|
| 309 |
+
if {$last + [string length $tail] != [string length $errInfo]} {
|
| 310 |
+
# Very likely cannot happen
|
| 311 |
+
return -options $opts $msg
|
| 312 |
+
}
|
| 313 |
+
set errInfo [string range $errInfo 0 $last-1]
|
| 314 |
+
set tail "\"$cinfo\""
|
| 315 |
+
set last [string last $tail $errInfo]
|
| 316 |
+
if {$last < 0 || $last + [string length $tail] != [string length $errInfo]} {
|
| 317 |
+
return -code error -errorcode $errCode \
|
| 318 |
+
-errorinfo $errInfo $msg
|
| 319 |
+
}
|
| 320 |
+
set errInfo [string range $errInfo 0 $last-1]
|
| 321 |
+
set tail "\n invoked from within\n"
|
| 322 |
+
set last [string last $tail $errInfo]
|
| 323 |
+
if {$last + [string length $tail] == [string length $errInfo]} {
|
| 324 |
+
return -code error -errorcode $errCode \
|
| 325 |
+
-errorinfo [string range $errInfo 0 $last-1] $msg
|
| 326 |
+
}
|
| 327 |
+
set tail "\n while executing\n"
|
| 328 |
+
set last [string last $tail $errInfo]
|
| 329 |
+
if {$last + [string length $tail] == [string length $errInfo]} {
|
| 330 |
+
return -code error -errorcode $errCode \
|
| 331 |
+
-errorinfo [string range $errInfo 0 $last-1] $msg
|
| 332 |
+
}
|
| 333 |
+
return -options $opts $msg
|
| 334 |
+
} else {
|
| 335 |
+
dict incr opts -level
|
| 336 |
+
return -options $opts $msg
|
| 337 |
+
}
|
| 338 |
+
}
|
| 339 |
+
}
|
| 340 |
+
|
| 341 |
+
if {([info level] == 1) && ([info script] eq "")
|
| 342 |
+
&& [info exists tcl_interactive] && $tcl_interactive} {
|
| 343 |
+
if {![info exists auto_noexec]} {
|
| 344 |
+
set new [auto_execok $name]
|
| 345 |
+
if {$new ne ""} {
|
| 346 |
+
set redir ""
|
| 347 |
+
if {[namespace which -command console] eq ""} {
|
| 348 |
+
set redir ">&@stdout <@stdin"
|
| 349 |
+
}
|
| 350 |
+
uplevel 1 [list ::catch \
|
| 351 |
+
[concat exec $redir $new [lrange $args 1 end]] \
|
| 352 |
+
::tcl::UnknownResult ::tcl::UnknownOptions]
|
| 353 |
+
dict incr ::tcl::UnknownOptions -level
|
| 354 |
+
return -options $::tcl::UnknownOptions $::tcl::UnknownResult
|
| 355 |
+
}
|
| 356 |
+
}
|
| 357 |
+
if {$name eq "!!"} {
|
| 358 |
+
set newcmd [history event]
|
| 359 |
+
} elseif {[regexp {^!(.+)$} $name -> event]} {
|
| 360 |
+
set newcmd [history event $event]
|
| 361 |
+
} elseif {[regexp {^\^([^^]*)\^([^^]*)\^?$} $name -> old new]} {
|
| 362 |
+
set newcmd [history event -1]
|
| 363 |
+
catch {regsub -all -- $old $newcmd $new newcmd}
|
| 364 |
+
}
|
| 365 |
+
if {[info exists newcmd]} {
|
| 366 |
+
tclLog $newcmd
|
| 367 |
+
history change $newcmd 0
|
| 368 |
+
uplevel 1 [list ::catch $newcmd \
|
| 369 |
+
::tcl::UnknownResult ::tcl::UnknownOptions]
|
| 370 |
+
dict incr ::tcl::UnknownOptions -level
|
| 371 |
+
return -options $::tcl::UnknownOptions $::tcl::UnknownResult
|
| 372 |
+
}
|
| 373 |
+
|
| 374 |
+
set ret [catch [list uplevel 1 [list info commands $name*]] candidates]
|
| 375 |
+
if {$name eq "::"} {
|
| 376 |
+
set name ""
|
| 377 |
+
}
|
| 378 |
+
if {$ret != 0} {
|
| 379 |
+
dict append opts -errorinfo \
|
| 380 |
+
"\n (expanding command prefix \"$name\" in unknown)"
|
| 381 |
+
return -options $opts $candidates
|
| 382 |
+
}
|
| 383 |
+
# Filter out bogus matches when $name contained
|
| 384 |
+
# a glob-special char [Bug 946952]
|
| 385 |
+
if {$name eq ""} {
|
| 386 |
+
# Handle empty $name separately due to strangeness
|
| 387 |
+
# in [string first] (See RFE 1243354)
|
| 388 |
+
set cmds $candidates
|
| 389 |
+
} else {
|
| 390 |
+
set cmds [list]
|
| 391 |
+
foreach x $candidates {
|
| 392 |
+
if {[string first $name $x] == 0} {
|
| 393 |
+
lappend cmds $x
|
| 394 |
+
}
|
| 395 |
+
}
|
| 396 |
+
}
|
| 397 |
+
if {[llength $cmds] == 1} {
|
| 398 |
+
uplevel 1 [list ::catch [lreplace $args 0 0 [lindex $cmds 0]] \
|
| 399 |
+
::tcl::UnknownResult ::tcl::UnknownOptions]
|
| 400 |
+
dict incr ::tcl::UnknownOptions -level
|
| 401 |
+
return -options $::tcl::UnknownOptions $::tcl::UnknownResult
|
| 402 |
+
}
|
| 403 |
+
if {[llength $cmds]} {
|
| 404 |
+
return -code error "ambiguous command name \"$name\": [lsort $cmds]"
|
| 405 |
+
}
|
| 406 |
+
}
|
| 407 |
+
return -code error -errorcode [list TCL LOOKUP COMMAND $name] \
|
| 408 |
+
"invalid command name \"$name\""
|
| 409 |
+
}
|
| 410 |
+
|
| 411 |
+
# auto_load --
|
| 412 |
+
# Checks a collection of library directories to see if a procedure
|
| 413 |
+
# is defined in one of them. If so, it sources the appropriate
|
| 414 |
+
# library file to create the procedure. Returns 1 if it successfully
|
| 415 |
+
# loaded the procedure, 0 otherwise.
|
| 416 |
+
#
|
| 417 |
+
# Arguments:
|
| 418 |
+
# cmd - Name of the command to find and load.
|
| 419 |
+
# namespace (optional) The namespace where the command is being used - must be
|
| 420 |
+
# a canonical namespace as returned [namespace current]
|
| 421 |
+
# for instance. If not given, namespace current is used.
|
| 422 |
+
|
| 423 |
+
proc auto_load {cmd {namespace {}}} {
|
| 424 |
+
global auto_index auto_path
|
| 425 |
+
|
| 426 |
+
if {$namespace eq ""} {
|
| 427 |
+
set namespace [uplevel 1 [list ::namespace current]]
|
| 428 |
+
}
|
| 429 |
+
set nameList [auto_qualify $cmd $namespace]
|
| 430 |
+
# workaround non canonical auto_index entries that might be around
|
| 431 |
+
# from older auto_mkindex versions
|
| 432 |
+
lappend nameList $cmd
|
| 433 |
+
foreach name $nameList {
|
| 434 |
+
if {[info exists auto_index($name)]} {
|
| 435 |
+
namespace eval :: $auto_index($name)
|
| 436 |
+
# There's a couple of ways to look for a command of a given
|
| 437 |
+
# name. One is to use
|
| 438 |
+
# info commands $name
|
| 439 |
+
# Unfortunately, if the name has glob-magic chars in it like *
|
| 440 |
+
# or [], it may not match. For our purposes here, a better
|
| 441 |
+
# route is to use
|
| 442 |
+
# namespace which -command $name
|
| 443 |
+
if {[namespace which -command $name] ne ""} {
|
| 444 |
+
return 1
|
| 445 |
+
}
|
| 446 |
+
}
|
| 447 |
+
}
|
| 448 |
+
if {![info exists auto_path]} {
|
| 449 |
+
return 0
|
| 450 |
+
}
|
| 451 |
+
|
| 452 |
+
if {![auto_load_index]} {
|
| 453 |
+
return 0
|
| 454 |
+
}
|
| 455 |
+
foreach name $nameList {
|
| 456 |
+
if {[info exists auto_index($name)]} {
|
| 457 |
+
namespace eval :: $auto_index($name)
|
| 458 |
+
if {[namespace which -command $name] ne ""} {
|
| 459 |
+
return 1
|
| 460 |
+
}
|
| 461 |
+
}
|
| 462 |
+
}
|
| 463 |
+
return 0
|
| 464 |
+
}
|
| 465 |
+
|
| 466 |
+
# auto_load_index --
|
| 467 |
+
# Loads the contents of tclIndex files on the auto_path directory
|
| 468 |
+
# list. This is usually invoked within auto_load to load the index
|
| 469 |
+
# of available commands. Returns 1 if the index is loaded, and 0 if
|
| 470 |
+
# the index is already loaded and up to date.
|
| 471 |
+
#
|
| 472 |
+
# Arguments:
|
| 473 |
+
# None.
|
| 474 |
+
|
| 475 |
+
proc auto_load_index {} {
|
| 476 |
+
variable ::tcl::auto_oldpath
|
| 477 |
+
global auto_index auto_path
|
| 478 |
+
|
| 479 |
+
if {[info exists auto_oldpath] && ($auto_oldpath eq $auto_path)} {
|
| 480 |
+
return 0
|
| 481 |
+
}
|
| 482 |
+
set auto_oldpath $auto_path
|
| 483 |
+
|
| 484 |
+
# Check if we are a safe interpreter. In that case, we support only
|
| 485 |
+
# newer format tclIndex files.
|
| 486 |
+
|
| 487 |
+
set issafe [interp issafe]
|
| 488 |
+
for {set i [expr {[llength $auto_path] - 1}]} {$i >= 0} {incr i -1} {
|
| 489 |
+
set dir [lindex $auto_path $i]
|
| 490 |
+
set f ""
|
| 491 |
+
if {$issafe} {
|
| 492 |
+
catch {source [file join $dir tclIndex]}
|
| 493 |
+
} elseif {[catch {set f [open [file join $dir tclIndex]]}]} {
|
| 494 |
+
continue
|
| 495 |
+
} else {
|
| 496 |
+
set error [catch {
|
| 497 |
+
fconfigure $f -eofchar "\x1A {}"
|
| 498 |
+
set id [gets $f]
|
| 499 |
+
if {$id eq "# Tcl autoload index file, version 2.0"} {
|
| 500 |
+
eval [read $f]
|
| 501 |
+
} elseif {$id eq "# Tcl autoload index file: each line identifies a Tcl"} {
|
| 502 |
+
while {[gets $f line] >= 0} {
|
| 503 |
+
if {([string index $line 0] eq "#") \
|
| 504 |
+
|| ([llength $line] != 2)} {
|
| 505 |
+
continue
|
| 506 |
+
}
|
| 507 |
+
set name [lindex $line 0]
|
| 508 |
+
set auto_index($name) \
|
| 509 |
+
"source [file join $dir [lindex $line 1]]"
|
| 510 |
+
}
|
| 511 |
+
} else {
|
| 512 |
+
error "[file join $dir tclIndex] isn't a proper Tcl index file"
|
| 513 |
+
}
|
| 514 |
+
} msg opts]
|
| 515 |
+
if {$f ne ""} {
|
| 516 |
+
close $f
|
| 517 |
+
}
|
| 518 |
+
if {$error} {
|
| 519 |
+
return -options $opts $msg
|
| 520 |
+
}
|
| 521 |
+
}
|
| 522 |
+
}
|
| 523 |
+
return 1
|
| 524 |
+
}
|
| 525 |
+
|
| 526 |
+
# auto_qualify --
|
| 527 |
+
#
|
| 528 |
+
# Compute a fully qualified names list for use in the auto_index array.
|
| 529 |
+
# For historical reasons, commands in the global namespace do not have leading
|
| 530 |
+
# :: in the index key. The list has two elements when the command name is
|
| 531 |
+
# relative (no leading ::) and the namespace is not the global one. Otherwise
|
| 532 |
+
# only one name is returned (and searched in the auto_index).
|
| 533 |
+
#
|
| 534 |
+
# Arguments -
|
| 535 |
+
# cmd The command name. Can be any name accepted for command
|
| 536 |
+
# invocations (Like "foo::::bar").
|
| 537 |
+
# namespace The namespace where the command is being used - must be
|
| 538 |
+
# a canonical namespace as returned by [namespace current]
|
| 539 |
+
# for instance.
|
| 540 |
+
|
| 541 |
+
proc auto_qualify {cmd namespace} {
|
| 542 |
+
|
| 543 |
+
# count separators and clean them up
|
| 544 |
+
# (making sure that foo:::::bar will be treated as foo::bar)
|
| 545 |
+
set n [regsub -all {::+} $cmd :: cmd]
|
| 546 |
+
|
| 547 |
+
# Ignore namespace if the name starts with ::
|
| 548 |
+
# Handle special case of only leading ::
|
| 549 |
+
|
| 550 |
+
# Before each return case we give an example of which category it is
|
| 551 |
+
# with the following form :
|
| 552 |
+
# (inputCmd, inputNameSpace) -> output
|
| 553 |
+
|
| 554 |
+
if {[string match ::* $cmd]} {
|
| 555 |
+
if {$n > 1} {
|
| 556 |
+
# (::foo::bar , *) -> ::foo::bar
|
| 557 |
+
return [list $cmd]
|
| 558 |
+
} else {
|
| 559 |
+
# (::global , *) -> global
|
| 560 |
+
return [list [string range $cmd 2 end]]
|
| 561 |
+
}
|
| 562 |
+
}
|
| 563 |
+
|
| 564 |
+
# Potentially returning 2 elements to try :
|
| 565 |
+
# (if the current namespace is not the global one)
|
| 566 |
+
|
| 567 |
+
if {$n == 0} {
|
| 568 |
+
if {$namespace eq "::"} {
|
| 569 |
+
# (nocolons , ::) -> nocolons
|
| 570 |
+
return [list $cmd]
|
| 571 |
+
} else {
|
| 572 |
+
# (nocolons , ::sub) -> ::sub::nocolons nocolons
|
| 573 |
+
return [list ${namespace}::$cmd $cmd]
|
| 574 |
+
}
|
| 575 |
+
} elseif {$namespace eq "::"} {
|
| 576 |
+
# (foo::bar , ::) -> ::foo::bar
|
| 577 |
+
return [list ::$cmd]
|
| 578 |
+
} else {
|
| 579 |
+
# (foo::bar , ::sub) -> ::sub::foo::bar ::foo::bar
|
| 580 |
+
return [list ${namespace}::$cmd ::$cmd]
|
| 581 |
+
}
|
| 582 |
+
}
|
| 583 |
+
|
| 584 |
+
# auto_import --
|
| 585 |
+
#
|
| 586 |
+
# Invoked during "namespace import" to make see if the imported commands
|
| 587 |
+
# reside in an autoloaded library. If so, the commands are loaded so
|
| 588 |
+
# that they will be available for the import links. If not, then this
|
| 589 |
+
# procedure does nothing.
|
| 590 |
+
#
|
| 591 |
+
# Arguments -
|
| 592 |
+
# pattern The pattern of commands being imported (like "foo::*")
|
| 593 |
+
# a canonical namespace as returned by [namespace current]
|
| 594 |
+
|
| 595 |
+
proc auto_import {pattern} {
|
| 596 |
+
global auto_index
|
| 597 |
+
|
| 598 |
+
# If no namespace is specified, this will be an error case
|
| 599 |
+
|
| 600 |
+
if {![string match *::* $pattern]} {
|
| 601 |
+
return
|
| 602 |
+
}
|
| 603 |
+
|
| 604 |
+
set ns [uplevel 1 [list ::namespace current]]
|
| 605 |
+
set patternList [auto_qualify $pattern $ns]
|
| 606 |
+
|
| 607 |
+
auto_load_index
|
| 608 |
+
|
| 609 |
+
foreach pattern $patternList {
|
| 610 |
+
foreach name [array names auto_index $pattern] {
|
| 611 |
+
if {([namespace which -command $name] eq "")
|
| 612 |
+
&& ([namespace qualifiers $pattern] eq [namespace qualifiers $name])} {
|
| 613 |
+
namespace eval :: $auto_index($name)
|
| 614 |
+
}
|
| 615 |
+
}
|
| 616 |
+
}
|
| 617 |
+
}
|
| 618 |
+
|
| 619 |
+
# auto_execok --
|
| 620 |
+
#
|
| 621 |
+
# Returns string that indicates name of program to execute if
|
| 622 |
+
# name corresponds to a shell builtin or an executable in the
|
| 623 |
+
# Windows search path, or "" otherwise. Builds an associative
|
| 624 |
+
# array auto_execs that caches information about previous checks,
|
| 625 |
+
# for speed.
|
| 626 |
+
#
|
| 627 |
+
# Arguments:
|
| 628 |
+
# name - Name of a command.
|
| 629 |
+
|
| 630 |
+
if {$tcl_platform(platform) eq "windows"} {
|
| 631 |
+
# Windows version.
|
| 632 |
+
#
|
| 633 |
+
# Note that file executable doesn't work under Windows, so we have to
|
| 634 |
+
# look for files with .exe, .com, or .bat extensions. Also, the path
|
| 635 |
+
# may be in the Path or PATH environment variables, and path
|
| 636 |
+
# components are separated with semicolons, not colons as under Unix.
|
| 637 |
+
#
|
| 638 |
+
proc auto_execok name {
|
| 639 |
+
global auto_execs env tcl_platform
|
| 640 |
+
|
| 641 |
+
if {[info exists auto_execs($name)]} {
|
| 642 |
+
return $auto_execs($name)
|
| 643 |
+
}
|
| 644 |
+
set auto_execs($name) ""
|
| 645 |
+
|
| 646 |
+
set shellBuiltins [list assoc cls copy date del dir echo erase exit ftype \
|
| 647 |
+
md mkdir mklink move rd ren rename rmdir start time type ver vol]
|
| 648 |
+
if {[info exists env(PATHEXT)]} {
|
| 649 |
+
# Add an initial ; to have the {} extension check first.
|
| 650 |
+
set execExtensions [split ";$env(PATHEXT)" ";"]
|
| 651 |
+
} else {
|
| 652 |
+
set execExtensions [list {} .com .exe .bat .cmd]
|
| 653 |
+
}
|
| 654 |
+
|
| 655 |
+
if {[string tolower $name] in $shellBuiltins} {
|
| 656 |
+
# When this is command.com for some reason on Win2K, Tcl won't
|
| 657 |
+
# exec it unless the case is right, which this corrects. COMSPEC
|
| 658 |
+
# may not point to a real file, so do the check.
|
| 659 |
+
set cmd $env(COMSPEC)
|
| 660 |
+
if {[file exists $cmd]} {
|
| 661 |
+
set cmd [file attributes $cmd -shortname]
|
| 662 |
+
}
|
| 663 |
+
return [set auto_execs($name) [list $cmd /c $name]]
|
| 664 |
+
}
|
| 665 |
+
|
| 666 |
+
if {[llength [file split $name]] != 1} {
|
| 667 |
+
foreach ext $execExtensions {
|
| 668 |
+
set file ${name}${ext}
|
| 669 |
+
if {[file exists $file] && ![file isdirectory $file]} {
|
| 670 |
+
return [set auto_execs($name) [list $file]]
|
| 671 |
+
}
|
| 672 |
+
}
|
| 673 |
+
return ""
|
| 674 |
+
}
|
| 675 |
+
|
| 676 |
+
set path "[file dirname [info nameof]];.;"
|
| 677 |
+
if {[info exists env(SystemRoot)]} {
|
| 678 |
+
set windir $env(SystemRoot)
|
| 679 |
+
} elseif {[info exists env(WINDIR)]} {
|
| 680 |
+
set windir $env(WINDIR)
|
| 681 |
+
}
|
| 682 |
+
if {[info exists windir]} {
|
| 683 |
+
if {$tcl_platform(os) eq "Windows NT"} {
|
| 684 |
+
append path "$windir/system32;"
|
| 685 |
+
}
|
| 686 |
+
append path "$windir/system;$windir;"
|
| 687 |
+
}
|
| 688 |
+
|
| 689 |
+
foreach var {PATH Path path} {
|
| 690 |
+
if {[info exists env($var)]} {
|
| 691 |
+
append path ";$env($var)"
|
| 692 |
+
}
|
| 693 |
+
}
|
| 694 |
+
|
| 695 |
+
foreach ext $execExtensions {
|
| 696 |
+
unset -nocomplain checked
|
| 697 |
+
foreach dir [split $path {;}] {
|
| 698 |
+
# Skip already checked directories
|
| 699 |
+
if {[info exists checked($dir)] || ($dir eq "")} {
|
| 700 |
+
continue
|
| 701 |
+
}
|
| 702 |
+
set checked($dir) {}
|
| 703 |
+
set file [file join $dir ${name}${ext}]
|
| 704 |
+
if {[file exists $file] && ![file isdirectory $file]} {
|
| 705 |
+
return [set auto_execs($name) [list $file]]
|
| 706 |
+
}
|
| 707 |
+
}
|
| 708 |
+
}
|
| 709 |
+
return ""
|
| 710 |
+
}
|
| 711 |
+
|
| 712 |
+
} else {
|
| 713 |
+
# Unix version.
|
| 714 |
+
#
|
| 715 |
+
proc auto_execok name {
|
| 716 |
+
global auto_execs env
|
| 717 |
+
|
| 718 |
+
if {[info exists auto_execs($name)]} {
|
| 719 |
+
return $auto_execs($name)
|
| 720 |
+
}
|
| 721 |
+
set auto_execs($name) ""
|
| 722 |
+
if {[llength [file split $name]] != 1} {
|
| 723 |
+
if {[file executable $name] && ![file isdirectory $name]} {
|
| 724 |
+
set auto_execs($name) [list $name]
|
| 725 |
+
}
|
| 726 |
+
return $auto_execs($name)
|
| 727 |
+
}
|
| 728 |
+
foreach dir [split $env(PATH) :] {
|
| 729 |
+
if {$dir eq ""} {
|
| 730 |
+
set dir .
|
| 731 |
+
}
|
| 732 |
+
set file [file join $dir $name]
|
| 733 |
+
if {[file executable $file] && ![file isdirectory $file]} {
|
| 734 |
+
set auto_execs($name) [list $file]
|
| 735 |
+
return $auto_execs($name)
|
| 736 |
+
}
|
| 737 |
+
}
|
| 738 |
+
return ""
|
| 739 |
+
}
|
| 740 |
+
|
| 741 |
+
}
|
| 742 |
+
|
| 743 |
+
# ::tcl::CopyDirectory --
|
| 744 |
+
#
|
| 745 |
+
# This procedure is called by Tcl's core when attempts to call the
|
| 746 |
+
# filesystem's copydirectory function fail. The semantics of the call
|
| 747 |
+
# are that 'dest' does not yet exist, i.e. dest should become the exact
|
| 748 |
+
# image of src. If dest does exist, we throw an error.
|
| 749 |
+
#
|
| 750 |
+
# Note that making changes to this procedure can change the results
|
| 751 |
+
# of running Tcl's tests.
|
| 752 |
+
#
|
| 753 |
+
# Arguments:
|
| 754 |
+
# action - "renaming" or "copying"
|
| 755 |
+
# src - source directory
|
| 756 |
+
# dest - destination directory
|
| 757 |
+
proc tcl::CopyDirectory {action src dest} {
|
| 758 |
+
set nsrc [file normalize $src]
|
| 759 |
+
set ndest [file normalize $dest]
|
| 760 |
+
|
| 761 |
+
if {$action eq "renaming"} {
|
| 762 |
+
# Can't rename volumes. We could give a more precise
|
| 763 |
+
# error message here, but that would break the test suite.
|
| 764 |
+
if {$nsrc in [file volumes]} {
|
| 765 |
+
return -code error "error $action \"$src\" to\
|
| 766 |
+
\"$dest\": trying to rename a volume or move a directory\
|
| 767 |
+
into itself"
|
| 768 |
+
}
|
| 769 |
+
}
|
| 770 |
+
if {[file exists $dest]} {
|
| 771 |
+
if {$nsrc eq $ndest} {
|
| 772 |
+
return -code error "error $action \"$src\" to\
|
| 773 |
+
\"$dest\": trying to rename a volume or move a directory\
|
| 774 |
+
into itself"
|
| 775 |
+
}
|
| 776 |
+
if {$action eq "copying"} {
|
| 777 |
+
# We used to throw an error here, but, looking more closely
|
| 778 |
+
# at the core copy code in tclFCmd.c, if the destination
|
| 779 |
+
# exists, then we should only call this function if -force
|
| 780 |
+
# is true, which means we just want to over-write. So,
|
| 781 |
+
# the following code is now commented out.
|
| 782 |
+
#
|
| 783 |
+
# return -code error "error $action \"$src\" to\
|
| 784 |
+
# \"$dest\": file already exists"
|
| 785 |
+
} else {
|
| 786 |
+
# Depending on the platform, and on the current
|
| 787 |
+
# working directory, the directories '.', '..'
|
| 788 |
+
# can be returned in various combinations. Anyway,
|
| 789 |
+
# if any other file is returned, we must signal an error.
|
| 790 |
+
set existing [glob -nocomplain -directory $dest * .*]
|
| 791 |
+
lappend existing {*}[glob -nocomplain -directory $dest \
|
| 792 |
+
-type hidden * .*]
|
| 793 |
+
foreach s $existing {
|
| 794 |
+
if {[file tail $s] ni {. ..}} {
|
| 795 |
+
return -code error "error $action \"$src\" to\
|
| 796 |
+
\"$dest\": file already exists"
|
| 797 |
+
}
|
| 798 |
+
}
|
| 799 |
+
}
|
| 800 |
+
} else {
|
| 801 |
+
if {[string first $nsrc $ndest] >= 0} {
|
| 802 |
+
set srclen [expr {[llength [file split $nsrc]] - 1}]
|
| 803 |
+
set ndest [lindex [file split $ndest] $srclen]
|
| 804 |
+
if {$ndest eq [file tail $nsrc]} {
|
| 805 |
+
return -code error "error $action \"$src\" to\
|
| 806 |
+
\"$dest\": trying to rename a volume or move a directory\
|
| 807 |
+
into itself"
|
| 808 |
+
}
|
| 809 |
+
}
|
| 810 |
+
file mkdir $dest
|
| 811 |
+
}
|
| 812 |
+
# Have to be careful to capture both visible and hidden files.
|
| 813 |
+
# We will also be more generous to the file system and not
|
| 814 |
+
# assume the hidden and non-hidden lists are non-overlapping.
|
| 815 |
+
#
|
| 816 |
+
# On Unix 'hidden' files begin with '.'. On other platforms
|
| 817 |
+
# or filesystems hidden files may have other interpretations.
|
| 818 |
+
set filelist [concat [glob -nocomplain -directory $src *] \
|
| 819 |
+
[glob -nocomplain -directory $src -types hidden *]]
|
| 820 |
+
|
| 821 |
+
foreach s [lsort -unique $filelist] {
|
| 822 |
+
if {[file tail $s] ni {. ..}} {
|
| 823 |
+
file copy -force -- $s [file join $dest [file tail $s]]
|
| 824 |
+
}
|
| 825 |
+
}
|
| 826 |
+
return
|
| 827 |
+
}
|
lib/tcl8.6/package.tcl
ADDED
|
@@ -0,0 +1,751 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# package.tcl --
|
| 2 |
+
#
|
| 3 |
+
# utility procs formerly in init.tcl which can be loaded on demand
|
| 4 |
+
# for package management.
|
| 5 |
+
#
|
| 6 |
+
# Copyright (c) 1991-1993 The Regents of the University of California.
|
| 7 |
+
# Copyright (c) 1994-1998 Sun Microsystems, Inc.
|
| 8 |
+
#
|
| 9 |
+
# See the file "license.terms" for information on usage and redistribution
|
| 10 |
+
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
| 11 |
+
#
|
| 12 |
+
|
| 13 |
+
namespace eval tcl::Pkg {}
|
| 14 |
+
|
| 15 |
+
# ::tcl::Pkg::CompareExtension --
|
| 16 |
+
#
|
| 17 |
+
# Used internally by pkg_mkIndex to compare the extension of a file to a given
|
| 18 |
+
# extension. On Windows, it uses a case-insensitive comparison because the
|
| 19 |
+
# file system can be file insensitive.
|
| 20 |
+
#
|
| 21 |
+
# Arguments:
|
| 22 |
+
# fileName name of a file whose extension is compared
|
| 23 |
+
# ext (optional) The extension to compare against; you must
|
| 24 |
+
# provide the starting dot.
|
| 25 |
+
# Defaults to [info sharedlibextension]
|
| 26 |
+
#
|
| 27 |
+
# Results:
|
| 28 |
+
# Returns 1 if the extension matches, 0 otherwise
|
| 29 |
+
|
| 30 |
+
proc tcl::Pkg::CompareExtension {fileName {ext {}}} {
|
| 31 |
+
global tcl_platform
|
| 32 |
+
if {$ext eq ""} {set ext [info sharedlibextension]}
|
| 33 |
+
if {$tcl_platform(platform) eq "windows"} {
|
| 34 |
+
return [string equal -nocase [file extension $fileName] $ext]
|
| 35 |
+
} else {
|
| 36 |
+
# Some unices add trailing numbers after the .so, so
|
| 37 |
+
# we could have something like '.so.1.2'.
|
| 38 |
+
set root $fileName
|
| 39 |
+
while {1} {
|
| 40 |
+
set currExt [file extension $root]
|
| 41 |
+
if {$currExt eq $ext} {
|
| 42 |
+
return 1
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
# The current extension does not match; if it is not a numeric
|
| 46 |
+
# value, quit, as we are only looking to ignore version number
|
| 47 |
+
# extensions. Otherwise we might return 1 in this case:
|
| 48 |
+
# tcl::Pkg::CompareExtension foo.so.bar .so
|
| 49 |
+
# which should not match.
|
| 50 |
+
|
| 51 |
+
if {![string is integer -strict [string range $currExt 1 end]]} {
|
| 52 |
+
return 0
|
| 53 |
+
}
|
| 54 |
+
set root [file rootname $root]
|
| 55 |
+
}
|
| 56 |
+
}
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
# pkg_mkIndex --
|
| 60 |
+
# This procedure creates a package index in a given directory. The package
|
| 61 |
+
# index consists of a "pkgIndex.tcl" file whose contents are a Tcl script that
|
| 62 |
+
# sets up package information with "package require" commands. The commands
|
| 63 |
+
# describe all of the packages defined by the files given as arguments.
|
| 64 |
+
#
|
| 65 |
+
# Arguments:
|
| 66 |
+
# -direct (optional) If this flag is present, the generated
|
| 67 |
+
# code in pkgMkIndex.tcl will cause the package to be
|
| 68 |
+
# loaded when "package require" is executed, rather
|
| 69 |
+
# than lazily when the first reference to an exported
|
| 70 |
+
# procedure in the package is made.
|
| 71 |
+
# -verbose (optional) Verbose output; the name of each file that
|
| 72 |
+
# was successfully processed is printed out. Additionally,
|
| 73 |
+
# if processing of a file failed a message is printed.
|
| 74 |
+
# -load pat (optional) Preload any packages whose names match
|
| 75 |
+
# the pattern. Used to handle DLLs that depend on
|
| 76 |
+
# other packages during their Init procedure.
|
| 77 |
+
# dir - Name of the directory in which to create the index.
|
| 78 |
+
# args - Any number of additional arguments, each giving
|
| 79 |
+
# a glob pattern that matches the names of one or
|
| 80 |
+
# more shared libraries or Tcl script files in
|
| 81 |
+
# dir.
|
| 82 |
+
|
| 83 |
+
proc pkg_mkIndex {args} {
|
| 84 |
+
set usage {"pkg_mkIndex ?-direct? ?-lazy? ?-load pattern? ?-verbose? ?--? dir ?pattern ...?"}
|
| 85 |
+
|
| 86 |
+
set argCount [llength $args]
|
| 87 |
+
if {$argCount < 1} {
|
| 88 |
+
return -code error "wrong # args: should be\n$usage"
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
set more ""
|
| 92 |
+
set direct 1
|
| 93 |
+
set doVerbose 0
|
| 94 |
+
set loadPat ""
|
| 95 |
+
for {set idx 0} {$idx < $argCount} {incr idx} {
|
| 96 |
+
set flag [lindex $args $idx]
|
| 97 |
+
switch -glob -- $flag {
|
| 98 |
+
-- {
|
| 99 |
+
# done with the flags
|
| 100 |
+
incr idx
|
| 101 |
+
break
|
| 102 |
+
}
|
| 103 |
+
-verbose {
|
| 104 |
+
set doVerbose 1
|
| 105 |
+
}
|
| 106 |
+
-lazy {
|
| 107 |
+
set direct 0
|
| 108 |
+
append more " -lazy"
|
| 109 |
+
}
|
| 110 |
+
-direct {
|
| 111 |
+
append more " -direct"
|
| 112 |
+
}
|
| 113 |
+
-load {
|
| 114 |
+
incr idx
|
| 115 |
+
set loadPat [lindex $args $idx]
|
| 116 |
+
append more " -load $loadPat"
|
| 117 |
+
}
|
| 118 |
+
-* {
|
| 119 |
+
return -code error "unknown flag $flag: should be\n$usage"
|
| 120 |
+
}
|
| 121 |
+
default {
|
| 122 |
+
# done with the flags
|
| 123 |
+
break
|
| 124 |
+
}
|
| 125 |
+
}
|
| 126 |
+
}
|
| 127 |
+
|
| 128 |
+
set dir [lindex $args $idx]
|
| 129 |
+
set patternList [lrange $args [expr {$idx + 1}] end]
|
| 130 |
+
if {![llength $patternList]} {
|
| 131 |
+
set patternList [list "*.tcl" "*[info sharedlibextension]"]
|
| 132 |
+
}
|
| 133 |
+
|
| 134 |
+
try {
|
| 135 |
+
set fileList [glob -directory $dir -tails -types {r f} -- \
|
| 136 |
+
{*}$patternList]
|
| 137 |
+
} on error {msg opt} {
|
| 138 |
+
return -options $opt $msg
|
| 139 |
+
}
|
| 140 |
+
foreach file $fileList {
|
| 141 |
+
# For each file, figure out what commands and packages it provides.
|
| 142 |
+
# To do this, create a child interpreter, load the file into the
|
| 143 |
+
# interpreter, and get a list of the new commands and packages that
|
| 144 |
+
# are defined.
|
| 145 |
+
|
| 146 |
+
if {$file eq "pkgIndex.tcl"} {
|
| 147 |
+
continue
|
| 148 |
+
}
|
| 149 |
+
|
| 150 |
+
set c [interp create]
|
| 151 |
+
|
| 152 |
+
# Load into the child any packages currently loaded in the parent
|
| 153 |
+
# interpreter that match the -load pattern.
|
| 154 |
+
|
| 155 |
+
if {$loadPat ne ""} {
|
| 156 |
+
if {$doVerbose} {
|
| 157 |
+
tclLog "currently loaded packages: '[info loaded]'"
|
| 158 |
+
tclLog "trying to load all packages matching $loadPat"
|
| 159 |
+
}
|
| 160 |
+
if {![llength [info loaded]]} {
|
| 161 |
+
tclLog "warning: no packages are currently loaded, nothing"
|
| 162 |
+
tclLog "can possibly match '$loadPat'"
|
| 163 |
+
}
|
| 164 |
+
}
|
| 165 |
+
foreach pkg [info loaded] {
|
| 166 |
+
if {![string match -nocase $loadPat [lindex $pkg 1]]} {
|
| 167 |
+
continue
|
| 168 |
+
}
|
| 169 |
+
if {$doVerbose} {
|
| 170 |
+
tclLog "package [lindex $pkg 1] matches '$loadPat'"
|
| 171 |
+
}
|
| 172 |
+
try {
|
| 173 |
+
load [lindex $pkg 0] [lindex $pkg 1] $c
|
| 174 |
+
} on error err {
|
| 175 |
+
if {$doVerbose} {
|
| 176 |
+
tclLog "warning: load [lindex $pkg 0]\
|
| 177 |
+
[lindex $pkg 1]\nfailed with: $err"
|
| 178 |
+
}
|
| 179 |
+
} on ok {} {
|
| 180 |
+
if {$doVerbose} {
|
| 181 |
+
tclLog "loaded [lindex $pkg 0] [lindex $pkg 1]"
|
| 182 |
+
}
|
| 183 |
+
}
|
| 184 |
+
if {[lindex $pkg 1] eq "Tk"} {
|
| 185 |
+
# Withdraw . if Tk was loaded, to avoid showing a window.
|
| 186 |
+
$c eval [list wm withdraw .]
|
| 187 |
+
}
|
| 188 |
+
}
|
| 189 |
+
|
| 190 |
+
$c eval {
|
| 191 |
+
# Stub out the package command so packages can require other
|
| 192 |
+
# packages.
|
| 193 |
+
|
| 194 |
+
rename package __package_orig
|
| 195 |
+
proc package {what args} {
|
| 196 |
+
switch -- $what {
|
| 197 |
+
require {
|
| 198 |
+
return; # Ignore transitive requires
|
| 199 |
+
}
|
| 200 |
+
default {
|
| 201 |
+
__package_orig $what {*}$args
|
| 202 |
+
}
|
| 203 |
+
}
|
| 204 |
+
}
|
| 205 |
+
proc tclPkgUnknown args {}
|
| 206 |
+
package unknown tclPkgUnknown
|
| 207 |
+
|
| 208 |
+
# Stub out the unknown command so package can call into each other
|
| 209 |
+
# during their initialization.
|
| 210 |
+
|
| 211 |
+
proc unknown {args} {}
|
| 212 |
+
|
| 213 |
+
# Stub out the auto_import mechanism
|
| 214 |
+
|
| 215 |
+
proc auto_import {args} {}
|
| 216 |
+
|
| 217 |
+
# reserve the ::tcl namespace for support procs and temporary
|
| 218 |
+
# variables. This might make it awkward to generate a
|
| 219 |
+
# pkgIndex.tcl file for the ::tcl namespace.
|
| 220 |
+
|
| 221 |
+
namespace eval ::tcl {
|
| 222 |
+
variable dir ;# Current directory being processed
|
| 223 |
+
variable file ;# Current file being processed
|
| 224 |
+
variable direct ;# -direct flag value
|
| 225 |
+
variable x ;# Loop variable
|
| 226 |
+
variable debug ;# For debugging
|
| 227 |
+
variable type ;# "load" or "source", for -direct
|
| 228 |
+
variable namespaces ;# Existing namespaces (e.g., ::tcl)
|
| 229 |
+
variable packages ;# Existing packages (e.g., Tcl)
|
| 230 |
+
variable origCmds ;# Existing commands
|
| 231 |
+
variable newCmds ;# Newly created commands
|
| 232 |
+
variable newPkgs {} ;# Newly created packages
|
| 233 |
+
}
|
| 234 |
+
}
|
| 235 |
+
|
| 236 |
+
$c eval [list set ::tcl::dir $dir]
|
| 237 |
+
$c eval [list set ::tcl::file $file]
|
| 238 |
+
$c eval [list set ::tcl::direct $direct]
|
| 239 |
+
|
| 240 |
+
# Download needed procedures into the child because we've just deleted
|
| 241 |
+
# the unknown procedure. This doesn't handle procedures with default
|
| 242 |
+
# arguments.
|
| 243 |
+
|
| 244 |
+
foreach p {::tcl::Pkg::CompareExtension} {
|
| 245 |
+
$c eval [list namespace eval [namespace qualifiers $p] {}]
|
| 246 |
+
$c eval [list proc $p [info args $p] [info body $p]]
|
| 247 |
+
}
|
| 248 |
+
|
| 249 |
+
try {
|
| 250 |
+
$c eval {
|
| 251 |
+
set ::tcl::debug "loading or sourcing"
|
| 252 |
+
|
| 253 |
+
# we need to track command defined by each package even in the
|
| 254 |
+
# -direct case, because they are needed internally by the
|
| 255 |
+
# "partial pkgIndex.tcl" step above.
|
| 256 |
+
|
| 257 |
+
proc ::tcl::GetAllNamespaces {{root ::}} {
|
| 258 |
+
set list $root
|
| 259 |
+
foreach ns [namespace children $root] {
|
| 260 |
+
lappend list {*}[::tcl::GetAllNamespaces $ns]
|
| 261 |
+
}
|
| 262 |
+
return $list
|
| 263 |
+
}
|
| 264 |
+
|
| 265 |
+
# init the list of existing namespaces, packages, commands
|
| 266 |
+
|
| 267 |
+
foreach ::tcl::x [::tcl::GetAllNamespaces] {
|
| 268 |
+
set ::tcl::namespaces($::tcl::x) 1
|
| 269 |
+
}
|
| 270 |
+
foreach ::tcl::x [package names] {
|
| 271 |
+
if {[package provide $::tcl::x] ne ""} {
|
| 272 |
+
set ::tcl::packages($::tcl::x) 1
|
| 273 |
+
}
|
| 274 |
+
}
|
| 275 |
+
set ::tcl::origCmds [info commands]
|
| 276 |
+
|
| 277 |
+
# Try to load the file if it has the shared library extension,
|
| 278 |
+
# otherwise source it. It's important not to try to load
|
| 279 |
+
# files that aren't shared libraries, because on some systems
|
| 280 |
+
# (like SunOS) the loader will abort the whole application
|
| 281 |
+
# when it gets an error.
|
| 282 |
+
|
| 283 |
+
if {[::tcl::Pkg::CompareExtension $::tcl::file [info sharedlibextension]]} {
|
| 284 |
+
# The "file join ." command below is necessary. Without
|
| 285 |
+
# it, if the file name has no \'s and we're on UNIX, the
|
| 286 |
+
# load command will invoke the LD_LIBRARY_PATH search
|
| 287 |
+
# mechanism, which could cause the wrong file to be used.
|
| 288 |
+
|
| 289 |
+
set ::tcl::debug loading
|
| 290 |
+
load [file join $::tcl::dir $::tcl::file]
|
| 291 |
+
set ::tcl::type load
|
| 292 |
+
} else {
|
| 293 |
+
set ::tcl::debug sourcing
|
| 294 |
+
source [file join $::tcl::dir $::tcl::file]
|
| 295 |
+
set ::tcl::type source
|
| 296 |
+
}
|
| 297 |
+
|
| 298 |
+
# As a performance optimization, if we are creating direct
|
| 299 |
+
# load packages, don't bother figuring out the set of commands
|
| 300 |
+
# created by the new packages. We only need that list for
|
| 301 |
+
# setting up the autoloading used in the non-direct case.
|
| 302 |
+
if {!$::tcl::direct} {
|
| 303 |
+
# See what new namespaces appeared, and import commands
|
| 304 |
+
# from them. Only exported commands go into the index.
|
| 305 |
+
|
| 306 |
+
foreach ::tcl::x [::tcl::GetAllNamespaces] {
|
| 307 |
+
if {![info exists ::tcl::namespaces($::tcl::x)]} {
|
| 308 |
+
namespace import -force ${::tcl::x}::*
|
| 309 |
+
}
|
| 310 |
+
|
| 311 |
+
# Figure out what commands appeared
|
| 312 |
+
|
| 313 |
+
foreach ::tcl::x [info commands] {
|
| 314 |
+
set ::tcl::newCmds($::tcl::x) 1
|
| 315 |
+
}
|
| 316 |
+
foreach ::tcl::x $::tcl::origCmds {
|
| 317 |
+
unset -nocomplain ::tcl::newCmds($::tcl::x)
|
| 318 |
+
}
|
| 319 |
+
foreach ::tcl::x [array names ::tcl::newCmds] {
|
| 320 |
+
# determine which namespace a command comes from
|
| 321 |
+
|
| 322 |
+
set ::tcl::abs [namespace origin $::tcl::x]
|
| 323 |
+
|
| 324 |
+
# special case so that global names have no
|
| 325 |
+
# leading ::, this is required by the unknown
|
| 326 |
+
# command
|
| 327 |
+
|
| 328 |
+
set ::tcl::abs \
|
| 329 |
+
[lindex [auto_qualify $::tcl::abs ::] 0]
|
| 330 |
+
|
| 331 |
+
if {$::tcl::x ne $::tcl::abs} {
|
| 332 |
+
# Name changed during qualification
|
| 333 |
+
|
| 334 |
+
set ::tcl::newCmds($::tcl::abs) 1
|
| 335 |
+
unset ::tcl::newCmds($::tcl::x)
|
| 336 |
+
}
|
| 337 |
+
}
|
| 338 |
+
}
|
| 339 |
+
}
|
| 340 |
+
|
| 341 |
+
# Look through the packages that appeared, and if there is a
|
| 342 |
+
# version provided, then record it
|
| 343 |
+
|
| 344 |
+
foreach ::tcl::x [package names] {
|
| 345 |
+
if {[package provide $::tcl::x] ne ""
|
| 346 |
+
&& ![info exists ::tcl::packages($::tcl::x)]} {
|
| 347 |
+
lappend ::tcl::newPkgs \
|
| 348 |
+
[list $::tcl::x [package provide $::tcl::x]]
|
| 349 |
+
}
|
| 350 |
+
}
|
| 351 |
+
}
|
| 352 |
+
} on error msg {
|
| 353 |
+
set what [$c eval set ::tcl::debug]
|
| 354 |
+
if {$doVerbose} {
|
| 355 |
+
tclLog "warning: error while $what $file: $msg"
|
| 356 |
+
}
|
| 357 |
+
} on ok {} {
|
| 358 |
+
set what [$c eval set ::tcl::debug]
|
| 359 |
+
if {$doVerbose} {
|
| 360 |
+
tclLog "successful $what of $file"
|
| 361 |
+
}
|
| 362 |
+
set type [$c eval set ::tcl::type]
|
| 363 |
+
set cmds [lsort [$c eval array names ::tcl::newCmds]]
|
| 364 |
+
set pkgs [$c eval set ::tcl::newPkgs]
|
| 365 |
+
if {$doVerbose} {
|
| 366 |
+
if {!$direct} {
|
| 367 |
+
tclLog "commands provided were $cmds"
|
| 368 |
+
}
|
| 369 |
+
tclLog "packages provided were $pkgs"
|
| 370 |
+
}
|
| 371 |
+
if {[llength $pkgs] > 1} {
|
| 372 |
+
tclLog "warning: \"$file\" provides more than one package ($pkgs)"
|
| 373 |
+
}
|
| 374 |
+
foreach pkg $pkgs {
|
| 375 |
+
# cmds is empty/not used in the direct case
|
| 376 |
+
lappend files($pkg) [list $file $type $cmds]
|
| 377 |
+
}
|
| 378 |
+
|
| 379 |
+
if {$doVerbose} {
|
| 380 |
+
tclLog "processed $file"
|
| 381 |
+
}
|
| 382 |
+
}
|
| 383 |
+
interp delete $c
|
| 384 |
+
}
|
| 385 |
+
|
| 386 |
+
append index "# Tcl package index file, version 1.1\n"
|
| 387 |
+
append index "# This file is generated by the \"pkg_mkIndex$more\" command\n"
|
| 388 |
+
append index "# and sourced either when an application starts up or\n"
|
| 389 |
+
append index "# by a \"package unknown\" script. It invokes the\n"
|
| 390 |
+
append index "# \"package ifneeded\" command to set up package-related\n"
|
| 391 |
+
append index "# information so that packages will be loaded automatically\n"
|
| 392 |
+
append index "# in response to \"package require\" commands. When this\n"
|
| 393 |
+
append index "# script is sourced, the variable \$dir must contain the\n"
|
| 394 |
+
append index "# full path name of this file's directory.\n"
|
| 395 |
+
|
| 396 |
+
foreach pkg [lsort [array names files]] {
|
| 397 |
+
set cmd {}
|
| 398 |
+
lassign $pkg name version
|
| 399 |
+
lappend cmd ::tcl::Pkg::Create -name $name -version $version
|
| 400 |
+
foreach spec [lsort -index 0 $files($pkg)] {
|
| 401 |
+
foreach {file type procs} $spec {
|
| 402 |
+
if {$direct} {
|
| 403 |
+
set procs {}
|
| 404 |
+
}
|
| 405 |
+
lappend cmd "-$type" [list $file $procs]
|
| 406 |
+
}
|
| 407 |
+
}
|
| 408 |
+
append index "\n[eval $cmd]"
|
| 409 |
+
}
|
| 410 |
+
|
| 411 |
+
set f [open [file join $dir pkgIndex.tcl] w]
|
| 412 |
+
puts $f $index
|
| 413 |
+
close $f
|
| 414 |
+
}
|
| 415 |
+
|
| 416 |
+
# tclPkgSetup --
|
| 417 |
+
# This is a utility procedure use by pkgIndex.tcl files. It is invoked as
|
| 418 |
+
# part of a "package ifneeded" script. It calls "package provide" to indicate
|
| 419 |
+
# that a package is available, then sets entries in the auto_index array so
|
| 420 |
+
# that the package's files will be auto-loaded when the commands are used.
|
| 421 |
+
#
|
| 422 |
+
# Arguments:
|
| 423 |
+
# dir - Directory containing all the files for this package.
|
| 424 |
+
# pkg - Name of the package (no version number).
|
| 425 |
+
# version - Version number for the package, such as 2.1.3.
|
| 426 |
+
# files - List of files that constitute the package. Each
|
| 427 |
+
# element is a sub-list with three elements. The first
|
| 428 |
+
# is the name of a file relative to $dir, the second is
|
| 429 |
+
# "load" or "source", indicating whether the file is a
|
| 430 |
+
# loadable binary or a script to source, and the third
|
| 431 |
+
# is a list of commands defined by this file.
|
| 432 |
+
|
| 433 |
+
proc tclPkgSetup {dir pkg version files} {
|
| 434 |
+
global auto_index
|
| 435 |
+
|
| 436 |
+
package provide $pkg $version
|
| 437 |
+
foreach fileInfo $files {
|
| 438 |
+
set f [lindex $fileInfo 0]
|
| 439 |
+
set type [lindex $fileInfo 1]
|
| 440 |
+
foreach cmd [lindex $fileInfo 2] {
|
| 441 |
+
if {$type eq "load"} {
|
| 442 |
+
set auto_index($cmd) [list load [file join $dir $f] $pkg]
|
| 443 |
+
} else {
|
| 444 |
+
set auto_index($cmd) [list source [file join $dir $f]]
|
| 445 |
+
}
|
| 446 |
+
}
|
| 447 |
+
}
|
| 448 |
+
}
|
| 449 |
+
|
| 450 |
+
# tclPkgUnknown --
|
| 451 |
+
# This procedure provides the default for the "package unknown" function. It
|
| 452 |
+
# is invoked when a package that's needed can't be found. It scans the
|
| 453 |
+
# auto_path directories and their immediate children looking for pkgIndex.tcl
|
| 454 |
+
# files and sources any such files that are found to setup the package
|
| 455 |
+
# database. As it searches, it will recognize changes to the auto_path and
|
| 456 |
+
# scan any new directories.
|
| 457 |
+
#
|
| 458 |
+
# Arguments:
|
| 459 |
+
# name - Name of desired package. Not used.
|
| 460 |
+
# version - Version of desired package. Not used.
|
| 461 |
+
# exact - Either "-exact" or omitted. Not used.
|
| 462 |
+
|
| 463 |
+
proc tclPkgUnknown {name args} {
|
| 464 |
+
global auto_path env
|
| 465 |
+
|
| 466 |
+
if {![info exists auto_path]} {
|
| 467 |
+
return
|
| 468 |
+
}
|
| 469 |
+
# Cache the auto_path, because it may change while we run through the
|
| 470 |
+
# first set of pkgIndex.tcl files
|
| 471 |
+
set old_path [set use_path $auto_path]
|
| 472 |
+
while {[llength $use_path]} {
|
| 473 |
+
set dir [lindex $use_path end]
|
| 474 |
+
|
| 475 |
+
# Make sure we only scan each directory one time.
|
| 476 |
+
if {[info exists tclSeenPath($dir)]} {
|
| 477 |
+
set use_path [lrange $use_path 0 end-1]
|
| 478 |
+
continue
|
| 479 |
+
}
|
| 480 |
+
set tclSeenPath($dir) 1
|
| 481 |
+
|
| 482 |
+
# Get the pkgIndex.tcl files in subdirectories of auto_path directories.
|
| 483 |
+
# - Safe Base interpreters have a restricted "glob" command that
|
| 484 |
+
# works in this case.
|
| 485 |
+
# - The "catch" was essential when there was no safe glob and every
|
| 486 |
+
# call in a safe interp failed; it is retained only for corner
|
| 487 |
+
# cases in which the eventual call to glob returns an error.
|
| 488 |
+
catch {
|
| 489 |
+
foreach file [glob -directory $dir -join -nocomplain \
|
| 490 |
+
* pkgIndex.tcl] {
|
| 491 |
+
set dir [file dirname $file]
|
| 492 |
+
if {![info exists procdDirs($dir)]} {
|
| 493 |
+
try {
|
| 494 |
+
source $file
|
| 495 |
+
} trap {POSIX EACCES} {} {
|
| 496 |
+
# $file was not readable; silently ignore
|
| 497 |
+
continue
|
| 498 |
+
} on error msg {
|
| 499 |
+
tclLog "error reading package index file $file: $msg"
|
| 500 |
+
} on ok {} {
|
| 501 |
+
set procdDirs($dir) 1
|
| 502 |
+
}
|
| 503 |
+
}
|
| 504 |
+
}
|
| 505 |
+
}
|
| 506 |
+
set dir [lindex $use_path end]
|
| 507 |
+
if {![info exists procdDirs($dir)]} {
|
| 508 |
+
set file [file join $dir pkgIndex.tcl]
|
| 509 |
+
# safe interps usually don't have "file exists",
|
| 510 |
+
if {([interp issafe] || [file exists $file])} {
|
| 511 |
+
try {
|
| 512 |
+
source $file
|
| 513 |
+
} trap {POSIX EACCES} {} {
|
| 514 |
+
# $file was not readable; silently ignore
|
| 515 |
+
continue
|
| 516 |
+
} on error msg {
|
| 517 |
+
tclLog "error reading package index file $file: $msg"
|
| 518 |
+
} on ok {} {
|
| 519 |
+
set procdDirs($dir) 1
|
| 520 |
+
}
|
| 521 |
+
}
|
| 522 |
+
}
|
| 523 |
+
|
| 524 |
+
set use_path [lrange $use_path 0 end-1]
|
| 525 |
+
|
| 526 |
+
# Check whether any of the index scripts we [source]d above set a new
|
| 527 |
+
# value for $::auto_path. If so, then find any new directories on the
|
| 528 |
+
# $::auto_path, and lappend them to the $use_path we are working from.
|
| 529 |
+
# This gives index scripts the (arguably unwise) power to expand the
|
| 530 |
+
# index script search path while the search is in progress.
|
| 531 |
+
set index 0
|
| 532 |
+
if {[llength $old_path] == [llength $auto_path]} {
|
| 533 |
+
foreach dir $auto_path old $old_path {
|
| 534 |
+
if {$dir ne $old} {
|
| 535 |
+
# This entry in $::auto_path has changed.
|
| 536 |
+
break
|
| 537 |
+
}
|
| 538 |
+
incr index
|
| 539 |
+
}
|
| 540 |
+
}
|
| 541 |
+
|
| 542 |
+
# $index now points to the first element of $auto_path that has
|
| 543 |
+
# changed, or the beginning if $auto_path has changed length Scan the
|
| 544 |
+
# new elements of $auto_path for directories to add to $use_path.
|
| 545 |
+
# Don't add directories we've already seen, or ones already on the
|
| 546 |
+
# $use_path.
|
| 547 |
+
foreach dir [lrange $auto_path $index end] {
|
| 548 |
+
if {![info exists tclSeenPath($dir)] && ($dir ni $use_path)} {
|
| 549 |
+
lappend use_path $dir
|
| 550 |
+
}
|
| 551 |
+
}
|
| 552 |
+
set old_path $auto_path
|
| 553 |
+
}
|
| 554 |
+
}
|
| 555 |
+
|
| 556 |
+
# tcl::MacOSXPkgUnknown --
|
| 557 |
+
# This procedure extends the "package unknown" function for MacOSX. It scans
|
| 558 |
+
# the Resources/Scripts directories of the immediate children of the auto_path
|
| 559 |
+
# directories for pkgIndex files.
|
| 560 |
+
#
|
| 561 |
+
# Arguments:
|
| 562 |
+
# original - original [package unknown] procedure
|
| 563 |
+
# name - Name of desired package. Not used.
|
| 564 |
+
# version - Version of desired package. Not used.
|
| 565 |
+
# exact - Either "-exact" or omitted. Not used.
|
| 566 |
+
|
| 567 |
+
proc tcl::MacOSXPkgUnknown {original name args} {
|
| 568 |
+
# First do the cross-platform default search
|
| 569 |
+
uplevel 1 $original [linsert $args 0 $name]
|
| 570 |
+
|
| 571 |
+
# Now do MacOSX specific searching
|
| 572 |
+
global auto_path
|
| 573 |
+
|
| 574 |
+
if {![info exists auto_path]} {
|
| 575 |
+
return
|
| 576 |
+
}
|
| 577 |
+
# Cache the auto_path, because it may change while we run through the
|
| 578 |
+
# first set of pkgIndex.tcl files
|
| 579 |
+
set old_path [set use_path $auto_path]
|
| 580 |
+
while {[llength $use_path]} {
|
| 581 |
+
set dir [lindex $use_path end]
|
| 582 |
+
|
| 583 |
+
# Make sure we only scan each directory one time.
|
| 584 |
+
if {[info exists tclSeenPath($dir)]} {
|
| 585 |
+
set use_path [lrange $use_path 0 end-1]
|
| 586 |
+
continue
|
| 587 |
+
}
|
| 588 |
+
set tclSeenPath($dir) 1
|
| 589 |
+
|
| 590 |
+
# get the pkgIndex files out of the subdirectories
|
| 591 |
+
# Safe interpreters do not use tcl::MacOSXPkgUnknown - see init.tcl.
|
| 592 |
+
foreach file [glob -directory $dir -join -nocomplain \
|
| 593 |
+
* Resources Scripts pkgIndex.tcl] {
|
| 594 |
+
set dir [file dirname $file]
|
| 595 |
+
if {![info exists procdDirs($dir)]} {
|
| 596 |
+
try {
|
| 597 |
+
source $file
|
| 598 |
+
} trap {POSIX EACCES} {} {
|
| 599 |
+
# $file was not readable; silently ignore
|
| 600 |
+
continue
|
| 601 |
+
} on error msg {
|
| 602 |
+
tclLog "error reading package index file $file: $msg"
|
| 603 |
+
} on ok {} {
|
| 604 |
+
set procdDirs($dir) 1
|
| 605 |
+
}
|
| 606 |
+
}
|
| 607 |
+
}
|
| 608 |
+
set use_path [lrange $use_path 0 end-1]
|
| 609 |
+
|
| 610 |
+
# Check whether any of the index scripts we [source]d above set a new
|
| 611 |
+
# value for $::auto_path. If so, then find any new directories on the
|
| 612 |
+
# $::auto_path, and lappend them to the $use_path we are working from.
|
| 613 |
+
# This gives index scripts the (arguably unwise) power to expand the
|
| 614 |
+
# index script search path while the search is in progress.
|
| 615 |
+
set index 0
|
| 616 |
+
if {[llength $old_path] == [llength $auto_path]} {
|
| 617 |
+
foreach dir $auto_path old $old_path {
|
| 618 |
+
if {$dir ne $old} {
|
| 619 |
+
# This entry in $::auto_path has changed.
|
| 620 |
+
break
|
| 621 |
+
}
|
| 622 |
+
incr index
|
| 623 |
+
}
|
| 624 |
+
}
|
| 625 |
+
|
| 626 |
+
# $index now points to the first element of $auto_path that has
|
| 627 |
+
# changed, or the beginning if $auto_path has changed length Scan the
|
| 628 |
+
# new elements of $auto_path for directories to add to $use_path.
|
| 629 |
+
# Don't add directories we've already seen, or ones already on the
|
| 630 |
+
# $use_path.
|
| 631 |
+
foreach dir [lrange $auto_path $index end] {
|
| 632 |
+
if {![info exists tclSeenPath($dir)] && ($dir ni $use_path)} {
|
| 633 |
+
lappend use_path $dir
|
| 634 |
+
}
|
| 635 |
+
}
|
| 636 |
+
set old_path $auto_path
|
| 637 |
+
}
|
| 638 |
+
}
|
| 639 |
+
|
| 640 |
+
# ::tcl::Pkg::Create --
|
| 641 |
+
#
|
| 642 |
+
# Given a package specification generate a "package ifneeded" statement
|
| 643 |
+
# for the package, suitable for inclusion in a pkgIndex.tcl file.
|
| 644 |
+
#
|
| 645 |
+
# Arguments:
|
| 646 |
+
# args arguments used by the Create function:
|
| 647 |
+
# -name packageName
|
| 648 |
+
# -version packageVersion
|
| 649 |
+
# -load {filename ?{procs}?}
|
| 650 |
+
# ...
|
| 651 |
+
# -source {filename ?{procs}?}
|
| 652 |
+
# ...
|
| 653 |
+
#
|
| 654 |
+
# Any number of -load and -source parameters may be
|
| 655 |
+
# specified, so long as there is at least one -load or
|
| 656 |
+
# -source parameter. If the procs component of a module
|
| 657 |
+
# specifier is left off, that module will be set up for
|
| 658 |
+
# direct loading; otherwise, it will be set up for lazy
|
| 659 |
+
# loading. If both -source and -load are specified, the
|
| 660 |
+
# -load'ed files will be loaded first, followed by the
|
| 661 |
+
# -source'd files.
|
| 662 |
+
#
|
| 663 |
+
# Results:
|
| 664 |
+
# An appropriate "package ifneeded" statement for the package.
|
| 665 |
+
|
| 666 |
+
proc ::tcl::Pkg::Create {args} {
|
| 667 |
+
append err(usage) "[lindex [info level 0] 0] "
|
| 668 |
+
append err(usage) "-name packageName -version packageVersion"
|
| 669 |
+
append err(usage) "?-load {filename ?{procs}?}? ... "
|
| 670 |
+
append err(usage) "?-source {filename ?{procs}?}? ..."
|
| 671 |
+
|
| 672 |
+
set err(wrongNumArgs) "wrong # args: should be \"$err(usage)\""
|
| 673 |
+
set err(valueMissing) "value for \"%s\" missing: should be \"$err(usage)\""
|
| 674 |
+
set err(unknownOpt) "unknown option \"%s\": should be \"$err(usage)\""
|
| 675 |
+
set err(noLoadOrSource) "at least one of -load and -source must be given"
|
| 676 |
+
|
| 677 |
+
# process arguments
|
| 678 |
+
set len [llength $args]
|
| 679 |
+
if {$len < 6} {
|
| 680 |
+
error $err(wrongNumArgs)
|
| 681 |
+
}
|
| 682 |
+
|
| 683 |
+
# Initialize parameters
|
| 684 |
+
array set opts {-name {} -version {} -source {} -load {}}
|
| 685 |
+
|
| 686 |
+
# process parameters
|
| 687 |
+
for {set i 0} {$i < $len} {incr i} {
|
| 688 |
+
set flag [lindex $args $i]
|
| 689 |
+
incr i
|
| 690 |
+
switch -glob -- $flag {
|
| 691 |
+
"-name" -
|
| 692 |
+
"-version" {
|
| 693 |
+
if {$i >= $len} {
|
| 694 |
+
error [format $err(valueMissing) $flag]
|
| 695 |
+
}
|
| 696 |
+
set opts($flag) [lindex $args $i]
|
| 697 |
+
}
|
| 698 |
+
"-source" -
|
| 699 |
+
"-load" {
|
| 700 |
+
if {$i >= $len} {
|
| 701 |
+
error [format $err(valueMissing) $flag]
|
| 702 |
+
}
|
| 703 |
+
lappend opts($flag) [lindex $args $i]
|
| 704 |
+
}
|
| 705 |
+
default {
|
| 706 |
+
error [format $err(unknownOpt) [lindex $args $i]]
|
| 707 |
+
}
|
| 708 |
+
}
|
| 709 |
+
}
|
| 710 |
+
|
| 711 |
+
# Validate the parameters
|
| 712 |
+
if {![llength $opts(-name)]} {
|
| 713 |
+
error [format $err(valueMissing) "-name"]
|
| 714 |
+
}
|
| 715 |
+
if {![llength $opts(-version)]} {
|
| 716 |
+
error [format $err(valueMissing) "-version"]
|
| 717 |
+
}
|
| 718 |
+
|
| 719 |
+
if {!([llength $opts(-source)] || [llength $opts(-load)])} {
|
| 720 |
+
error $err(noLoadOrSource)
|
| 721 |
+
}
|
| 722 |
+
|
| 723 |
+
# OK, now everything is good. Generate the package ifneeded statement.
|
| 724 |
+
set cmdline "package ifneeded $opts(-name) $opts(-version) "
|
| 725 |
+
|
| 726 |
+
set cmdList {}
|
| 727 |
+
set lazyFileList {}
|
| 728 |
+
|
| 729 |
+
# Handle -load and -source specs
|
| 730 |
+
foreach key {load source} {
|
| 731 |
+
foreach filespec $opts(-$key) {
|
| 732 |
+
lassign $filespec filename proclist
|
| 733 |
+
|
| 734 |
+
if { [llength $proclist] == 0 } {
|
| 735 |
+
set cmd "\[list $key \[file join \$dir [list $filename]\]\]"
|
| 736 |
+
lappend cmdList $cmd
|
| 737 |
+
} else {
|
| 738 |
+
lappend lazyFileList [list $filename $key $proclist]
|
| 739 |
+
}
|
| 740 |
+
}
|
| 741 |
+
}
|
| 742 |
+
|
| 743 |
+
if {[llength $lazyFileList]} {
|
| 744 |
+
lappend cmdList "\[list tclPkgSetup \$dir $opts(-name)\
|
| 745 |
+
$opts(-version) [list $lazyFileList]\]"
|
| 746 |
+
}
|
| 747 |
+
append cmdline [join $cmdList "\\n"]
|
| 748 |
+
return $cmdline
|
| 749 |
+
}
|
| 750 |
+
|
| 751 |
+
interp alias {} ::pkg::create {} ::tcl::Pkg::Create
|
lib/tcl8.6/parray.tcl
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# parray:
|
| 2 |
+
# Print the contents of a global array on stdout.
|
| 3 |
+
#
|
| 4 |
+
# Copyright (c) 1991-1993 The Regents of the University of California.
|
| 5 |
+
# Copyright (c) 1994 Sun Microsystems, Inc.
|
| 6 |
+
#
|
| 7 |
+
# See the file "license.terms" for information on usage and redistribution
|
| 8 |
+
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
| 9 |
+
#
|
| 10 |
+
|
| 11 |
+
proc parray {a {pattern *}} {
|
| 12 |
+
upvar 1 $a array
|
| 13 |
+
if {![array exists array]} {
|
| 14 |
+
return -code error "\"$a\" isn't an array"
|
| 15 |
+
}
|
| 16 |
+
set maxl 0
|
| 17 |
+
set names [lsort [array names array $pattern]]
|
| 18 |
+
foreach name $names {
|
| 19 |
+
if {[string length $name] > $maxl} {
|
| 20 |
+
set maxl [string length $name]
|
| 21 |
+
}
|
| 22 |
+
}
|
| 23 |
+
set maxl [expr {$maxl + [string length $a] + 2}]
|
| 24 |
+
foreach name $names {
|
| 25 |
+
set nameString [format %s(%s) $a $name]
|
| 26 |
+
puts stdout [format "%-*s = %s" $maxl $nameString $array($name)]
|
| 27 |
+
}
|
| 28 |
+
}
|
lib/tcl8.6/safe.tcl
ADDED
|
@@ -0,0 +1,1289 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# safe.tcl --
|
| 2 |
+
#
|
| 3 |
+
# This file provide a safe loading/sourcing mechanism for safe interpreters.
|
| 4 |
+
# It implements a virtual path mechanism to hide the real pathnames from the
|
| 5 |
+
# child. It runs in a parent interpreter and sets up data structure and
|
| 6 |
+
# aliases that will be invoked when used from a child interpreter.
|
| 7 |
+
#
|
| 8 |
+
# See the safe.n man page for details.
|
| 9 |
+
#
|
| 10 |
+
# Copyright (c) 1996-1997 Sun Microsystems, Inc.
|
| 11 |
+
#
|
| 12 |
+
# See the file "license.terms" for information on usage and redistribution of
|
| 13 |
+
# this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
| 14 |
+
|
| 15 |
+
#
|
| 16 |
+
# The implementation is based on namespaces. These naming conventions are
|
| 17 |
+
# followed:
|
| 18 |
+
# Private procs starts with uppercase.
|
| 19 |
+
# Public procs are exported and starts with lowercase
|
| 20 |
+
#
|
| 21 |
+
|
| 22 |
+
# Needed utilities package
|
| 23 |
+
package require opt 0.4.8
|
| 24 |
+
|
| 25 |
+
# Create the safe namespace
|
| 26 |
+
namespace eval ::safe {
|
| 27 |
+
# Exported API:
|
| 28 |
+
namespace export interpCreate interpInit interpConfigure interpDelete \
|
| 29 |
+
interpAddToAccessPath interpFindInAccessPath setLogCmd
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
# Helper function to resolve the dual way of specifying staticsok (either
|
| 33 |
+
# by -noStatics or -statics 0)
|
| 34 |
+
proc ::safe::InterpStatics {} {
|
| 35 |
+
foreach v {Args statics noStatics} {
|
| 36 |
+
upvar $v $v
|
| 37 |
+
}
|
| 38 |
+
set flag [::tcl::OptProcArgGiven -noStatics]
|
| 39 |
+
if {$flag && (!$noStatics == !$statics)
|
| 40 |
+
&& ([::tcl::OptProcArgGiven -statics])} {
|
| 41 |
+
return -code error\
|
| 42 |
+
"conflicting values given for -statics and -noStatics"
|
| 43 |
+
}
|
| 44 |
+
if {$flag} {
|
| 45 |
+
return [expr {!$noStatics}]
|
| 46 |
+
} else {
|
| 47 |
+
return $statics
|
| 48 |
+
}
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
# Helper function to resolve the dual way of specifying nested loading
|
| 52 |
+
# (either by -nestedLoadOk or -nested 1)
|
| 53 |
+
proc ::safe::InterpNested {} {
|
| 54 |
+
foreach v {Args nested nestedLoadOk} {
|
| 55 |
+
upvar $v $v
|
| 56 |
+
}
|
| 57 |
+
set flag [::tcl::OptProcArgGiven -nestedLoadOk]
|
| 58 |
+
# note that the test here is the opposite of the "InterpStatics" one
|
| 59 |
+
# (it is not -noNested... because of the wanted default value)
|
| 60 |
+
if {$flag && (!$nestedLoadOk != !$nested)
|
| 61 |
+
&& ([::tcl::OptProcArgGiven -nested])} {
|
| 62 |
+
return -code error\
|
| 63 |
+
"conflicting values given for -nested and -nestedLoadOk"
|
| 64 |
+
}
|
| 65 |
+
if {$flag} {
|
| 66 |
+
# another difference with "InterpStatics"
|
| 67 |
+
return $nestedLoadOk
|
| 68 |
+
} else {
|
| 69 |
+
return $nested
|
| 70 |
+
}
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
####
|
| 74 |
+
#
|
| 75 |
+
# API entry points that needs argument parsing :
|
| 76 |
+
#
|
| 77 |
+
####
|
| 78 |
+
|
| 79 |
+
# Interface/entry point function and front end for "Create"
|
| 80 |
+
proc ::safe::interpCreate {args} {
|
| 81 |
+
set Args [::tcl::OptKeyParse ::safe::interpCreate $args]
|
| 82 |
+
RejectExcessColons $slave
|
| 83 |
+
InterpCreate $slave $accessPath \
|
| 84 |
+
[InterpStatics] [InterpNested] $deleteHook
|
| 85 |
+
}
|
| 86 |
+
|
| 87 |
+
proc ::safe::interpInit {args} {
|
| 88 |
+
set Args [::tcl::OptKeyParse ::safe::interpIC $args]
|
| 89 |
+
if {![::interp exists $slave]} {
|
| 90 |
+
return -code error "\"$slave\" is not an interpreter"
|
| 91 |
+
}
|
| 92 |
+
RejectExcessColons $slave
|
| 93 |
+
InterpInit $slave $accessPath \
|
| 94 |
+
[InterpStatics] [InterpNested] $deleteHook
|
| 95 |
+
}
|
| 96 |
+
|
| 97 |
+
# Check that the given child is "one of us"
|
| 98 |
+
proc ::safe::CheckInterp {child} {
|
| 99 |
+
namespace upvar ::safe [VarName $child] state
|
| 100 |
+
if {![info exists state] || ![::interp exists $child]} {
|
| 101 |
+
return -code error \
|
| 102 |
+
"\"$child\" is not an interpreter managed by ::safe::"
|
| 103 |
+
}
|
| 104 |
+
}
|
| 105 |
+
|
| 106 |
+
# Interface/entry point function and front end for "Configure". This code
|
| 107 |
+
# is awfully pedestrian because it would need more coupling and support
|
| 108 |
+
# between the way we store the configuration values in safe::interp's and
|
| 109 |
+
# the Opt package. Obviously we would like an OptConfigure to avoid
|
| 110 |
+
# duplicating all this code everywhere.
|
| 111 |
+
# -> TODO (the app should share or access easily the program/value stored
|
| 112 |
+
# by opt)
|
| 113 |
+
|
| 114 |
+
# This is even more complicated by the boolean flags with no values that
|
| 115 |
+
# we had the bad idea to support for the sake of user simplicity in
|
| 116 |
+
# create/init but which makes life hard in configure...
|
| 117 |
+
# So this will be hopefully written and some integrated with opt1.0
|
| 118 |
+
# (hopefully for tcl8.1 ?)
|
| 119 |
+
proc ::safe::interpConfigure {args} {
|
| 120 |
+
switch [llength $args] {
|
| 121 |
+
1 {
|
| 122 |
+
# If we have exactly 1 argument the semantic is to return all
|
| 123 |
+
# the current configuration. We still call OptKeyParse though
|
| 124 |
+
# we know that "child" is our given argument because it also
|
| 125 |
+
# checks for the "-help" option.
|
| 126 |
+
set Args [::tcl::OptKeyParse ::safe::interpIC $args]
|
| 127 |
+
CheckInterp $slave
|
| 128 |
+
namespace upvar ::safe [VarName $slave] state
|
| 129 |
+
|
| 130 |
+
return [join [list \
|
| 131 |
+
[list -accessPath $state(access_path)] \
|
| 132 |
+
[list -statics $state(staticsok)] \
|
| 133 |
+
[list -nested $state(nestedok)] \
|
| 134 |
+
[list -deleteHook $state(cleanupHook)]]]
|
| 135 |
+
}
|
| 136 |
+
2 {
|
| 137 |
+
# If we have exactly 2 arguments the semantic is a "configure
|
| 138 |
+
# get"
|
| 139 |
+
lassign $args slave arg
|
| 140 |
+
|
| 141 |
+
# get the flag sub program (we 'know' about Opt's internal
|
| 142 |
+
# representation of data)
|
| 143 |
+
set desc [lindex [::tcl::OptKeyGetDesc ::safe::interpIC] 2]
|
| 144 |
+
set hits [::tcl::OptHits desc $arg]
|
| 145 |
+
if {$hits > 1} {
|
| 146 |
+
return -code error [::tcl::OptAmbigous $desc $arg]
|
| 147 |
+
} elseif {$hits == 0} {
|
| 148 |
+
return -code error [::tcl::OptFlagUsage $desc $arg]
|
| 149 |
+
}
|
| 150 |
+
CheckInterp $slave
|
| 151 |
+
namespace upvar ::safe [VarName $slave] state
|
| 152 |
+
|
| 153 |
+
set item [::tcl::OptCurDesc $desc]
|
| 154 |
+
set name [::tcl::OptName $item]
|
| 155 |
+
switch -exact -- $name {
|
| 156 |
+
-accessPath {
|
| 157 |
+
return [list -accessPath $state(access_path)]
|
| 158 |
+
}
|
| 159 |
+
-statics {
|
| 160 |
+
return [list -statics $state(staticsok)]
|
| 161 |
+
}
|
| 162 |
+
-nested {
|
| 163 |
+
return [list -nested $state(nestedok)]
|
| 164 |
+
}
|
| 165 |
+
-deleteHook {
|
| 166 |
+
return [list -deleteHook $state(cleanupHook)]
|
| 167 |
+
}
|
| 168 |
+
-noStatics {
|
| 169 |
+
# it is most probably a set in fact but we would need
|
| 170 |
+
# then to jump to the set part and it is not *sure*
|
| 171 |
+
# that it is a set action that the user want, so force
|
| 172 |
+
# it to use the unambiguous -statics ?value? instead:
|
| 173 |
+
return -code error\
|
| 174 |
+
"ambigous query (get or set -noStatics ?)\
|
| 175 |
+
use -statics instead"
|
| 176 |
+
}
|
| 177 |
+
-nestedLoadOk {
|
| 178 |
+
return -code error\
|
| 179 |
+
"ambigous query (get or set -nestedLoadOk ?)\
|
| 180 |
+
use -nested instead"
|
| 181 |
+
}
|
| 182 |
+
default {
|
| 183 |
+
return -code error "unknown flag $name (bug)"
|
| 184 |
+
}
|
| 185 |
+
}
|
| 186 |
+
}
|
| 187 |
+
default {
|
| 188 |
+
# Otherwise we want to parse the arguments like init and
|
| 189 |
+
# create did
|
| 190 |
+
set Args [::tcl::OptKeyParse ::safe::interpIC $args]
|
| 191 |
+
CheckInterp $slave
|
| 192 |
+
namespace upvar ::safe [VarName $slave] state
|
| 193 |
+
|
| 194 |
+
# Get the current (and not the default) values of whatever has
|
| 195 |
+
# not been given:
|
| 196 |
+
if {![::tcl::OptProcArgGiven -accessPath]} {
|
| 197 |
+
set doreset 0
|
| 198 |
+
set accessPath $state(access_path)
|
| 199 |
+
} else {
|
| 200 |
+
set doreset 1
|
| 201 |
+
}
|
| 202 |
+
if {
|
| 203 |
+
![::tcl::OptProcArgGiven -statics]
|
| 204 |
+
&& ![::tcl::OptProcArgGiven -noStatics]
|
| 205 |
+
} then {
|
| 206 |
+
set statics $state(staticsok)
|
| 207 |
+
} else {
|
| 208 |
+
set statics [InterpStatics]
|
| 209 |
+
}
|
| 210 |
+
if {
|
| 211 |
+
[::tcl::OptProcArgGiven -nested] ||
|
| 212 |
+
[::tcl::OptProcArgGiven -nestedLoadOk]
|
| 213 |
+
} then {
|
| 214 |
+
set nested [InterpNested]
|
| 215 |
+
} else {
|
| 216 |
+
set nested $state(nestedok)
|
| 217 |
+
}
|
| 218 |
+
if {![::tcl::OptProcArgGiven -deleteHook]} {
|
| 219 |
+
set deleteHook $state(cleanupHook)
|
| 220 |
+
}
|
| 221 |
+
# we can now reconfigure :
|
| 222 |
+
InterpSetConfig $slave $accessPath $statics $nested $deleteHook
|
| 223 |
+
# auto_reset the child (to completely synch the new access_path)
|
| 224 |
+
if {$doreset} {
|
| 225 |
+
if {[catch {::interp eval $slave {auto_reset}} msg]} {
|
| 226 |
+
Log $slave "auto_reset failed: $msg"
|
| 227 |
+
} else {
|
| 228 |
+
Log $slave "successful auto_reset" NOTICE
|
| 229 |
+
}
|
| 230 |
+
|
| 231 |
+
# Sync the paths used to search for Tcl modules.
|
| 232 |
+
::interp eval $slave {tcl::tm::path remove {*}[tcl::tm::list]}
|
| 233 |
+
if {[llength $state(tm_path_slave)] > 0} {
|
| 234 |
+
::interp eval $slave [list \
|
| 235 |
+
::tcl::tm::add {*}[lreverse $state(tm_path_slave)]]
|
| 236 |
+
}
|
| 237 |
+
|
| 238 |
+
# Remove stale "package ifneeded" data for non-loaded packages.
|
| 239 |
+
# - Not for loaded packages, because "package forget" erases
|
| 240 |
+
# data from "package provide" as well as "package ifneeded".
|
| 241 |
+
# - This is OK because the script cannot reload any version of
|
| 242 |
+
# the package unless it first does "package forget".
|
| 243 |
+
foreach pkg [::interp eval $slave {package names}] {
|
| 244 |
+
if {[::interp eval $slave [list package provide $pkg]] eq ""} {
|
| 245 |
+
::interp eval $slave [list package forget $pkg]
|
| 246 |
+
}
|
| 247 |
+
}
|
| 248 |
+
}
|
| 249 |
+
return
|
| 250 |
+
}
|
| 251 |
+
}
|
| 252 |
+
}
|
| 253 |
+
|
| 254 |
+
####
|
| 255 |
+
#
|
| 256 |
+
# Functions that actually implements the exported APIs
|
| 257 |
+
#
|
| 258 |
+
####
|
| 259 |
+
|
| 260 |
+
#
|
| 261 |
+
# safe::InterpCreate : doing the real job
|
| 262 |
+
#
|
| 263 |
+
# This procedure creates a safe interpreter and initializes it with the safe
|
| 264 |
+
# base aliases.
|
| 265 |
+
# NB: child name must be simple alphanumeric string, no spaces, no (), no
|
| 266 |
+
# {},... {because the state array is stored as part of the name}
|
| 267 |
+
#
|
| 268 |
+
# Returns the child name.
|
| 269 |
+
#
|
| 270 |
+
# Optional Arguments :
|
| 271 |
+
# + child name : if empty, generated name will be used
|
| 272 |
+
# + access_path: path list controlling where load/source can occur,
|
| 273 |
+
# if empty: the parent auto_path will be used.
|
| 274 |
+
# + staticsok : flag, if 0 :no static package can be loaded (load {} Xxx)
|
| 275 |
+
# if 1 :static packages are ok.
|
| 276 |
+
# + nestedok: flag, if 0 :no loading to sub-sub interps (load xx xx sub)
|
| 277 |
+
# if 1 : multiple levels are ok.
|
| 278 |
+
|
| 279 |
+
# use the full name and no indent so auto_mkIndex can find us
|
| 280 |
+
proc ::safe::InterpCreate {
|
| 281 |
+
child
|
| 282 |
+
access_path
|
| 283 |
+
staticsok
|
| 284 |
+
nestedok
|
| 285 |
+
deletehook
|
| 286 |
+
} {
|
| 287 |
+
# Create the child.
|
| 288 |
+
# If evaluated in ::safe, the interpreter command for foo is ::foo;
|
| 289 |
+
# but for foo::bar is safe::foo::bar. So evaluate in :: instead.
|
| 290 |
+
if {$child ne ""} {
|
| 291 |
+
namespace eval :: [list ::interp create -safe $child]
|
| 292 |
+
} else {
|
| 293 |
+
# empty argument: generate child name
|
| 294 |
+
set child [::interp create -safe]
|
| 295 |
+
}
|
| 296 |
+
Log $child "Created" NOTICE
|
| 297 |
+
|
| 298 |
+
# Initialize it. (returns child name)
|
| 299 |
+
InterpInit $child $access_path $staticsok $nestedok $deletehook
|
| 300 |
+
}
|
| 301 |
+
|
| 302 |
+
#
|
| 303 |
+
# InterpSetConfig (was setAccessPath) :
|
| 304 |
+
# Sets up child virtual auto_path and corresponding structure within
|
| 305 |
+
# the parent. Also sets the tcl_library in the child to be the first
|
| 306 |
+
# directory in the path.
|
| 307 |
+
# NB: If you change the path after the child has been initialized you
|
| 308 |
+
# probably need to call "auto_reset" in the child in order that it gets
|
| 309 |
+
# the right auto_index() array values.
|
| 310 |
+
|
| 311 |
+
proc ::safe::InterpSetConfig {child access_path staticsok nestedok deletehook} {
|
| 312 |
+
global auto_path
|
| 313 |
+
|
| 314 |
+
# determine and store the access path if empty
|
| 315 |
+
if {$access_path eq ""} {
|
| 316 |
+
set access_path $auto_path
|
| 317 |
+
|
| 318 |
+
# Make sure that tcl_library is in auto_path and at the first
|
| 319 |
+
# position (needed by setAccessPath)
|
| 320 |
+
set where [lsearch -exact $access_path [info library]]
|
| 321 |
+
if {$where < 0} {
|
| 322 |
+
# not found, add it.
|
| 323 |
+
set access_path [linsert $access_path 0 [info library]]
|
| 324 |
+
Log $child "tcl_library was not in auto_path,\
|
| 325 |
+
added it to slave's access_path" NOTICE
|
| 326 |
+
} elseif {$where != 0} {
|
| 327 |
+
# not first, move it first
|
| 328 |
+
set access_path [linsert \
|
| 329 |
+
[lreplace $access_path $where $where] \
|
| 330 |
+
0 [info library]]
|
| 331 |
+
Log $child "tcl_libray was not in first in auto_path,\
|
| 332 |
+
moved it to front of slave's access_path" NOTICE
|
| 333 |
+
}
|
| 334 |
+
|
| 335 |
+
# Add 1st level subdirs (will searched by auto loading from tcl
|
| 336 |
+
# code in the child using glob and thus fail, so we add them here
|
| 337 |
+
# so by default it works the same).
|
| 338 |
+
set access_path [AddSubDirs $access_path]
|
| 339 |
+
}
|
| 340 |
+
|
| 341 |
+
Log $child "Setting accessPath=($access_path) staticsok=$staticsok\
|
| 342 |
+
nestedok=$nestedok deletehook=($deletehook)" NOTICE
|
| 343 |
+
|
| 344 |
+
namespace upvar ::safe [VarName $child] state
|
| 345 |
+
|
| 346 |
+
# clear old autopath if it existed
|
| 347 |
+
# build new one
|
| 348 |
+
# Extend the access list with the paths used to look for Tcl Modules.
|
| 349 |
+
# We save the virtual form separately as well, as syncing it with the
|
| 350 |
+
# child has to be deferred until the necessary commands are present for
|
| 351 |
+
# setup.
|
| 352 |
+
|
| 353 |
+
set norm_access_path {}
|
| 354 |
+
set slave_access_path {}
|
| 355 |
+
set map_access_path {}
|
| 356 |
+
set remap_access_path {}
|
| 357 |
+
set slave_tm_path {}
|
| 358 |
+
|
| 359 |
+
set i 0
|
| 360 |
+
foreach dir $access_path {
|
| 361 |
+
set token [PathToken $i]
|
| 362 |
+
lappend slave_access_path $token
|
| 363 |
+
lappend map_access_path $token $dir
|
| 364 |
+
lappend remap_access_path $dir $token
|
| 365 |
+
lappend norm_access_path [file normalize $dir]
|
| 366 |
+
incr i
|
| 367 |
+
}
|
| 368 |
+
|
| 369 |
+
set morepaths [::tcl::tm::list]
|
| 370 |
+
set firstpass 1
|
| 371 |
+
while {[llength $morepaths]} {
|
| 372 |
+
set addpaths $morepaths
|
| 373 |
+
set morepaths {}
|
| 374 |
+
|
| 375 |
+
foreach dir $addpaths {
|
| 376 |
+
# Prevent the addition of dirs on the tm list to the
|
| 377 |
+
# result if they are already known.
|
| 378 |
+
if {[dict exists $remap_access_path $dir]} {
|
| 379 |
+
if {$firstpass} {
|
| 380 |
+
# $dir is in [::tcl::tm::list] and belongs in the slave_tm_path.
|
| 381 |
+
# Later passes handle subdirectories, which belong in the
|
| 382 |
+
# access path but not in the module path.
|
| 383 |
+
lappend slave_tm_path [dict get $remap_access_path $dir]
|
| 384 |
+
}
|
| 385 |
+
continue
|
| 386 |
+
}
|
| 387 |
+
|
| 388 |
+
set token [PathToken $i]
|
| 389 |
+
lappend access_path $dir
|
| 390 |
+
lappend slave_access_path $token
|
| 391 |
+
lappend map_access_path $token $dir
|
| 392 |
+
lappend remap_access_path $dir $token
|
| 393 |
+
lappend norm_access_path [file normalize $dir]
|
| 394 |
+
if {$firstpass} {
|
| 395 |
+
# $dir is in [::tcl::tm::list] and belongs in the slave_tm_path.
|
| 396 |
+
# Later passes handle subdirectories, which belong in the
|
| 397 |
+
# access path but not in the module path.
|
| 398 |
+
lappend slave_tm_path $token
|
| 399 |
+
}
|
| 400 |
+
incr i
|
| 401 |
+
|
| 402 |
+
# [Bug 2854929]
|
| 403 |
+
# Recursively find deeper paths which may contain
|
| 404 |
+
# modules. Required to handle modules with names like
|
| 405 |
+
# 'platform::shell', which translate into
|
| 406 |
+
# 'platform/shell-X.tm', i.e arbitrarily deep
|
| 407 |
+
# subdirectories.
|
| 408 |
+
lappend morepaths {*}[glob -nocomplain -directory $dir -type d *]
|
| 409 |
+
}
|
| 410 |
+
set firstpass 0
|
| 411 |
+
}
|
| 412 |
+
|
| 413 |
+
set state(access_path) $access_path
|
| 414 |
+
set state(access_path,map) $map_access_path
|
| 415 |
+
set state(access_path,remap) $remap_access_path
|
| 416 |
+
set state(access_path,norm) $norm_access_path
|
| 417 |
+
set state(access_path,slave) $slave_access_path
|
| 418 |
+
set state(tm_path_slave) $slave_tm_path
|
| 419 |
+
set state(staticsok) $staticsok
|
| 420 |
+
set state(nestedok) $nestedok
|
| 421 |
+
set state(cleanupHook) $deletehook
|
| 422 |
+
|
| 423 |
+
SyncAccessPath $child
|
| 424 |
+
return
|
| 425 |
+
}
|
| 426 |
+
|
| 427 |
+
#
|
| 428 |
+
#
|
| 429 |
+
# FindInAccessPath:
|
| 430 |
+
# Search for a real directory and returns its virtual Id (including the
|
| 431 |
+
# "$")
|
| 432 |
+
proc ::safe::interpFindInAccessPath {child path} {
|
| 433 |
+
CheckInterp $child
|
| 434 |
+
namespace upvar ::safe [VarName $child] state
|
| 435 |
+
|
| 436 |
+
if {![dict exists $state(access_path,remap) $path]} {
|
| 437 |
+
return -code error "$path not found in access path"
|
| 438 |
+
}
|
| 439 |
+
|
| 440 |
+
return [dict get $state(access_path,remap) $path]
|
| 441 |
+
}
|
| 442 |
+
|
| 443 |
+
#
|
| 444 |
+
# addToAccessPath:
|
| 445 |
+
# add (if needed) a real directory to access path and return its
|
| 446 |
+
# virtual token (including the "$").
|
| 447 |
+
proc ::safe::interpAddToAccessPath {child path} {
|
| 448 |
+
# first check if the directory is already in there
|
| 449 |
+
# (inlined interpFindInAccessPath).
|
| 450 |
+
CheckInterp $child
|
| 451 |
+
namespace upvar ::safe [VarName $child] state
|
| 452 |
+
|
| 453 |
+
if {[dict exists $state(access_path,remap) $path]} {
|
| 454 |
+
return [dict get $state(access_path,remap) $path]
|
| 455 |
+
}
|
| 456 |
+
|
| 457 |
+
# new one, add it:
|
| 458 |
+
set token [PathToken [llength $state(access_path)]]
|
| 459 |
+
|
| 460 |
+
lappend state(access_path) $path
|
| 461 |
+
lappend state(access_path,slave) $token
|
| 462 |
+
lappend state(access_path,map) $token $path
|
| 463 |
+
lappend state(access_path,remap) $path $token
|
| 464 |
+
lappend state(access_path,norm) [file normalize $path]
|
| 465 |
+
|
| 466 |
+
SyncAccessPath $child
|
| 467 |
+
return $token
|
| 468 |
+
}
|
| 469 |
+
|
| 470 |
+
# This procedure applies the initializations to an already existing
|
| 471 |
+
# interpreter. It is useful when you want to install the safe base aliases
|
| 472 |
+
# into a preexisting safe interpreter.
|
| 473 |
+
proc ::safe::InterpInit {
|
| 474 |
+
child
|
| 475 |
+
access_path
|
| 476 |
+
staticsok
|
| 477 |
+
nestedok
|
| 478 |
+
deletehook
|
| 479 |
+
} {
|
| 480 |
+
# Configure will generate an access_path when access_path is empty.
|
| 481 |
+
InterpSetConfig $child $access_path $staticsok $nestedok $deletehook
|
| 482 |
+
|
| 483 |
+
# NB we need to add [namespace current], aliases are always absolute
|
| 484 |
+
# paths.
|
| 485 |
+
|
| 486 |
+
# These aliases let the child load files to define new commands
|
| 487 |
+
# This alias lets the child use the encoding names, convertfrom,
|
| 488 |
+
# convertto, and system, but not "encoding system <name>" to set the
|
| 489 |
+
# system encoding.
|
| 490 |
+
# Handling Tcl Modules, we need a restricted form of Glob.
|
| 491 |
+
# This alias interposes on the 'exit' command and cleanly terminates
|
| 492 |
+
# the child.
|
| 493 |
+
|
| 494 |
+
foreach {command alias} {
|
| 495 |
+
source AliasSource
|
| 496 |
+
load AliasLoad
|
| 497 |
+
encoding AliasEncoding
|
| 498 |
+
exit interpDelete
|
| 499 |
+
glob AliasGlob
|
| 500 |
+
} {
|
| 501 |
+
::interp alias $child $command {} [namespace current]::$alias $child
|
| 502 |
+
}
|
| 503 |
+
|
| 504 |
+
# This alias lets the child have access to a subset of the 'file'
|
| 505 |
+
# command functionality.
|
| 506 |
+
|
| 507 |
+
::interp expose $child file
|
| 508 |
+
foreach subcommand {dirname extension rootname tail} {
|
| 509 |
+
::interp alias $child ::tcl::file::$subcommand {} \
|
| 510 |
+
::safe::AliasFileSubcommand $child $subcommand
|
| 511 |
+
}
|
| 512 |
+
foreach subcommand {
|
| 513 |
+
atime attributes copy delete executable exists isdirectory isfile
|
| 514 |
+
link lstat mtime mkdir nativename normalize owned readable readlink
|
| 515 |
+
rename size stat tempfile type volumes writable
|
| 516 |
+
} {
|
| 517 |
+
::interp alias $child ::tcl::file::$subcommand {} \
|
| 518 |
+
::safe::BadSubcommand $child file $subcommand
|
| 519 |
+
}
|
| 520 |
+
|
| 521 |
+
# Subcommands of info
|
| 522 |
+
foreach {subcommand alias} {
|
| 523 |
+
nameofexecutable AliasExeName
|
| 524 |
+
} {
|
| 525 |
+
::interp alias $child ::tcl::info::$subcommand \
|
| 526 |
+
{} [namespace current]::$alias $child
|
| 527 |
+
}
|
| 528 |
+
|
| 529 |
+
# The allowed child variables already have been set by Tcl_MakeSafe(3)
|
| 530 |
+
|
| 531 |
+
# Source init.tcl and tm.tcl into the child, to get auto_load and
|
| 532 |
+
# other procedures defined:
|
| 533 |
+
|
| 534 |
+
if {[catch {::interp eval $child {
|
| 535 |
+
source [file join $tcl_library init.tcl]
|
| 536 |
+
}} msg opt]} {
|
| 537 |
+
Log $child "can't source init.tcl ($msg)"
|
| 538 |
+
return -options $opt "can't source init.tcl into slave $child ($msg)"
|
| 539 |
+
}
|
| 540 |
+
|
| 541 |
+
if {[catch {::interp eval $child {
|
| 542 |
+
source [file join $tcl_library tm.tcl]
|
| 543 |
+
}} msg opt]} {
|
| 544 |
+
Log $child "can't source tm.tcl ($msg)"
|
| 545 |
+
return -options $opt "can't source tm.tcl into slave $child ($msg)"
|
| 546 |
+
}
|
| 547 |
+
|
| 548 |
+
# Sync the paths used to search for Tcl modules. This can be done only
|
| 549 |
+
# now, after tm.tcl was loaded.
|
| 550 |
+
namespace upvar ::safe [VarName $child] state
|
| 551 |
+
if {[llength $state(tm_path_slave)] > 0} {
|
| 552 |
+
::interp eval $child [list \
|
| 553 |
+
::tcl::tm::add {*}[lreverse $state(tm_path_slave)]]
|
| 554 |
+
}
|
| 555 |
+
return $child
|
| 556 |
+
}
|
| 557 |
+
|
| 558 |
+
# Add (only if needed, avoid duplicates) 1 level of sub directories to an
|
| 559 |
+
# existing path list. Also removes non directories from the returned
|
| 560 |
+
# list.
|
| 561 |
+
proc ::safe::AddSubDirs {pathList} {
|
| 562 |
+
set res {}
|
| 563 |
+
foreach dir $pathList {
|
| 564 |
+
if {[file isdirectory $dir]} {
|
| 565 |
+
# check that we don't have it yet as a children of a previous
|
| 566 |
+
# dir
|
| 567 |
+
if {$dir ni $res} {
|
| 568 |
+
lappend res $dir
|
| 569 |
+
}
|
| 570 |
+
foreach sub [glob -directory $dir -nocomplain *] {
|
| 571 |
+
if {[file isdirectory $sub] && ($sub ni $res)} {
|
| 572 |
+
# new sub dir, add it !
|
| 573 |
+
lappend res $sub
|
| 574 |
+
}
|
| 575 |
+
}
|
| 576 |
+
}
|
| 577 |
+
}
|
| 578 |
+
return $res
|
| 579 |
+
}
|
| 580 |
+
|
| 581 |
+
# This procedure deletes a safe interpreter managed by Safe Tcl and cleans up
|
| 582 |
+
# associated state.
|
| 583 |
+
# - The command will also delete non-Safe-Base interpreters.
|
| 584 |
+
# - This is regrettable, but to avoid breaking existing code this should be
|
| 585 |
+
# amended at the next major revision by uncommenting "CheckInterp".
|
| 586 |
+
|
| 587 |
+
proc ::safe::interpDelete {child} {
|
| 588 |
+
Log $child "About to delete" NOTICE
|
| 589 |
+
|
| 590 |
+
# CheckInterp $child
|
| 591 |
+
namespace upvar ::safe [VarName $child] state
|
| 592 |
+
|
| 593 |
+
# When an interpreter is deleted with [interp delete], any sub-interpreters
|
| 594 |
+
# are deleted automatically, but this leaves behind their data in the Safe
|
| 595 |
+
# Base. To clean up properly, we call safe::interpDelete recursively on each
|
| 596 |
+
# Safe Base sub-interpreter, so each one is deleted cleanly and not by
|
| 597 |
+
# the automatic mechanism built into [interp delete].
|
| 598 |
+
foreach sub [interp children $child] {
|
| 599 |
+
if {[info exists ::safe::[VarName [list $child $sub]]]} {
|
| 600 |
+
::safe::interpDelete [list $child $sub]
|
| 601 |
+
}
|
| 602 |
+
}
|
| 603 |
+
|
| 604 |
+
# If the child has a cleanup hook registered, call it. Check the
|
| 605 |
+
# existence because we might be called to delete an interp which has
|
| 606 |
+
# not been registered with us at all
|
| 607 |
+
|
| 608 |
+
if {[info exists state(cleanupHook)]} {
|
| 609 |
+
set hook $state(cleanupHook)
|
| 610 |
+
if {[llength $hook]} {
|
| 611 |
+
# remove the hook now, otherwise if the hook calls us somehow,
|
| 612 |
+
# we'll loop
|
| 613 |
+
unset state(cleanupHook)
|
| 614 |
+
try {
|
| 615 |
+
{*}$hook $child
|
| 616 |
+
} on error err {
|
| 617 |
+
Log $child "Delete hook error ($err)"
|
| 618 |
+
}
|
| 619 |
+
}
|
| 620 |
+
}
|
| 621 |
+
|
| 622 |
+
# Discard the global array of state associated with the child, and
|
| 623 |
+
# delete the interpreter.
|
| 624 |
+
|
| 625 |
+
if {[info exists state]} {
|
| 626 |
+
unset state
|
| 627 |
+
}
|
| 628 |
+
|
| 629 |
+
# if we have been called twice, the interp might have been deleted
|
| 630 |
+
# already
|
| 631 |
+
if {[::interp exists $child]} {
|
| 632 |
+
::interp delete $child
|
| 633 |
+
Log $child "Deleted" NOTICE
|
| 634 |
+
}
|
| 635 |
+
|
| 636 |
+
return
|
| 637 |
+
}
|
| 638 |
+
|
| 639 |
+
# Set (or get) the logging mechanism
|
| 640 |
+
|
| 641 |
+
proc ::safe::setLogCmd {args} {
|
| 642 |
+
variable Log
|
| 643 |
+
set la [llength $args]
|
| 644 |
+
if {$la == 0} {
|
| 645 |
+
return $Log
|
| 646 |
+
} elseif {$la == 1} {
|
| 647 |
+
set Log [lindex $args 0]
|
| 648 |
+
} else {
|
| 649 |
+
set Log $args
|
| 650 |
+
}
|
| 651 |
+
|
| 652 |
+
if {$Log eq ""} {
|
| 653 |
+
# Disable logging completely. Calls to it will be compiled out
|
| 654 |
+
# of all users.
|
| 655 |
+
proc ::safe::Log {args} {}
|
| 656 |
+
} else {
|
| 657 |
+
# Activate logging, define proper command.
|
| 658 |
+
|
| 659 |
+
proc ::safe::Log {child msg {type ERROR}} {
|
| 660 |
+
variable Log
|
| 661 |
+
{*}$Log "$type for slave $child : $msg"
|
| 662 |
+
return
|
| 663 |
+
}
|
| 664 |
+
}
|
| 665 |
+
}
|
| 666 |
+
|
| 667 |
+
# ------------------- END OF PUBLIC METHODS ------------
|
| 668 |
+
|
| 669 |
+
#
|
| 670 |
+
# Sets the child auto_path to the parent recorded value. Also sets
|
| 671 |
+
# tcl_library to the first token of the virtual path.
|
| 672 |
+
#
|
| 673 |
+
proc ::safe::SyncAccessPath {child} {
|
| 674 |
+
namespace upvar ::safe [VarName $child] state
|
| 675 |
+
|
| 676 |
+
set slave_access_path $state(access_path,slave)
|
| 677 |
+
::interp eval $child [list set auto_path $slave_access_path]
|
| 678 |
+
|
| 679 |
+
Log $child "auto_path in $child has been set to $slave_access_path"\
|
| 680 |
+
NOTICE
|
| 681 |
+
|
| 682 |
+
# This code assumes that info library is the first element in the
|
| 683 |
+
# list of auto_path's. See -> InterpSetConfig for the code which
|
| 684 |
+
# ensures this condition.
|
| 685 |
+
|
| 686 |
+
::interp eval $child [list \
|
| 687 |
+
set tcl_library [lindex $slave_access_path 0]]
|
| 688 |
+
}
|
| 689 |
+
|
| 690 |
+
# Returns the virtual token for directory number N.
|
| 691 |
+
proc ::safe::PathToken {n} {
|
| 692 |
+
# We need to have a ":" in the token string so [file join] on the
|
| 693 |
+
# mac won't turn it into a relative path.
|
| 694 |
+
return "\$p(:$n:)" ;# Form tested by case 7.2
|
| 695 |
+
}
|
| 696 |
+
|
| 697 |
+
#
|
| 698 |
+
# translate virtual path into real path
|
| 699 |
+
#
|
| 700 |
+
proc ::safe::TranslatePath {child path} {
|
| 701 |
+
namespace upvar ::safe [VarName $child] state
|
| 702 |
+
|
| 703 |
+
# somehow strip the namespaces 'functionality' out (the danger is that
|
| 704 |
+
# we would strip valid macintosh "../" queries... :
|
| 705 |
+
if {[string match "*::*" $path] || [string match "*..*" $path]} {
|
| 706 |
+
return -code error "invalid characters in path $path"
|
| 707 |
+
}
|
| 708 |
+
|
| 709 |
+
# Use a cached map instead of computed local vars and subst.
|
| 710 |
+
|
| 711 |
+
return [string map $state(access_path,map) $path]
|
| 712 |
+
}
|
| 713 |
+
|
| 714 |
+
# file name control (limit access to files/resources that should be a
|
| 715 |
+
# valid tcl source file)
|
| 716 |
+
proc ::safe::CheckFileName {child file} {
|
| 717 |
+
# This used to limit what can be sourced to ".tcl" and forbid files
|
| 718 |
+
# with more than 1 dot and longer than 14 chars, but I changed that
|
| 719 |
+
# for 8.4 as a safe interp has enough internal protection already to
|
| 720 |
+
# allow sourcing anything. - hobbs
|
| 721 |
+
|
| 722 |
+
if {![file exists $file]} {
|
| 723 |
+
# don't tell the file path
|
| 724 |
+
return -code error "no such file or directory"
|
| 725 |
+
}
|
| 726 |
+
|
| 727 |
+
if {![file readable $file]} {
|
| 728 |
+
# don't tell the file path
|
| 729 |
+
return -code error "not readable"
|
| 730 |
+
}
|
| 731 |
+
}
|
| 732 |
+
|
| 733 |
+
# AliasFileSubcommand handles selected subcommands of [file] in safe
|
| 734 |
+
# interpreters that are *almost* safe. In particular, it just acts to
|
| 735 |
+
# prevent discovery of what home directories exist.
|
| 736 |
+
|
| 737 |
+
proc ::safe::AliasFileSubcommand {child subcommand name} {
|
| 738 |
+
if {[string match ~* $name]} {
|
| 739 |
+
set name ./$name
|
| 740 |
+
}
|
| 741 |
+
tailcall ::interp invokehidden $child tcl:file:$subcommand $name
|
| 742 |
+
}
|
| 743 |
+
|
| 744 |
+
# AliasGlob is the target of the "glob" alias in safe interpreters.
|
| 745 |
+
|
| 746 |
+
proc ::safe::AliasGlob {child args} {
|
| 747 |
+
Log $child "GLOB ! $args" NOTICE
|
| 748 |
+
set cmd {}
|
| 749 |
+
set at 0
|
| 750 |
+
array set got {
|
| 751 |
+
-directory 0
|
| 752 |
+
-nocomplain 0
|
| 753 |
+
-join 0
|
| 754 |
+
-tails 0
|
| 755 |
+
-- 0
|
| 756 |
+
}
|
| 757 |
+
|
| 758 |
+
if {$::tcl_platform(platform) eq "windows"} {
|
| 759 |
+
set dirPartRE {^(.*)[\\/]([^\\/]*)$}
|
| 760 |
+
} else {
|
| 761 |
+
set dirPartRE {^(.*)/([^/]*)$}
|
| 762 |
+
}
|
| 763 |
+
|
| 764 |
+
set dir {}
|
| 765 |
+
set virtualdir {}
|
| 766 |
+
|
| 767 |
+
while {$at < [llength $args]} {
|
| 768 |
+
switch -glob -- [set opt [lindex $args $at]] {
|
| 769 |
+
-nocomplain - -- - -tails {
|
| 770 |
+
lappend cmd $opt
|
| 771 |
+
set got($opt) 1
|
| 772 |
+
incr at
|
| 773 |
+
}
|
| 774 |
+
-join {
|
| 775 |
+
set got($opt) 1
|
| 776 |
+
incr at
|
| 777 |
+
}
|
| 778 |
+
-types - -type {
|
| 779 |
+
lappend cmd -types [lindex $args [incr at]]
|
| 780 |
+
incr at
|
| 781 |
+
}
|
| 782 |
+
-directory {
|
| 783 |
+
if {$got($opt)} {
|
| 784 |
+
return -code error \
|
| 785 |
+
{"-directory" cannot be used with "-path"}
|
| 786 |
+
}
|
| 787 |
+
set got($opt) 1
|
| 788 |
+
set virtualdir [lindex $args [incr at]]
|
| 789 |
+
incr at
|
| 790 |
+
}
|
| 791 |
+
-* {
|
| 792 |
+
Log $child "Safe base rejecting glob option '$opt'"
|
| 793 |
+
return -code error "Safe base rejecting glob option '$opt'"
|
| 794 |
+
}
|
| 795 |
+
default {
|
| 796 |
+
break
|
| 797 |
+
}
|
| 798 |
+
}
|
| 799 |
+
if {$got(--)} break
|
| 800 |
+
}
|
| 801 |
+
|
| 802 |
+
# Get the real path from the virtual one and check that the path is in the
|
| 803 |
+
# access path of that child. Done after basic argument processing so that
|
| 804 |
+
# we know if -nocomplain is set.
|
| 805 |
+
if {$got(-directory)} {
|
| 806 |
+
try {
|
| 807 |
+
set dir [TranslatePath $child $virtualdir]
|
| 808 |
+
DirInAccessPath $child $dir
|
| 809 |
+
} on error msg {
|
| 810 |
+
Log $child $msg
|
| 811 |
+
if {$got(-nocomplain)} return
|
| 812 |
+
return -code error "permission denied"
|
| 813 |
+
}
|
| 814 |
+
if {$got(--)} {
|
| 815 |
+
set cmd [linsert $cmd end-1 -directory $dir]
|
| 816 |
+
} else {
|
| 817 |
+
lappend cmd -directory $dir
|
| 818 |
+
}
|
| 819 |
+
} else {
|
| 820 |
+
# The code after this "if ... else" block would conspire to return with
|
| 821 |
+
# no results in this case, if it were allowed to proceed. Instead,
|
| 822 |
+
# return now and reduce the number of cases to be considered later.
|
| 823 |
+
Log $child {option -directory must be supplied}
|
| 824 |
+
if {$got(-nocomplain)} return
|
| 825 |
+
return -code error "permission denied"
|
| 826 |
+
}
|
| 827 |
+
|
| 828 |
+
# Apply the -join semantics ourselves.
|
| 829 |
+
if {$got(-join)} {
|
| 830 |
+
set args [lreplace $args $at end [join [lrange $args $at end] "/"]]
|
| 831 |
+
}
|
| 832 |
+
|
| 833 |
+
# Process the pattern arguments. If we've done a join there is only one
|
| 834 |
+
# pattern argument.
|
| 835 |
+
|
| 836 |
+
set firstPattern [llength $cmd]
|
| 837 |
+
foreach opt [lrange $args $at end] {
|
| 838 |
+
if {![regexp $dirPartRE $opt -> thedir thefile]} {
|
| 839 |
+
set thedir .
|
| 840 |
+
# The *.tm search comes here.
|
| 841 |
+
}
|
| 842 |
+
# "Special" treatment for (joined) argument {*/pkgIndex.tcl}.
|
| 843 |
+
# Do the expansion of "*" here, and filter out any directories that are
|
| 844 |
+
# not in the access path. The outcome is to lappend to cmd a path of
|
| 845 |
+
# the form $virtualdir/subdir/pkgIndex.tcl for each subdirectory subdir,
|
| 846 |
+
# after removing any subdir that are not in the access path.
|
| 847 |
+
if {($thedir eq "*") && ($thefile eq "pkgIndex.tcl")} {
|
| 848 |
+
set mapped 0
|
| 849 |
+
foreach d [glob -directory [TranslatePath $child $virtualdir] \
|
| 850 |
+
-types d -tails *] {
|
| 851 |
+
catch {
|
| 852 |
+
DirInAccessPath $child \
|
| 853 |
+
[TranslatePath $child [file join $virtualdir $d]]
|
| 854 |
+
lappend cmd [file join $d $thefile]
|
| 855 |
+
set mapped 1
|
| 856 |
+
}
|
| 857 |
+
}
|
| 858 |
+
if {$mapped} continue
|
| 859 |
+
# Don't [continue] if */pkgIndex.tcl has no matches in the access
|
| 860 |
+
# path. The pattern will now receive the same treatment as a
|
| 861 |
+
# "non-special" pattern (and will fail because it includes a "*" in
|
| 862 |
+
# the directory name).
|
| 863 |
+
}
|
| 864 |
+
# Any directory pattern that is not an exact (i.e. non-glob) match to a
|
| 865 |
+
# directory in the access path will be rejected here.
|
| 866 |
+
# - Rejections include any directory pattern that has glob matching
|
| 867 |
+
# patterns "*", "?", backslashes, braces or square brackets, (UNLESS
|
| 868 |
+
# it corresponds to a genuine directory name AND that directory is in
|
| 869 |
+
# the access path).
|
| 870 |
+
# - The only "special matching characters" that remain in patterns for
|
| 871 |
+
# processing by glob are in the filename tail.
|
| 872 |
+
# - [file join $anything ~${foo}] is ~${foo}, which is not an exact
|
| 873 |
+
# match to any directory in the access path. Hence directory patterns
|
| 874 |
+
# that begin with "~" are rejected here. Tests safe-16.[5-8] check
|
| 875 |
+
# that "file join" remains as required and does not expand ~${foo}.
|
| 876 |
+
# - Bug [3529949] relates to unwanted expansion of ~${foo} and this is
|
| 877 |
+
# how the present code avoids the bug. All tests safe-16.* relate.
|
| 878 |
+
try {
|
| 879 |
+
DirInAccessPath $child [TranslatePath $child \
|
| 880 |
+
[file join $virtualdir $thedir]]
|
| 881 |
+
} on error msg {
|
| 882 |
+
Log $child $msg
|
| 883 |
+
if {$got(-nocomplain)} continue
|
| 884 |
+
return -code error "permission denied"
|
| 885 |
+
}
|
| 886 |
+
lappend cmd $opt
|
| 887 |
+
}
|
| 888 |
+
|
| 889 |
+
Log $child "GLOB = $cmd" NOTICE
|
| 890 |
+
|
| 891 |
+
if {$got(-nocomplain) && [llength $cmd] eq $firstPattern} {
|
| 892 |
+
return
|
| 893 |
+
}
|
| 894 |
+
try {
|
| 895 |
+
# >>>>>>>>>> HERE'S THE CALL TO SAFE INTERP GLOB <<<<<<<<<<
|
| 896 |
+
# - Pattern arguments added to cmd have NOT been translated from tokens.
|
| 897 |
+
# Only the virtualdir is translated (to dir).
|
| 898 |
+
# - In the pkgIndex.tcl case, there is no "*" in the pattern arguments,
|
| 899 |
+
# which are a list of names each with tail pkgIndex.tcl. The purpose
|
| 900 |
+
# of the call to glob is to remove the names for which the file does
|
| 901 |
+
# not exist.
|
| 902 |
+
set entries [::interp invokehidden $child glob {*}$cmd]
|
| 903 |
+
} on error msg {
|
| 904 |
+
# This is the only place that a call with -nocomplain and no invalid
|
| 905 |
+
# "dash-options" can return an error.
|
| 906 |
+
Log $child $msg
|
| 907 |
+
return -code error "script error"
|
| 908 |
+
}
|
| 909 |
+
|
| 910 |
+
Log $child "GLOB < $entries" NOTICE
|
| 911 |
+
|
| 912 |
+
# Translate path back to what the child should see.
|
| 913 |
+
set res {}
|
| 914 |
+
set l [string length $dir]
|
| 915 |
+
foreach p $entries {
|
| 916 |
+
if {[string equal -length $l $dir $p]} {
|
| 917 |
+
set p [string replace $p 0 [expr {$l-1}] $virtualdir]
|
| 918 |
+
}
|
| 919 |
+
lappend res $p
|
| 920 |
+
}
|
| 921 |
+
|
| 922 |
+
Log $child "GLOB > $res" NOTICE
|
| 923 |
+
return $res
|
| 924 |
+
}
|
| 925 |
+
|
| 926 |
+
# AliasSource is the target of the "source" alias in safe interpreters.
|
| 927 |
+
|
| 928 |
+
proc ::safe::AliasSource {child args} {
|
| 929 |
+
set argc [llength $args]
|
| 930 |
+
# Extended for handling of Tcl Modules to allow not only "source
|
| 931 |
+
# filename", but "source -encoding E filename" as well.
|
| 932 |
+
if {[lindex $args 0] eq "-encoding"} {
|
| 933 |
+
incr argc -2
|
| 934 |
+
set encoding [lindex $args 1]
|
| 935 |
+
set at 2
|
| 936 |
+
if {$encoding eq "identity"} {
|
| 937 |
+
Log $child "attempt to use the identity encoding"
|
| 938 |
+
return -code error "permission denied"
|
| 939 |
+
}
|
| 940 |
+
} else {
|
| 941 |
+
set at 0
|
| 942 |
+
set encoding {}
|
| 943 |
+
}
|
| 944 |
+
if {$argc != 1} {
|
| 945 |
+
set msg "wrong # args: should be \"source ?-encoding E? fileName\""
|
| 946 |
+
Log $child "$msg ($args)"
|
| 947 |
+
return -code error $msg
|
| 948 |
+
}
|
| 949 |
+
set file [lindex $args $at]
|
| 950 |
+
|
| 951 |
+
# get the real path from the virtual one.
|
| 952 |
+
if {[catch {
|
| 953 |
+
set realfile [TranslatePath $child $file]
|
| 954 |
+
} msg]} {
|
| 955 |
+
Log $child $msg
|
| 956 |
+
return -code error "permission denied"
|
| 957 |
+
}
|
| 958 |
+
|
| 959 |
+
# check that the path is in the access path of that child
|
| 960 |
+
if {[catch {
|
| 961 |
+
FileInAccessPath $child $realfile
|
| 962 |
+
} msg]} {
|
| 963 |
+
Log $child $msg
|
| 964 |
+
return -code error "permission denied"
|
| 965 |
+
}
|
| 966 |
+
|
| 967 |
+
# Check that the filename exists and is readable. If it is not, deliver
|
| 968 |
+
# this -errorcode so that caller in tclPkgUnknown does not write a message
|
| 969 |
+
# to tclLog. Has no effect on other callers of ::source, which are in
|
| 970 |
+
# "package ifneeded" scripts.
|
| 971 |
+
if {[catch {
|
| 972 |
+
CheckFileName $child $realfile
|
| 973 |
+
} msg]} {
|
| 974 |
+
Log $child "$realfile:$msg"
|
| 975 |
+
return -code error -errorcode {POSIX EACCES} $msg
|
| 976 |
+
}
|
| 977 |
+
|
| 978 |
+
# Passed all the tests, lets source it. Note that we do this all manually
|
| 979 |
+
# because we want to control [info script] in the child so information
|
| 980 |
+
# doesn't leak so much. [Bug 2913625]
|
| 981 |
+
set old [::interp eval $child {info script}]
|
| 982 |
+
set replacementMsg "script error"
|
| 983 |
+
set code [catch {
|
| 984 |
+
set f [open $realfile]
|
| 985 |
+
fconfigure $f -eofchar "\x1A {}"
|
| 986 |
+
if {$encoding ne ""} {
|
| 987 |
+
fconfigure $f -encoding $encoding
|
| 988 |
+
}
|
| 989 |
+
set contents [read $f]
|
| 990 |
+
close $f
|
| 991 |
+
::interp eval $child [list info script $file]
|
| 992 |
+
} msg opt]
|
| 993 |
+
if {$code == 0} {
|
| 994 |
+
set code [catch {::interp eval $child $contents} msg opt]
|
| 995 |
+
set replacementMsg $msg
|
| 996 |
+
}
|
| 997 |
+
catch {interp eval $child [list info script $old]}
|
| 998 |
+
# Note that all non-errors are fine result codes from [source], so we must
|
| 999 |
+
# take a little care to do it properly. [Bug 2923613]
|
| 1000 |
+
if {$code == 1} {
|
| 1001 |
+
Log $child $msg
|
| 1002 |
+
return -code error $replacementMsg
|
| 1003 |
+
}
|
| 1004 |
+
return -code $code -options $opt $msg
|
| 1005 |
+
}
|
| 1006 |
+
|
| 1007 |
+
# AliasLoad is the target of the "load" alias in safe interpreters.
|
| 1008 |
+
|
| 1009 |
+
proc ::safe::AliasLoad {child file args} {
|
| 1010 |
+
set argc [llength $args]
|
| 1011 |
+
if {$argc > 2} {
|
| 1012 |
+
set msg "load error: too many arguments"
|
| 1013 |
+
Log $child "$msg ($argc) {$file $args}"
|
| 1014 |
+
return -code error $msg
|
| 1015 |
+
}
|
| 1016 |
+
|
| 1017 |
+
# package name (can be empty if file is not).
|
| 1018 |
+
set package [lindex $args 0]
|
| 1019 |
+
|
| 1020 |
+
namespace upvar ::safe [VarName $child] state
|
| 1021 |
+
|
| 1022 |
+
# Determine where to load. load use a relative interp path and {}
|
| 1023 |
+
# means self, so we can directly and safely use passed arg.
|
| 1024 |
+
set target [lindex $args 1]
|
| 1025 |
+
if {$target ne ""} {
|
| 1026 |
+
# we will try to load into a sub sub interp; check that we want to
|
| 1027 |
+
# authorize that.
|
| 1028 |
+
if {!$state(nestedok)} {
|
| 1029 |
+
Log $child "loading to a sub interp (nestedok)\
|
| 1030 |
+
disabled (trying to load $package to $target)"
|
| 1031 |
+
return -code error "permission denied (nested load)"
|
| 1032 |
+
}
|
| 1033 |
+
}
|
| 1034 |
+
|
| 1035 |
+
# Determine what kind of load is requested
|
| 1036 |
+
if {$file eq ""} {
|
| 1037 |
+
# static package loading
|
| 1038 |
+
if {$package eq ""} {
|
| 1039 |
+
set msg "load error: empty filename and no package name"
|
| 1040 |
+
Log $child $msg
|
| 1041 |
+
return -code error $msg
|
| 1042 |
+
}
|
| 1043 |
+
if {!$state(staticsok)} {
|
| 1044 |
+
Log $child "static packages loading disabled\
|
| 1045 |
+
(trying to load $package to $target)"
|
| 1046 |
+
return -code error "permission denied (static package)"
|
| 1047 |
+
}
|
| 1048 |
+
} else {
|
| 1049 |
+
# file loading
|
| 1050 |
+
|
| 1051 |
+
# get the real path from the virtual one.
|
| 1052 |
+
try {
|
| 1053 |
+
set file [TranslatePath $child $file]
|
| 1054 |
+
} on error msg {
|
| 1055 |
+
Log $child $msg
|
| 1056 |
+
return -code error "permission denied"
|
| 1057 |
+
}
|
| 1058 |
+
|
| 1059 |
+
# check the translated path
|
| 1060 |
+
try {
|
| 1061 |
+
FileInAccessPath $child $file
|
| 1062 |
+
} on error msg {
|
| 1063 |
+
Log $child $msg
|
| 1064 |
+
return -code error "permission denied (path)"
|
| 1065 |
+
}
|
| 1066 |
+
}
|
| 1067 |
+
|
| 1068 |
+
try {
|
| 1069 |
+
return [::interp invokehidden $child load $file $package $target]
|
| 1070 |
+
} on error msg {
|
| 1071 |
+
# Some packages return no error message.
|
| 1072 |
+
set msg0 "load of binary library for package $package failed"
|
| 1073 |
+
if {$msg eq {}} {
|
| 1074 |
+
set msg $msg0
|
| 1075 |
+
} else {
|
| 1076 |
+
set msg "$msg0: $msg"
|
| 1077 |
+
}
|
| 1078 |
+
Log $child $msg
|
| 1079 |
+
return -code error $msg
|
| 1080 |
+
}
|
| 1081 |
+
}
|
| 1082 |
+
|
| 1083 |
+
# FileInAccessPath raises an error if the file is not found in the list of
|
| 1084 |
+
# directories contained in the (parent side recorded) child's access path.
|
| 1085 |
+
|
| 1086 |
+
# the security here relies on "file dirname" answering the proper
|
| 1087 |
+
# result... needs checking ?
|
| 1088 |
+
proc ::safe::FileInAccessPath {child file} {
|
| 1089 |
+
namespace upvar ::safe [VarName $child] state
|
| 1090 |
+
set access_path $state(access_path)
|
| 1091 |
+
|
| 1092 |
+
if {[file isdirectory $file]} {
|
| 1093 |
+
return -code error "\"$file\": is a directory"
|
| 1094 |
+
}
|
| 1095 |
+
set parent [file dirname $file]
|
| 1096 |
+
|
| 1097 |
+
# Normalize paths for comparison since lsearch knows nothing of
|
| 1098 |
+
# potential pathname anomalies.
|
| 1099 |
+
set norm_parent [file normalize $parent]
|
| 1100 |
+
|
| 1101 |
+
namespace upvar ::safe [VarName $child] state
|
| 1102 |
+
if {$norm_parent ni $state(access_path,norm)} {
|
| 1103 |
+
return -code error "\"$file\": not in access_path"
|
| 1104 |
+
}
|
| 1105 |
+
}
|
| 1106 |
+
|
| 1107 |
+
proc ::safe::DirInAccessPath {child dir} {
|
| 1108 |
+
namespace upvar ::safe [VarName $child] state
|
| 1109 |
+
set access_path $state(access_path)
|
| 1110 |
+
|
| 1111 |
+
if {[file isfile $dir]} {
|
| 1112 |
+
return -code error "\"$dir\": is a file"
|
| 1113 |
+
}
|
| 1114 |
+
|
| 1115 |
+
# Normalize paths for comparison since lsearch knows nothing of
|
| 1116 |
+
# potential pathname anomalies.
|
| 1117 |
+
set norm_dir [file normalize $dir]
|
| 1118 |
+
|
| 1119 |
+
namespace upvar ::safe [VarName $child] state
|
| 1120 |
+
if {$norm_dir ni $state(access_path,norm)} {
|
| 1121 |
+
return -code error "\"$dir\": not in access_path"
|
| 1122 |
+
}
|
| 1123 |
+
}
|
| 1124 |
+
|
| 1125 |
+
# This procedure is used to report an attempt to use an unsafe member of an
|
| 1126 |
+
# ensemble command.
|
| 1127 |
+
|
| 1128 |
+
proc ::safe::BadSubcommand {child command subcommand args} {
|
| 1129 |
+
set msg "not allowed to invoke subcommand $subcommand of $command"
|
| 1130 |
+
Log $child $msg
|
| 1131 |
+
return -code error -errorcode {TCL SAFE SUBCOMMAND} $msg
|
| 1132 |
+
}
|
| 1133 |
+
|
| 1134 |
+
# AliasEncoding is the target of the "encoding" alias in safe interpreters.
|
| 1135 |
+
|
| 1136 |
+
proc ::safe::AliasEncoding {child option args} {
|
| 1137 |
+
# Note that [encoding dirs] is not supported in safe children at all
|
| 1138 |
+
set subcommands {convertfrom convertto names system}
|
| 1139 |
+
try {
|
| 1140 |
+
set option [tcl::prefix match -error [list -level 1 -errorcode \
|
| 1141 |
+
[list TCL LOOKUP INDEX option $option]] $subcommands $option]
|
| 1142 |
+
# Special case: [encoding system] ok, but [encoding system foo] not
|
| 1143 |
+
if {$option eq "system" && [llength $args]} {
|
| 1144 |
+
return -code error -errorcode {TCL WRONGARGS} \
|
| 1145 |
+
"wrong # args: should be \"encoding system\""
|
| 1146 |
+
}
|
| 1147 |
+
} on error {msg options} {
|
| 1148 |
+
Log $child $msg
|
| 1149 |
+
return -options $options $msg
|
| 1150 |
+
}
|
| 1151 |
+
tailcall ::interp invokehidden $child encoding $option {*}$args
|
| 1152 |
+
}
|
| 1153 |
+
|
| 1154 |
+
# Various minor hiding of platform features. [Bug 2913625]
|
| 1155 |
+
|
| 1156 |
+
proc ::safe::AliasExeName {child} {
|
| 1157 |
+
return ""
|
| 1158 |
+
}
|
| 1159 |
+
|
| 1160 |
+
# ------------------------------------------------------------------------------
|
| 1161 |
+
# Using Interpreter Names with Namespace Qualifiers
|
| 1162 |
+
# ------------------------------------------------------------------------------
|
| 1163 |
+
# (1) We wish to preserve compatibility with existing code, in which Safe Base
|
| 1164 |
+
# interpreter names have no namespace qualifiers.
|
| 1165 |
+
# (2) safe::interpCreate and the rest of the Safe Base previously could not
|
| 1166 |
+
# accept namespace qualifiers in an interpreter name.
|
| 1167 |
+
# (3) The interp command will accept namespace qualifiers in an interpreter
|
| 1168 |
+
# name, but accepts distinct interpreters that will have the same command
|
| 1169 |
+
# name (e.g. foo, ::foo, and :::foo) (bug 66c2e8c974).
|
| 1170 |
+
# (4) To satisfy these constraints, Safe Base interpreter names will be fully
|
| 1171 |
+
# qualified namespace names with no excess colons and with the leading "::"
|
| 1172 |
+
# omitted.
|
| 1173 |
+
# (5) Trailing "::" implies a namespace tail {}, which interp reads as {{}}.
|
| 1174 |
+
# Reject such names.
|
| 1175 |
+
# (6) We could:
|
| 1176 |
+
# (a) EITHER reject usable but non-compliant names (e.g. excess colons) in
|
| 1177 |
+
# interpCreate, interpInit;
|
| 1178 |
+
# (b) OR accept such names and then translate to a compliant name in every
|
| 1179 |
+
# command.
|
| 1180 |
+
# The problem with (b) is that the user will expect to use the name with the
|
| 1181 |
+
# interp command and will find that it is not recognised.
|
| 1182 |
+
# E.g "interpCreate ::foo" creates interpreter "foo", and the user's name
|
| 1183 |
+
# "::foo" works with all the Safe Base commands, but "interp eval ::foo"
|
| 1184 |
+
# fails.
|
| 1185 |
+
# So we choose (a).
|
| 1186 |
+
# (7) The command
|
| 1187 |
+
# namespace upvar ::safe S$child state
|
| 1188 |
+
# becomes
|
| 1189 |
+
# namespace upvar ::safe [VarName $child] state
|
| 1190 |
+
# ------------------------------------------------------------------------------
|
| 1191 |
+
|
| 1192 |
+
proc ::safe::RejectExcessColons {child} {
|
| 1193 |
+
set stripped [regsub -all -- {:::*} $child ::]
|
| 1194 |
+
if {[string range $stripped end-1 end] eq {::}} {
|
| 1195 |
+
return -code error {interpreter name must not end in "::"}
|
| 1196 |
+
}
|
| 1197 |
+
if {$stripped ne $child} {
|
| 1198 |
+
set msg {interpreter name has excess colons in namespace separators}
|
| 1199 |
+
return -code error $msg
|
| 1200 |
+
}
|
| 1201 |
+
if {[string range $stripped 0 1] eq {::}} {
|
| 1202 |
+
return -code error {interpreter name must not begin "::"}
|
| 1203 |
+
}
|
| 1204 |
+
return
|
| 1205 |
+
}
|
| 1206 |
+
|
| 1207 |
+
proc ::safe::VarName {child} {
|
| 1208 |
+
# return S$child
|
| 1209 |
+
return S[string map {:: @N @ @A} $child]
|
| 1210 |
+
}
|
| 1211 |
+
|
| 1212 |
+
proc ::safe::Setup {} {
|
| 1213 |
+
####
|
| 1214 |
+
#
|
| 1215 |
+
# Setup the arguments parsing
|
| 1216 |
+
#
|
| 1217 |
+
####
|
| 1218 |
+
|
| 1219 |
+
# Share the descriptions
|
| 1220 |
+
set temp [::tcl::OptKeyRegister {
|
| 1221 |
+
{-accessPath -list {} "access path for the slave"}
|
| 1222 |
+
{-noStatics "prevent loading of statically linked pkgs"}
|
| 1223 |
+
{-statics true "loading of statically linked pkgs"}
|
| 1224 |
+
{-nestedLoadOk "allow nested loading"}
|
| 1225 |
+
{-nested false "nested loading"}
|
| 1226 |
+
{-deleteHook -script {} "delete hook"}
|
| 1227 |
+
}]
|
| 1228 |
+
|
| 1229 |
+
# create case (slave is optional)
|
| 1230 |
+
::tcl::OptKeyRegister {
|
| 1231 |
+
{?slave? -name {} "name of the slave (optional)"}
|
| 1232 |
+
} ::safe::interpCreate
|
| 1233 |
+
|
| 1234 |
+
# adding the flags sub programs to the command program (relying on Opt's
|
| 1235 |
+
# internal implementation details)
|
| 1236 |
+
lappend ::tcl::OptDesc(::safe::interpCreate) $::tcl::OptDesc($temp)
|
| 1237 |
+
|
| 1238 |
+
# init and configure (slave is needed)
|
| 1239 |
+
::tcl::OptKeyRegister {
|
| 1240 |
+
{slave -name {} "name of the slave"}
|
| 1241 |
+
} ::safe::interpIC
|
| 1242 |
+
|
| 1243 |
+
# adding the flags sub programs to the command program (relying on Opt's
|
| 1244 |
+
# internal implementation details)
|
| 1245 |
+
lappend ::tcl::OptDesc(::safe::interpIC) $::tcl::OptDesc($temp)
|
| 1246 |
+
|
| 1247 |
+
# temp not needed anymore
|
| 1248 |
+
::tcl::OptKeyDelete $temp
|
| 1249 |
+
|
| 1250 |
+
####
|
| 1251 |
+
#
|
| 1252 |
+
# Default: No logging.
|
| 1253 |
+
#
|
| 1254 |
+
####
|
| 1255 |
+
|
| 1256 |
+
setLogCmd {}
|
| 1257 |
+
|
| 1258 |
+
# Log eventually.
|
| 1259 |
+
# To enable error logging, set Log to {puts stderr} for instance,
|
| 1260 |
+
# via setLogCmd.
|
| 1261 |
+
return
|
| 1262 |
+
}
|
| 1263 |
+
|
| 1264 |
+
namespace eval ::safe {
|
| 1265 |
+
# internal variables
|
| 1266 |
+
|
| 1267 |
+
# Log command, set via 'setLogCmd'. Logging is disabled when empty.
|
| 1268 |
+
variable Log {}
|
| 1269 |
+
|
| 1270 |
+
# The package maintains a state array per child interp under its
|
| 1271 |
+
# control. The name of this array is S<interp-name>. This array is
|
| 1272 |
+
# brought into scope where needed, using 'namespace upvar'. The S
|
| 1273 |
+
# prefix is used to avoid that a child interp called "Log" smashes
|
| 1274 |
+
# the "Log" variable.
|
| 1275 |
+
#
|
| 1276 |
+
# The array's elements are:
|
| 1277 |
+
#
|
| 1278 |
+
# access_path : List of paths accessible to the child.
|
| 1279 |
+
# access_path,norm : Ditto, in normalized form.
|
| 1280 |
+
# access_path,slave : Ditto, as the path tokens as seen by the child.
|
| 1281 |
+
# access_path,map : dict ( token -> path )
|
| 1282 |
+
# access_path,remap : dict ( path -> token )
|
| 1283 |
+
# tm_path_slave : List of TM root directories, as tokens seen by the child.
|
| 1284 |
+
# staticsok : Value of option -statics
|
| 1285 |
+
# nestedok : Value of option -nested
|
| 1286 |
+
# cleanupHook : Value of option -deleteHook
|
| 1287 |
+
}
|
| 1288 |
+
|
| 1289 |
+
::safe::Setup
|
lib/tcl8.6/tclAppInit.c
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/*
|
| 2 |
+
* tclAppInit.c --
|
| 3 |
+
*
|
| 4 |
+
* Provides a default version of the main program and Tcl_AppInit
|
| 5 |
+
* procedure for tclsh and other Tcl-based applications (without Tk).
|
| 6 |
+
*
|
| 7 |
+
* Copyright (c) 1993 The Regents of the University of California.
|
| 8 |
+
* Copyright (c) 1994-1997 Sun Microsystems, Inc.
|
| 9 |
+
* Copyright (c) 1998-1999 Scriptics Corporation.
|
| 10 |
+
*
|
| 11 |
+
* See the file "license.terms" for information on usage and redistribution of
|
| 12 |
+
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
| 13 |
+
*/
|
| 14 |
+
|
| 15 |
+
#undef BUILD_tcl
|
| 16 |
+
#undef STATIC_BUILD
|
| 17 |
+
#include "tcl.h"
|
| 18 |
+
#if TCL_MAJOR_VERSION < 9 && TCL_MINOR_VERSION < 7
|
| 19 |
+
# define Tcl_LibraryInitProc Tcl_PackageInitProc
|
| 20 |
+
# define Tcl_StaticLibrary Tcl_StaticPackage
|
| 21 |
+
#endif
|
| 22 |
+
|
| 23 |
+
#ifdef TCL_TEST
|
| 24 |
+
extern Tcl_LibraryInitProc Tcltest_Init;
|
| 25 |
+
extern Tcl_LibraryInitProc Tcltest_SafeInit;
|
| 26 |
+
#endif /* TCL_TEST */
|
| 27 |
+
|
| 28 |
+
#ifdef TCL_XT_TEST
|
| 29 |
+
extern void XtToolkitInitialize(void);
|
| 30 |
+
extern Tcl_LibraryInitProc Tclxttest_Init;
|
| 31 |
+
#endif /* TCL_XT_TEST */
|
| 32 |
+
|
| 33 |
+
/*
|
| 34 |
+
* The following #if block allows you to change the AppInit function by using
|
| 35 |
+
* a #define of TCL_LOCAL_APPINIT instead of rewriting this entire file. The
|
| 36 |
+
* #if checks for that #define and uses Tcl_AppInit if it does not exist.
|
| 37 |
+
*/
|
| 38 |
+
|
| 39 |
+
#ifndef TCL_LOCAL_APPINIT
|
| 40 |
+
#define TCL_LOCAL_APPINIT Tcl_AppInit
|
| 41 |
+
#endif
|
| 42 |
+
#ifndef MODULE_SCOPE
|
| 43 |
+
# define MODULE_SCOPE extern
|
| 44 |
+
#endif
|
| 45 |
+
MODULE_SCOPE int TCL_LOCAL_APPINIT(Tcl_Interp *);
|
| 46 |
+
MODULE_SCOPE int main(int, char **);
|
| 47 |
+
|
| 48 |
+
/*
|
| 49 |
+
* The following #if block allows you to change how Tcl finds the startup
|
| 50 |
+
* script, prime the library or encoding paths, fiddle with the argv, etc.,
|
| 51 |
+
* without needing to rewrite Tcl_Main()
|
| 52 |
+
*/
|
| 53 |
+
|
| 54 |
+
#ifdef TCL_LOCAL_MAIN_HOOK
|
| 55 |
+
MODULE_SCOPE int TCL_LOCAL_MAIN_HOOK(int *argc, char ***argv);
|
| 56 |
+
#endif
|
| 57 |
+
|
| 58 |
+
/*
|
| 59 |
+
*----------------------------------------------------------------------
|
| 60 |
+
*
|
| 61 |
+
* main --
|
| 62 |
+
*
|
| 63 |
+
* This is the main program for the application.
|
| 64 |
+
*
|
| 65 |
+
* Results:
|
| 66 |
+
* None: Tcl_Main never returns here, so this procedure never returns
|
| 67 |
+
* either.
|
| 68 |
+
*
|
| 69 |
+
* Side effects:
|
| 70 |
+
* Just about anything, since from here we call arbitrary Tcl code.
|
| 71 |
+
*
|
| 72 |
+
*----------------------------------------------------------------------
|
| 73 |
+
*/
|
| 74 |
+
|
| 75 |
+
int
|
| 76 |
+
main(
|
| 77 |
+
int argc, /* Number of command-line arguments. */
|
| 78 |
+
char *argv[]) /* Values of command-line arguments. */
|
| 79 |
+
{
|
| 80 |
+
#ifdef TCL_XT_TEST
|
| 81 |
+
XtToolkitInitialize();
|
| 82 |
+
#endif
|
| 83 |
+
|
| 84 |
+
#ifdef TCL_LOCAL_MAIN_HOOK
|
| 85 |
+
TCL_LOCAL_MAIN_HOOK(&argc, &argv);
|
| 86 |
+
#elif (TCL_MAJOR_VERSION > 8 || TCL_MINOR_VERSION > 6) && (!defined(_WIN32) || defined(UNICODE))
|
| 87 |
+
/* New in Tcl 8.7. This doesn't work on Windows without UNICODE */
|
| 88 |
+
TclZipfs_AppHook(&argc, &argv);
|
| 89 |
+
#endif
|
| 90 |
+
|
| 91 |
+
Tcl_Main(argc, argv, TCL_LOCAL_APPINIT);
|
| 92 |
+
return 0; /* Needed only to prevent compiler warning. */
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
/*
|
| 96 |
+
*----------------------------------------------------------------------
|
| 97 |
+
*
|
| 98 |
+
* Tcl_AppInit --
|
| 99 |
+
*
|
| 100 |
+
* This procedure performs application-specific initialization. Most
|
| 101 |
+
* applications, especially those that incorporate additional packages,
|
| 102 |
+
* will have their own version of this procedure.
|
| 103 |
+
*
|
| 104 |
+
* Results:
|
| 105 |
+
* Returns a standard Tcl completion code, and leaves an error message in
|
| 106 |
+
* the interp's result if an error occurs.
|
| 107 |
+
*
|
| 108 |
+
* Side effects:
|
| 109 |
+
* Depends on the startup script.
|
| 110 |
+
*
|
| 111 |
+
*----------------------------------------------------------------------
|
| 112 |
+
*/
|
| 113 |
+
|
| 114 |
+
int
|
| 115 |
+
Tcl_AppInit(
|
| 116 |
+
Tcl_Interp *interp) /* Interpreter for application. */
|
| 117 |
+
{
|
| 118 |
+
if ((Tcl_Init)(interp) == TCL_ERROR) {
|
| 119 |
+
return TCL_ERROR;
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
#ifdef TCL_XT_TEST
|
| 123 |
+
if (Tclxttest_Init(interp) == TCL_ERROR) {
|
| 124 |
+
return TCL_ERROR;
|
| 125 |
+
}
|
| 126 |
+
#endif
|
| 127 |
+
|
| 128 |
+
#ifdef TCL_TEST
|
| 129 |
+
if (Tcltest_Init(interp) == TCL_ERROR) {
|
| 130 |
+
return TCL_ERROR;
|
| 131 |
+
}
|
| 132 |
+
Tcl_StaticLibrary(interp, "Tcltest", Tcltest_Init, Tcltest_SafeInit);
|
| 133 |
+
#endif /* TCL_TEST */
|
| 134 |
+
|
| 135 |
+
/*
|
| 136 |
+
* Call the init procedures for included packages. Each call should look
|
| 137 |
+
* like this:
|
| 138 |
+
*
|
| 139 |
+
* if (Mod_Init(interp) == TCL_ERROR) {
|
| 140 |
+
* return TCL_ERROR;
|
| 141 |
+
* }
|
| 142 |
+
*
|
| 143 |
+
* where "Mod" is the name of the module. (Dynamically-loadable packages
|
| 144 |
+
* should have the same entry-point name.)
|
| 145 |
+
*/
|
| 146 |
+
|
| 147 |
+
/*
|
| 148 |
+
* Call Tcl_CreateCommand for application-specific commands, if they
|
| 149 |
+
* weren't already created by the init procedures called above.
|
| 150 |
+
*/
|
| 151 |
+
|
| 152 |
+
/*
|
| 153 |
+
* Specify a user-specific startup file to invoke if the application is
|
| 154 |
+
* run interactively. Typically the startup file is "~/.apprc" where "app"
|
| 155 |
+
* is the name of the application. If this line is deleted then no
|
| 156 |
+
* user-specific startup file will be run under any conditions.
|
| 157 |
+
*/
|
| 158 |
+
|
| 159 |
+
#ifdef DJGPP
|
| 160 |
+
(Tcl_ObjSetVar2)(interp, Tcl_NewStringObj("tcl_rcFileName", -1), NULL,
|
| 161 |
+
Tcl_NewStringObj("~/tclsh.rc", -1), TCL_GLOBAL_ONLY);
|
| 162 |
+
#else
|
| 163 |
+
(Tcl_ObjSetVar2)(interp, Tcl_NewStringObj("tcl_rcFileName", -1), NULL,
|
| 164 |
+
Tcl_NewStringObj("~/.tclshrc", -1), TCL_GLOBAL_ONLY);
|
| 165 |
+
#endif
|
| 166 |
+
|
| 167 |
+
return TCL_OK;
|
| 168 |
+
}
|
| 169 |
+
|
| 170 |
+
/*
|
| 171 |
+
* Local Variables:
|
| 172 |
+
* mode: c
|
| 173 |
+
* c-basic-offset: 4
|
| 174 |
+
* fill-column: 78
|
| 175 |
+
* End:
|
| 176 |
+
*/
|
lib/tcl8.6/tclIndex
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Tcl autoload index file, version 2.0
|
| 2 |
+
# -*- tcl -*-
|
| 3 |
+
# This file is generated by the "auto_mkindex" command
|
| 4 |
+
# and sourced to set up indexing information for one or
|
| 5 |
+
# more commands. Typically each line is a command that
|
| 6 |
+
# sets an element in the auto_index array, where the
|
| 7 |
+
# element name is the name of a command and the value is
|
| 8 |
+
# a script that loads the command.
|
| 9 |
+
|
| 10 |
+
set auto_index(auto_reset) [list source [file join $dir auto.tcl]]
|
| 11 |
+
set auto_index(tcl_findLibrary) [list source [file join $dir auto.tcl]]
|
| 12 |
+
set auto_index(auto_mkindex) [list source [file join $dir auto.tcl]]
|
| 13 |
+
set auto_index(auto_mkindex_old) [list source [file join $dir auto.tcl]]
|
| 14 |
+
set auto_index(::auto_mkindex_parser::init) [list source [file join $dir auto.tcl]]
|
| 15 |
+
set auto_index(::auto_mkindex_parser::cleanup) [list source [file join $dir auto.tcl]]
|
| 16 |
+
set auto_index(::auto_mkindex_parser::mkindex) [list source [file join $dir auto.tcl]]
|
| 17 |
+
set auto_index(::auto_mkindex_parser::hook) [list source [file join $dir auto.tcl]]
|
| 18 |
+
set auto_index(::auto_mkindex_parser::slavehook) [list source [file join $dir auto.tcl]]
|
| 19 |
+
set auto_index(::auto_mkindex_parser::command) [list source [file join $dir auto.tcl]]
|
| 20 |
+
set auto_index(::auto_mkindex_parser::commandInit) [list source [file join $dir auto.tcl]]
|
| 21 |
+
set auto_index(::auto_mkindex_parser::fullname) [list source [file join $dir auto.tcl]]
|
| 22 |
+
set auto_index(history) [list source [file join $dir history.tcl]]
|
| 23 |
+
set auto_index(::tcl::history) [list source [file join $dir history.tcl]]
|
| 24 |
+
set auto_index(::tcl::HistAdd) [list source [file join $dir history.tcl]]
|
| 25 |
+
set auto_index(::tcl::HistKeep) [list source [file join $dir history.tcl]]
|
| 26 |
+
set auto_index(::tcl::HistClear) [list source [file join $dir history.tcl]]
|
| 27 |
+
set auto_index(::tcl::HistInfo) [list source [file join $dir history.tcl]]
|
| 28 |
+
set auto_index(::tcl::HistRedo) [list source [file join $dir history.tcl]]
|
| 29 |
+
set auto_index(::tcl::HistIndex) [list source [file join $dir history.tcl]]
|
| 30 |
+
set auto_index(::tcl::HistEvent) [list source [file join $dir history.tcl]]
|
| 31 |
+
set auto_index(::tcl::HistChange) [list source [file join $dir history.tcl]]
|
| 32 |
+
set auto_index(pkg_mkIndex) [list source [file join $dir package.tcl]]
|
| 33 |
+
set auto_index(tclPkgSetup) [list source [file join $dir package.tcl]]
|
| 34 |
+
set auto_index(tclPkgUnknown) [list source [file join $dir package.tcl]]
|
| 35 |
+
set auto_index(::tcl::MacOSXPkgUnknown) [list source [file join $dir package.tcl]]
|
| 36 |
+
set auto_index(::pkg::create) [list source [file join $dir package.tcl]]
|
| 37 |
+
set auto_index(parray) [list source [file join $dir parray.tcl]]
|
| 38 |
+
set auto_index(::safe::InterpStatics) [list source [file join $dir safe.tcl]]
|
| 39 |
+
set auto_index(::safe::InterpNested) [list source [file join $dir safe.tcl]]
|
| 40 |
+
set auto_index(::safe::interpCreate) [list source [file join $dir safe.tcl]]
|
| 41 |
+
set auto_index(::safe::interpInit) [list source [file join $dir safe.tcl]]
|
| 42 |
+
set auto_index(::safe::CheckInterp) [list source [file join $dir safe.tcl]]
|
| 43 |
+
set auto_index(::safe::interpConfigure) [list source [file join $dir safe.tcl]]
|
| 44 |
+
set auto_index(::safe::InterpCreate) [list source [file join $dir safe.tcl]]
|
| 45 |
+
set auto_index(::safe::InterpSetConfig) [list source [file join $dir safe.tcl]]
|
| 46 |
+
set auto_index(::safe::interpFindInAccessPath) [list source [file join $dir safe.tcl]]
|
| 47 |
+
set auto_index(::safe::interpAddToAccessPath) [list source [file join $dir safe.tcl]]
|
| 48 |
+
set auto_index(::safe::InterpInit) [list source [file join $dir safe.tcl]]
|
| 49 |
+
set auto_index(::safe::AddSubDirs) [list source [file join $dir safe.tcl]]
|
| 50 |
+
set auto_index(::safe::interpDelete) [list source [file join $dir safe.tcl]]
|
| 51 |
+
set auto_index(::safe::setLogCmd) [list source [file join $dir safe.tcl]]
|
| 52 |
+
set auto_index(::safe::SyncAccessPath) [list source [file join $dir safe.tcl]]
|
| 53 |
+
set auto_index(::safe::PathToken) [list source [file join $dir safe.tcl]]
|
| 54 |
+
set auto_index(::safe::TranslatePath) [list source [file join $dir safe.tcl]]
|
| 55 |
+
set auto_index(::safe::Log) [list source [file join $dir safe.tcl]]
|
| 56 |
+
set auto_index(::safe::CheckFileName) [list source [file join $dir safe.tcl]]
|
| 57 |
+
set auto_index(::safe::AliasGlob) [list source [file join $dir safe.tcl]]
|
| 58 |
+
set auto_index(::safe::AliasSource) [list source [file join $dir safe.tcl]]
|
| 59 |
+
set auto_index(::safe::AliasLoad) [list source [file join $dir safe.tcl]]
|
| 60 |
+
set auto_index(::safe::FileInAccessPath) [list source [file join $dir safe.tcl]]
|
| 61 |
+
set auto_index(::safe::DirInAccessPath) [list source [file join $dir safe.tcl]]
|
| 62 |
+
set auto_index(::safe::Subset) [list source [file join $dir safe.tcl]]
|
| 63 |
+
set auto_index(::safe::AliasSubset) [list source [file join $dir safe.tcl]]
|
| 64 |
+
set auto_index(::safe::AliasEncoding) [list source [file join $dir safe.tcl]]
|
| 65 |
+
set auto_index(tcl_wordBreakAfter) [list source [file join $dir word.tcl]]
|
| 66 |
+
set auto_index(tcl_wordBreakBefore) [list source [file join $dir word.tcl]]
|
| 67 |
+
set auto_index(tcl_endOfWord) [list source [file join $dir word.tcl]]
|
| 68 |
+
set auto_index(tcl_startOfNextWord) [list source [file join $dir word.tcl]]
|
| 69 |
+
set auto_index(tcl_startOfPreviousWord) [list source [file join $dir word.tcl]]
|
| 70 |
+
set auto_index(::tcl::tm::add) [list source [file join $dir tm.tcl]]
|
| 71 |
+
set auto_index(::tcl::tm::remove) [list source [file join $dir tm.tcl]]
|
| 72 |
+
set auto_index(::tcl::tm::list) [list source [file join $dir tm.tcl]]
|
| 73 |
+
set auto_index(::tcl::tm::Defaults) [list source [file join $dir tm.tcl]]
|
| 74 |
+
set auto_index(::tcl::tm::UnknownHandler) [list source [file join $dir tm.tcl]]
|
| 75 |
+
set auto_index(::tcl::tm::roots) [list source [file join $dir tm.tcl]]
|
| 76 |
+
set auto_index(::tcl::tm::path) [list source [file join $dir tm.tcl]]
|
| 77 |
+
if {[namespace exists ::tcl::unsupported]} {
|
| 78 |
+
set auto_index(timerate) {namespace import ::tcl::unsupported::timerate}
|
| 79 |
+
}
|
lib/tcl8.6/tm.tcl
ADDED
|
@@ -0,0 +1,380 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- tcl -*-
|
| 2 |
+
#
|
| 3 |
+
# Searching for Tcl Modules. Defines a procedure, declares it as the primary
|
| 4 |
+
# command for finding packages, however also uses the former 'package unknown'
|
| 5 |
+
# command as a fallback.
|
| 6 |
+
#
|
| 7 |
+
# Locates all possible packages in a directory via a less restricted glob. The
|
| 8 |
+
# targeted directory is derived from the name of the requested package, i.e.
|
| 9 |
+
# the TM scan will look only at directories which can contain the requested
|
| 10 |
+
# package. It will register all packages it found in the directory so that
|
| 11 |
+
# future requests have a higher chance of being fulfilled by the ifneeded
|
| 12 |
+
# database without having to come to us again.
|
| 13 |
+
#
|
| 14 |
+
# We do not remember where we have been and simply rescan targeted directories
|
| 15 |
+
# when invoked again. The reasoning is this:
|
| 16 |
+
#
|
| 17 |
+
# - The only way we get back to the same directory is if someone is trying to
|
| 18 |
+
# [package require] something that wasn't there on the first scan.
|
| 19 |
+
#
|
| 20 |
+
# Either
|
| 21 |
+
# 1) It is there now: If we rescan, you get it; if not you don't.
|
| 22 |
+
#
|
| 23 |
+
# This covers the possibility that the application asked for a package
|
| 24 |
+
# late, and the package was actually added to the installation after the
|
| 25 |
+
# application was started. It should still be able to find it.
|
| 26 |
+
#
|
| 27 |
+
# 2) It still is not there: Either way, you don't get it, but the rescan
|
| 28 |
+
# takes time. This is however an error case and we don't care that much
|
| 29 |
+
# about it
|
| 30 |
+
#
|
| 31 |
+
# 3) It was there the first time; but for some reason a "package forget" has
|
| 32 |
+
# been run, and "package" doesn't know about it anymore.
|
| 33 |
+
#
|
| 34 |
+
# This can be an indication that the application wishes to reload some
|
| 35 |
+
# functionality. And should work as well.
|
| 36 |
+
#
|
| 37 |
+
# Note that this also strikes a balance between doing a glob targeting a
|
| 38 |
+
# single package, and thus most likely requiring multiple globs of the same
|
| 39 |
+
# directory when the application is asking for many packages, and trying to
|
| 40 |
+
# glob for _everything_ in all subdirectories when looking for a package,
|
| 41 |
+
# which comes with a heavy startup cost.
|
| 42 |
+
#
|
| 43 |
+
# We scan for regular packages only if no satisfying module was found.
|
| 44 |
+
|
| 45 |
+
namespace eval ::tcl::tm {
|
| 46 |
+
# Default paths. None yet.
|
| 47 |
+
|
| 48 |
+
variable paths {}
|
| 49 |
+
|
| 50 |
+
# The regex pattern a file name has to match to make it a Tcl Module.
|
| 51 |
+
|
| 52 |
+
set pkgpattern {^([_[:alpha:]][:_[:alnum:]]*)-([[:digit:]].*)[.]tm$}
|
| 53 |
+
|
| 54 |
+
# Export the public API
|
| 55 |
+
|
| 56 |
+
namespace export path
|
| 57 |
+
namespace ensemble create -command path -subcommands {add remove list}
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
# ::tcl::tm::path implementations --
|
| 61 |
+
#
|
| 62 |
+
# Public API to the module path. See specification.
|
| 63 |
+
#
|
| 64 |
+
# Arguments
|
| 65 |
+
# cmd - The subcommand to execute
|
| 66 |
+
# args - The paths to add/remove. Must not appear querying the
|
| 67 |
+
# path with 'list'.
|
| 68 |
+
#
|
| 69 |
+
# Results
|
| 70 |
+
# No result for subcommands 'add' and 'remove'. A list of paths for
|
| 71 |
+
# 'list'.
|
| 72 |
+
#
|
| 73 |
+
# Side effects
|
| 74 |
+
# The subcommands 'add' and 'remove' manipulate the list of paths to
|
| 75 |
+
# search for Tcl Modules. The subcommand 'list' has no side effects.
|
| 76 |
+
|
| 77 |
+
proc ::tcl::tm::add {args} {
|
| 78 |
+
# PART OF THE ::tcl::tm::path ENSEMBLE
|
| 79 |
+
#
|
| 80 |
+
# The path is added at the head to the list of module paths.
|
| 81 |
+
#
|
| 82 |
+
# The command enforces the restriction that no path may be an ancestor
|
| 83 |
+
# directory of any other path on the list. If the new path violates this
|
| 84 |
+
# restriction an error will be raised.
|
| 85 |
+
#
|
| 86 |
+
# If the path is already present as is no error will be raised and no
|
| 87 |
+
# action will be taken.
|
| 88 |
+
|
| 89 |
+
variable paths
|
| 90 |
+
|
| 91 |
+
# We use a copy of the path as source during validation, and extend it as
|
| 92 |
+
# well. Because we not only have to detect if the new paths are bogus with
|
| 93 |
+
# respect to the existing paths, but also between themselves. Otherwise we
|
| 94 |
+
# can still add bogus paths, by specifying them in a single call. This
|
| 95 |
+
# makes the use of the new paths simpler as well, a trivial assignment of
|
| 96 |
+
# the collected paths to the official state var.
|
| 97 |
+
|
| 98 |
+
set newpaths $paths
|
| 99 |
+
foreach p $args {
|
| 100 |
+
if {$p in $newpaths} {
|
| 101 |
+
# Ignore a path already on the list.
|
| 102 |
+
continue
|
| 103 |
+
}
|
| 104 |
+
|
| 105 |
+
# Search for paths which are subdirectories of the new one. If there
|
| 106 |
+
# are any then the new path violates the restriction about ancestors.
|
| 107 |
+
|
| 108 |
+
set pos [lsearch -glob $newpaths ${p}/*]
|
| 109 |
+
# Cannot use "in", we need the position for the message.
|
| 110 |
+
if {$pos >= 0} {
|
| 111 |
+
return -code error \
|
| 112 |
+
"$p is ancestor of existing module path [lindex $newpaths $pos]."
|
| 113 |
+
}
|
| 114 |
+
|
| 115 |
+
# Now look for existing paths which are ancestors of the new one. This
|
| 116 |
+
# reverse question forces us to loop over the existing paths, as each
|
| 117 |
+
# element is the pattern, not the new path :(
|
| 118 |
+
|
| 119 |
+
foreach ep $newpaths {
|
| 120 |
+
if {[string match ${ep}/* $p]} {
|
| 121 |
+
return -code error \
|
| 122 |
+
"$p is subdirectory of existing module path $ep."
|
| 123 |
+
}
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
set newpaths [linsert $newpaths 0 $p]
|
| 127 |
+
}
|
| 128 |
+
|
| 129 |
+
# The validation of the input is complete and successful, and everything
|
| 130 |
+
# in newpaths is either an old path, or added. We can now extend the
|
| 131 |
+
# official list of paths, a simple assignment is sufficient.
|
| 132 |
+
|
| 133 |
+
set paths $newpaths
|
| 134 |
+
return
|
| 135 |
+
}
|
| 136 |
+
|
| 137 |
+
proc ::tcl::tm::remove {args} {
|
| 138 |
+
# PART OF THE ::tcl::tm::path ENSEMBLE
|
| 139 |
+
#
|
| 140 |
+
# Removes the path from the list of module paths. The command is silently
|
| 141 |
+
# ignored if the path is not on the list.
|
| 142 |
+
|
| 143 |
+
variable paths
|
| 144 |
+
|
| 145 |
+
foreach p $args {
|
| 146 |
+
set pos [lsearch -exact $paths $p]
|
| 147 |
+
if {$pos >= 0} {
|
| 148 |
+
set paths [lreplace $paths $pos $pos]
|
| 149 |
+
}
|
| 150 |
+
}
|
| 151 |
+
}
|
| 152 |
+
|
| 153 |
+
proc ::tcl::tm::list {} {
|
| 154 |
+
# PART OF THE ::tcl::tm::path ENSEMBLE
|
| 155 |
+
|
| 156 |
+
variable paths
|
| 157 |
+
return $paths
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
+
# ::tcl::tm::UnknownHandler --
|
| 161 |
+
#
|
| 162 |
+
# Unknown handler for Tcl Modules, i.e. packages in module form.
|
| 163 |
+
#
|
| 164 |
+
# Arguments
|
| 165 |
+
# original - Original [package unknown] procedure.
|
| 166 |
+
# name - Name of desired package.
|
| 167 |
+
# version - Version of desired package. Can be the
|
| 168 |
+
# empty string.
|
| 169 |
+
# exact - Either -exact or omitted.
|
| 170 |
+
#
|
| 171 |
+
# Name, version, and exact are used to determine satisfaction. The
|
| 172 |
+
# original is called iff no satisfaction was achieved. The name is also
|
| 173 |
+
# used to compute the directory to target in the search.
|
| 174 |
+
#
|
| 175 |
+
# Results
|
| 176 |
+
# None.
|
| 177 |
+
#
|
| 178 |
+
# Side effects
|
| 179 |
+
# May populate the package ifneeded database with additional provide
|
| 180 |
+
# scripts.
|
| 181 |
+
|
| 182 |
+
proc ::tcl::tm::UnknownHandler {original name args} {
|
| 183 |
+
# Import the list of paths to search for packages in module form.
|
| 184 |
+
# Import the pattern used to check package names in detail.
|
| 185 |
+
|
| 186 |
+
variable paths
|
| 187 |
+
variable pkgpattern
|
| 188 |
+
|
| 189 |
+
# Without paths to search we can do nothing. (Except falling back to the
|
| 190 |
+
# regular search).
|
| 191 |
+
|
| 192 |
+
if {[llength $paths]} {
|
| 193 |
+
set pkgpath [string map {:: /} $name]
|
| 194 |
+
set pkgroot [file dirname $pkgpath]
|
| 195 |
+
if {$pkgroot eq "."} {
|
| 196 |
+
set pkgroot ""
|
| 197 |
+
}
|
| 198 |
+
|
| 199 |
+
# We don't remember a copy of the paths while looping. Tcl Modules are
|
| 200 |
+
# unable to change the list while we are searching for them. This also
|
| 201 |
+
# simplifies the loop, as we cannot get additional directories while
|
| 202 |
+
# iterating over the list. A simple foreach is sufficient.
|
| 203 |
+
|
| 204 |
+
set satisfied 0
|
| 205 |
+
foreach path $paths {
|
| 206 |
+
if {![interp issafe] && ![file exists $path]} {
|
| 207 |
+
continue
|
| 208 |
+
}
|
| 209 |
+
set currentsearchpath [file join $path $pkgroot]
|
| 210 |
+
if {![interp issafe] && ![file exists $currentsearchpath]} {
|
| 211 |
+
continue
|
| 212 |
+
}
|
| 213 |
+
set strip [llength [file split $path]]
|
| 214 |
+
|
| 215 |
+
# Get the module files out of the subdirectories.
|
| 216 |
+
# - Safe Base interpreters have a restricted "glob" command that
|
| 217 |
+
# works in this case.
|
| 218 |
+
# - The "catch" was essential when there was no safe glob and every
|
| 219 |
+
# call in a safe interp failed; it is retained only for corner
|
| 220 |
+
# cases in which the eventual call to glob returns an error.
|
| 221 |
+
|
| 222 |
+
catch {
|
| 223 |
+
# We always look for _all_ possible modules in the current
|
| 224 |
+
# path, to get the max result out of the glob.
|
| 225 |
+
|
| 226 |
+
foreach file [glob -nocomplain -directory $currentsearchpath *.tm] {
|
| 227 |
+
set pkgfilename [join [lrange [file split $file] $strip end] ::]
|
| 228 |
+
|
| 229 |
+
if {![regexp -- $pkgpattern $pkgfilename --> pkgname pkgversion]} {
|
| 230 |
+
# Ignore everything not matching our pattern for
|
| 231 |
+
# package names.
|
| 232 |
+
continue
|
| 233 |
+
}
|
| 234 |
+
try {
|
| 235 |
+
package vcompare $pkgversion 0
|
| 236 |
+
} on error {} {
|
| 237 |
+
# Ignore everything where the version part is not
|
| 238 |
+
# acceptable to "package vcompare".
|
| 239 |
+
continue
|
| 240 |
+
}
|
| 241 |
+
|
| 242 |
+
if {([package ifneeded $pkgname $pkgversion] ne {})
|
| 243 |
+
&& (![interp issafe])
|
| 244 |
+
} {
|
| 245 |
+
# There's already a provide script registered for
|
| 246 |
+
# this version of this package. Since all units of
|
| 247 |
+
# code claiming to be the same version of the same
|
| 248 |
+
# package ought to be identical, just stick with
|
| 249 |
+
# the one we already have.
|
| 250 |
+
# This does not apply to Safe Base interpreters because
|
| 251 |
+
# the token-to-directory mapping may have changed.
|
| 252 |
+
continue
|
| 253 |
+
}
|
| 254 |
+
|
| 255 |
+
# We have found a candidate, generate a "provide script"
|
| 256 |
+
# for it, and remember it. Note that we are using ::list
|
| 257 |
+
# to do this; locally [list] means something else without
|
| 258 |
+
# the namespace specifier.
|
| 259 |
+
|
| 260 |
+
# NOTE. When making changes to the format of the provide
|
| 261 |
+
# command generated below CHECK that the 'LOCATE'
|
| 262 |
+
# procedure in core file 'platform/shell.tcl' still
|
| 263 |
+
# understands it, or, if not, update its implementation
|
| 264 |
+
# appropriately.
|
| 265 |
+
#
|
| 266 |
+
# Right now LOCATE's implementation assumes that the path
|
| 267 |
+
# of the package file is the last element in the list.
|
| 268 |
+
|
| 269 |
+
package ifneeded $pkgname $pkgversion \
|
| 270 |
+
"[::list package provide $pkgname $pkgversion];[::list source -encoding utf-8 $file]"
|
| 271 |
+
|
| 272 |
+
# We abort in this unknown handler only if we got a
|
| 273 |
+
# satisfying candidate for the requested package.
|
| 274 |
+
# Otherwise we still have to fallback to the regular
|
| 275 |
+
# package search to complete the processing.
|
| 276 |
+
|
| 277 |
+
if {($pkgname eq $name)
|
| 278 |
+
&& [package vsatisfies $pkgversion {*}$args]} {
|
| 279 |
+
set satisfied 1
|
| 280 |
+
|
| 281 |
+
# We do not abort the loop, and keep adding provide
|
| 282 |
+
# scripts for every candidate in the directory, just
|
| 283 |
+
# remember to not fall back to the regular search
|
| 284 |
+
# anymore.
|
| 285 |
+
}
|
| 286 |
+
}
|
| 287 |
+
}
|
| 288 |
+
}
|
| 289 |
+
|
| 290 |
+
if {$satisfied} {
|
| 291 |
+
return
|
| 292 |
+
}
|
| 293 |
+
}
|
| 294 |
+
|
| 295 |
+
# Fallback to previous command, if existing. See comment above about
|
| 296 |
+
# ::list...
|
| 297 |
+
|
| 298 |
+
if {[llength $original]} {
|
| 299 |
+
uplevel 1 $original [::linsert $args 0 $name]
|
| 300 |
+
}
|
| 301 |
+
}
|
| 302 |
+
|
| 303 |
+
# ::tcl::tm::Defaults --
|
| 304 |
+
#
|
| 305 |
+
# Determines the default search paths.
|
| 306 |
+
#
|
| 307 |
+
# Arguments
|
| 308 |
+
# None
|
| 309 |
+
#
|
| 310 |
+
# Results
|
| 311 |
+
# None.
|
| 312 |
+
#
|
| 313 |
+
# Side effects
|
| 314 |
+
# May add paths to the list of defaults.
|
| 315 |
+
|
| 316 |
+
proc ::tcl::tm::Defaults {} {
|
| 317 |
+
global env tcl_platform
|
| 318 |
+
|
| 319 |
+
regexp {^(\d+)\.(\d+)} [package provide Tcl] - major minor
|
| 320 |
+
set exe [file normalize [info nameofexecutable]]
|
| 321 |
+
|
| 322 |
+
# Note that we're using [::list], not [list] because [list] means
|
| 323 |
+
# something other than [::list] in this namespace.
|
| 324 |
+
roots [::list \
|
| 325 |
+
[file dirname [info library]] \
|
| 326 |
+
[file join [file dirname [file dirname $exe]] lib] \
|
| 327 |
+
]
|
| 328 |
+
|
| 329 |
+
if {$tcl_platform(platform) eq "windows"} {
|
| 330 |
+
set sep ";"
|
| 331 |
+
} else {
|
| 332 |
+
set sep ":"
|
| 333 |
+
}
|
| 334 |
+
for {set n $minor} {$n >= 0} {incr n -1} {
|
| 335 |
+
foreach ev [::list \
|
| 336 |
+
TCL${major}.${n}_TM_PATH \
|
| 337 |
+
TCL${major}_${n}_TM_PATH \
|
| 338 |
+
] {
|
| 339 |
+
if {![info exists env($ev)]} continue
|
| 340 |
+
foreach p [split $env($ev) $sep] {
|
| 341 |
+
path add $p
|
| 342 |
+
}
|
| 343 |
+
}
|
| 344 |
+
}
|
| 345 |
+
return
|
| 346 |
+
}
|
| 347 |
+
|
| 348 |
+
# ::tcl::tm::roots --
|
| 349 |
+
#
|
| 350 |
+
# Public API to the module path. See specification.
|
| 351 |
+
#
|
| 352 |
+
# Arguments
|
| 353 |
+
# paths - List of 'root' paths to derive search paths from.
|
| 354 |
+
#
|
| 355 |
+
# Results
|
| 356 |
+
# No result.
|
| 357 |
+
#
|
| 358 |
+
# Side effects
|
| 359 |
+
# Calls 'path add' to paths to the list of module search paths.
|
| 360 |
+
|
| 361 |
+
proc ::tcl::tm::roots {paths} {
|
| 362 |
+
regexp {^(\d+)\.(\d+)} [package provide Tcl] - major minor
|
| 363 |
+
foreach pa $paths {
|
| 364 |
+
set p [file join $pa tcl$major]
|
| 365 |
+
for {set n $minor} {$n >= 0} {incr n -1} {
|
| 366 |
+
set px [file join $p ${major}.${n}]
|
| 367 |
+
if {![interp issafe]} {set px [file normalize $px]}
|
| 368 |
+
path add $px
|
| 369 |
+
}
|
| 370 |
+
set px [file join $p site-tcl]
|
| 371 |
+
if {![interp issafe]} {set px [file normalize $px]}
|
| 372 |
+
path add $px
|
| 373 |
+
}
|
| 374 |
+
return
|
| 375 |
+
}
|
| 376 |
+
|
| 377 |
+
# Initialization. Set up the default paths, then insert the new handler into
|
| 378 |
+
# the chain.
|
| 379 |
+
|
| 380 |
+
if {![interp issafe]} {::tcl::tm::Defaults}
|
lib/tcl8.6/word.tcl
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# word.tcl --
|
| 2 |
+
#
|
| 3 |
+
# This file defines various procedures for computing word boundaries in
|
| 4 |
+
# strings. This file is primarily needed so Tk text and entry widgets behave
|
| 5 |
+
# properly for different platforms.
|
| 6 |
+
#
|
| 7 |
+
# Copyright (c) 1996 Sun Microsystems, Inc.
|
| 8 |
+
# Copyright (c) 1998 Scriptics Corporation.
|
| 9 |
+
#
|
| 10 |
+
# See the file "license.terms" for information on usage and redistribution
|
| 11 |
+
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
| 12 |
+
|
| 13 |
+
# The following variables are used to determine which characters are
|
| 14 |
+
# interpreted as white space.
|
| 15 |
+
|
| 16 |
+
if {$::tcl_platform(platform) eq "windows"} {
|
| 17 |
+
# Windows style - any but a Unicode space char
|
| 18 |
+
if {![info exists ::tcl_wordchars]} {
|
| 19 |
+
set ::tcl_wordchars {\S}
|
| 20 |
+
}
|
| 21 |
+
if {![info exists ::tcl_nonwordchars]} {
|
| 22 |
+
set ::tcl_nonwordchars {\s}
|
| 23 |
+
}
|
| 24 |
+
} else {
|
| 25 |
+
# Motif style - any Unicode word char (number, letter, or underscore)
|
| 26 |
+
if {![info exists ::tcl_wordchars]} {
|
| 27 |
+
set ::tcl_wordchars {\w}
|
| 28 |
+
}
|
| 29 |
+
if {![info exists ::tcl_nonwordchars]} {
|
| 30 |
+
set ::tcl_nonwordchars {\W}
|
| 31 |
+
}
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
# Arrange for caches of the real matcher REs to be kept, which enables the REs
|
| 35 |
+
# themselves to be cached for greater performance (and somewhat greater
|
| 36 |
+
# clarity too).
|
| 37 |
+
|
| 38 |
+
namespace eval ::tcl {
|
| 39 |
+
variable WordBreakRE
|
| 40 |
+
array set WordBreakRE {}
|
| 41 |
+
|
| 42 |
+
proc UpdateWordBreakREs args {
|
| 43 |
+
# Ignores the arguments
|
| 44 |
+
global tcl_wordchars tcl_nonwordchars
|
| 45 |
+
variable WordBreakRE
|
| 46 |
+
|
| 47 |
+
# To keep the RE strings short...
|
| 48 |
+
set letter $tcl_wordchars
|
| 49 |
+
set space $tcl_nonwordchars
|
| 50 |
+
|
| 51 |
+
set WordBreakRE(after) "$letter$space|$space$letter"
|
| 52 |
+
set WordBreakRE(before) "^.*($letter$space|$space$letter)"
|
| 53 |
+
set WordBreakRE(end) "$space*$letter+$space"
|
| 54 |
+
set WordBreakRE(next) "$letter*$space+$letter"
|
| 55 |
+
set WordBreakRE(previous) "$space*($letter+)$space*\$"
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
# Initialize the cache
|
| 59 |
+
UpdateWordBreakREs
|
| 60 |
+
trace add variable ::tcl_wordchars write ::tcl::UpdateWordBreakREs
|
| 61 |
+
trace add variable ::tcl_nonwordchars write ::tcl::UpdateWordBreakREs
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
# tcl_wordBreakAfter --
|
| 65 |
+
#
|
| 66 |
+
# This procedure returns the index of the first word boundary after the
|
| 67 |
+
# starting point in the given string, or -1 if there are no more boundaries in
|
| 68 |
+
# the given string. The index returned refers to the first character of the
|
| 69 |
+
# pair that comprises a boundary.
|
| 70 |
+
#
|
| 71 |
+
# Arguments:
|
| 72 |
+
# str - String to search.
|
| 73 |
+
# start - Index into string specifying starting point.
|
| 74 |
+
|
| 75 |
+
proc tcl_wordBreakAfter {str start} {
|
| 76 |
+
variable ::tcl::WordBreakRE
|
| 77 |
+
set result {-1 -1}
|
| 78 |
+
regexp -indices -start $start -- $WordBreakRE(after) $str result
|
| 79 |
+
return [lindex $result 1]
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
# tcl_wordBreakBefore --
|
| 83 |
+
#
|
| 84 |
+
# This procedure returns the index of the first word boundary before the
|
| 85 |
+
# starting point in the given string, or -1 if there are no more boundaries in
|
| 86 |
+
# the given string. The index returned refers to the second character of the
|
| 87 |
+
# pair that comprises a boundary.
|
| 88 |
+
#
|
| 89 |
+
# Arguments:
|
| 90 |
+
# str - String to search.
|
| 91 |
+
# start - Index into string specifying starting point.
|
| 92 |
+
|
| 93 |
+
proc tcl_wordBreakBefore {str start} {
|
| 94 |
+
variable ::tcl::WordBreakRE
|
| 95 |
+
set result {-1 -1}
|
| 96 |
+
regexp -indices -- $WordBreakRE(before) [string range $str 0 $start] result
|
| 97 |
+
return [lindex $result 1]
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
# tcl_endOfWord --
|
| 101 |
+
#
|
| 102 |
+
# This procedure returns the index of the first end-of-word location after a
|
| 103 |
+
# starting index in the given string. An end-of-word location is defined to be
|
| 104 |
+
# the first whitespace character following the first non-whitespace character
|
| 105 |
+
# after the starting point. Returns -1 if there are no more words after the
|
| 106 |
+
# starting point.
|
| 107 |
+
#
|
| 108 |
+
# Arguments:
|
| 109 |
+
# str - String to search.
|
| 110 |
+
# start - Index into string specifying starting point.
|
| 111 |
+
|
| 112 |
+
proc tcl_endOfWord {str start} {
|
| 113 |
+
variable ::tcl::WordBreakRE
|
| 114 |
+
set result {-1 -1}
|
| 115 |
+
regexp -indices -start $start -- $WordBreakRE(end) $str result
|
| 116 |
+
return [lindex $result 1]
|
| 117 |
+
}
|
| 118 |
+
|
| 119 |
+
# tcl_startOfNextWord --
|
| 120 |
+
#
|
| 121 |
+
# This procedure returns the index of the first start-of-word location after a
|
| 122 |
+
# starting index in the given string. A start-of-word location is defined to
|
| 123 |
+
# be a non-whitespace character following a whitespace character. Returns -1
|
| 124 |
+
# if there are no more start-of-word locations after the starting point.
|
| 125 |
+
#
|
| 126 |
+
# Arguments:
|
| 127 |
+
# str - String to search.
|
| 128 |
+
# start - Index into string specifying starting point.
|
| 129 |
+
|
| 130 |
+
proc tcl_startOfNextWord {str start} {
|
| 131 |
+
variable ::tcl::WordBreakRE
|
| 132 |
+
set result {-1 -1}
|
| 133 |
+
regexp -indices -start $start -- $WordBreakRE(next) $str result
|
| 134 |
+
return [lindex $result 1]
|
| 135 |
+
}
|
| 136 |
+
|
| 137 |
+
# tcl_startOfPreviousWord --
|
| 138 |
+
#
|
| 139 |
+
# This procedure returns the index of the first start-of-word location before
|
| 140 |
+
# a starting index in the given string.
|
| 141 |
+
#
|
| 142 |
+
# Arguments:
|
| 143 |
+
# str - String to search.
|
| 144 |
+
# start - Index into string specifying starting point.
|
| 145 |
+
|
| 146 |
+
proc tcl_startOfPreviousWord {str start} {
|
| 147 |
+
variable ::tcl::WordBreakRE
|
| 148 |
+
set word {-1 -1}
|
| 149 |
+
if {$start > 0} {
|
| 150 |
+
regexp -indices -- $WordBreakRE(previous) [string range [string range $str 0 $start] 0 end-1] \
|
| 151 |
+
result word
|
| 152 |
+
}
|
| 153 |
+
return [lindex $word 0]
|
| 154 |
+
}
|
lib/tdbc1.1.7/libtdbc1.1.7.so
ADDED
|
Binary file (24.3 kB). View file
|
|
|
lib/tdbc1.1.7/libtdbcstub1.1.7.a
ADDED
|
Binary file (3.04 kB). View file
|
|
|
lib/tdbc1.1.7/pkgIndex.tcl
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- tcl -*-
|
| 2 |
+
# Tcl package index file, version 1.1
|
| 3 |
+
#
|
| 4 |
+
# Make sure that TDBC is running in a compatible version of Tcl, and
|
| 5 |
+
# that TclOO is available.
|
| 6 |
+
|
| 7 |
+
if {![package vsatisfies [package provide Tcl] 8.6-]} {
|
| 8 |
+
return
|
| 9 |
+
}
|
| 10 |
+
apply {{dir} {
|
| 11 |
+
set libraryfile [file join $dir tdbc.tcl]
|
| 12 |
+
if {![file exists $libraryfile] && [info exists ::env(TDBC_LIBRARY)]} {
|
| 13 |
+
set libraryfile [file join $::env(TDBC_LIBRARY) tdbc.tcl]
|
| 14 |
+
}
|
| 15 |
+
if {[package vsatisfies [package provide Tcl] 9.0-]} {
|
| 16 |
+
package ifneeded tdbc 1.1.7 \
|
| 17 |
+
"package require TclOO;\
|
| 18 |
+
[list load [file join $dir libtcl9tdbc1.1.7.so] [string totitle tdbc]]\;\
|
| 19 |
+
[list source $libraryfile]"
|
| 20 |
+
} else {
|
| 21 |
+
package ifneeded tdbc 1.1.7 \
|
| 22 |
+
"package require TclOO;\
|
| 23 |
+
[list load [file join $dir libtdbc1.1.7.so] [string totitle tdbc]]\;\
|
| 24 |
+
[list source $libraryfile]"
|
| 25 |
+
}
|
| 26 |
+
}} $dir
|