Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .gitattributes +1 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/collections/abc.py +2 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/distutils/cmd.py +403 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/distutils/config.py +130 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/distutils/dir_util.py +210 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/distutils/extension.py +240 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/distutils/log.py +77 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/distutils/msvc9compiler.py +789 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/distutils/sysconfig.py +554 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/distutils/text_file.py +286 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/HISTORY.txt +296 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/NEWS2x.txt +660 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/TODO.txt +210 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/__init__.py +10 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/autocomplete.py +223 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/autoexpand.py +96 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/calltip.py +205 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/codecontext.py +270 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/colorizer.py +340 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/config-extensions.def +62 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/config-highlight.def +105 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/config-keys.def +309 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/config-main.def +93 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/config.py +911 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/configdialog.py +0 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/debugger.py +550 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/debugger_r.py +393 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/debugobj.py +142 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/debugobj_r.py +41 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/delegator.py +33 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/editor.py +1672 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/extend.txt +83 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/filelist.py +131 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/format.py +426 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/grep.py +221 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/help.html +1014 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/help.py +294 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/help_about.py +207 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/history.py +106 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/hyperparser.py +312 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/idle.bat +4 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/idle.py +14 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/idle.pyw +17 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/macosx.py +287 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/multicall.py +448 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/outwin.py +187 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/parenmatch.py +183 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/pathbrowser.py +111 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/pyparse.py +593 -0
- my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/pyshell.py +1579 -0
.gitattributes
CHANGED
|
@@ -172,3 +172,4 @@ my_container_sandbox/workspace/anaconda3/pkgs/ca-certificates-2024.2.2-hbcca054_
|
|
| 172 |
my_container_sandbox/workspace/anaconda3/pkgs/ncurses-6.4-h6a678d5_0.conda filter=lfs diff=lfs merge=lfs -text
|
| 173 |
my_container_sandbox/workspace/anaconda3/pkgs/ca-certificates-2021.7.5-h06a4308_1.conda filter=lfs diff=lfs merge=lfs -text
|
| 174 |
my_container_sandbox/workspace/anaconda3/pkgs/conda-package-handling-2.2.0-pyh38be061_0.conda filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
| 172 |
my_container_sandbox/workspace/anaconda3/pkgs/ncurses-6.4-h6a678d5_0.conda filter=lfs diff=lfs merge=lfs -text
|
| 173 |
my_container_sandbox/workspace/anaconda3/pkgs/ca-certificates-2021.7.5-h06a4308_1.conda filter=lfs diff=lfs merge=lfs -text
|
| 174 |
my_container_sandbox/workspace/anaconda3/pkgs/conda-package-handling-2.2.0-pyh38be061_0.conda filter=lfs diff=lfs merge=lfs -text
|
| 175 |
+
my_container_sandbox/workspace/anaconda3/pkgs/sqlite-3.36.0-hc218d9a_0.conda filter=lfs diff=lfs merge=lfs -text
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/collections/abc.py
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from _collections_abc import *
|
| 2 |
+
from _collections_abc import __all__
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/distutils/cmd.py
ADDED
|
@@ -0,0 +1,403 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""distutils.cmd
|
| 2 |
+
|
| 3 |
+
Provides the Command class, the base class for the command classes
|
| 4 |
+
in the distutils.command package.
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
import sys, os, re
|
| 8 |
+
from distutils.errors import DistutilsOptionError
|
| 9 |
+
from distutils import util, dir_util, file_util, archive_util, dep_util
|
| 10 |
+
from distutils import log
|
| 11 |
+
|
| 12 |
+
class Command:
|
| 13 |
+
"""Abstract base class for defining command classes, the "worker bees"
|
| 14 |
+
of the Distutils. A useful analogy for command classes is to think of
|
| 15 |
+
them as subroutines with local variables called "options". The options
|
| 16 |
+
are "declared" in 'initialize_options()' and "defined" (given their
|
| 17 |
+
final values, aka "finalized") in 'finalize_options()', both of which
|
| 18 |
+
must be defined by every command class. The distinction between the
|
| 19 |
+
two is necessary because option values might come from the outside
|
| 20 |
+
world (command line, config file, ...), and any options dependent on
|
| 21 |
+
other options must be computed *after* these outside influences have
|
| 22 |
+
been processed -- hence 'finalize_options()'. The "body" of the
|
| 23 |
+
subroutine, where it does all its work based on the values of its
|
| 24 |
+
options, is the 'run()' method, which must also be implemented by every
|
| 25 |
+
command class.
|
| 26 |
+
"""
|
| 27 |
+
|
| 28 |
+
# 'sub_commands' formalizes the notion of a "family" of commands,
|
| 29 |
+
# eg. "install" as the parent with sub-commands "install_lib",
|
| 30 |
+
# "install_headers", etc. The parent of a family of commands
|
| 31 |
+
# defines 'sub_commands' as a class attribute; it's a list of
|
| 32 |
+
# (command_name : string, predicate : unbound_method | string | None)
|
| 33 |
+
# tuples, where 'predicate' is a method of the parent command that
|
| 34 |
+
# determines whether the corresponding command is applicable in the
|
| 35 |
+
# current situation. (Eg. we "install_headers" is only applicable if
|
| 36 |
+
# we have any C header files to install.) If 'predicate' is None,
|
| 37 |
+
# that command is always applicable.
|
| 38 |
+
#
|
| 39 |
+
# 'sub_commands' is usually defined at the *end* of a class, because
|
| 40 |
+
# predicates can be unbound methods, so they must already have been
|
| 41 |
+
# defined. The canonical example is the "install" command.
|
| 42 |
+
sub_commands = []
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
# -- Creation/initialization methods -------------------------------
|
| 46 |
+
|
| 47 |
+
def __init__(self, dist):
|
| 48 |
+
"""Create and initialize a new Command object. Most importantly,
|
| 49 |
+
invokes the 'initialize_options()' method, which is the real
|
| 50 |
+
initializer and depends on the actual command being
|
| 51 |
+
instantiated.
|
| 52 |
+
"""
|
| 53 |
+
# late import because of mutual dependence between these classes
|
| 54 |
+
from distutils.dist import Distribution
|
| 55 |
+
|
| 56 |
+
if not isinstance(dist, Distribution):
|
| 57 |
+
raise TypeError("dist must be a Distribution instance")
|
| 58 |
+
if self.__class__ is Command:
|
| 59 |
+
raise RuntimeError("Command is an abstract class")
|
| 60 |
+
|
| 61 |
+
self.distribution = dist
|
| 62 |
+
self.initialize_options()
|
| 63 |
+
|
| 64 |
+
# Per-command versions of the global flags, so that the user can
|
| 65 |
+
# customize Distutils' behaviour command-by-command and let some
|
| 66 |
+
# commands fall back on the Distribution's behaviour. None means
|
| 67 |
+
# "not defined, check self.distribution's copy", while 0 or 1 mean
|
| 68 |
+
# false and true (duh). Note that this means figuring out the real
|
| 69 |
+
# value of each flag is a touch complicated -- hence "self._dry_run"
|
| 70 |
+
# will be handled by __getattr__, below.
|
| 71 |
+
# XXX This needs to be fixed.
|
| 72 |
+
self._dry_run = None
|
| 73 |
+
|
| 74 |
+
# verbose is largely ignored, but needs to be set for
|
| 75 |
+
# backwards compatibility (I think)?
|
| 76 |
+
self.verbose = dist.verbose
|
| 77 |
+
|
| 78 |
+
# Some commands define a 'self.force' option to ignore file
|
| 79 |
+
# timestamps, but methods defined *here* assume that
|
| 80 |
+
# 'self.force' exists for all commands. So define it here
|
| 81 |
+
# just to be safe.
|
| 82 |
+
self.force = None
|
| 83 |
+
|
| 84 |
+
# The 'help' flag is just used for command-line parsing, so
|
| 85 |
+
# none of that complicated bureaucracy is needed.
|
| 86 |
+
self.help = 0
|
| 87 |
+
|
| 88 |
+
# 'finalized' records whether or not 'finalize_options()' has been
|
| 89 |
+
# called. 'finalize_options()' itself should not pay attention to
|
| 90 |
+
# this flag: it is the business of 'ensure_finalized()', which
|
| 91 |
+
# always calls 'finalize_options()', to respect/update it.
|
| 92 |
+
self.finalized = 0
|
| 93 |
+
|
| 94 |
+
# XXX A more explicit way to customize dry_run would be better.
|
| 95 |
+
def __getattr__(self, attr):
|
| 96 |
+
if attr == 'dry_run':
|
| 97 |
+
myval = getattr(self, "_" + attr)
|
| 98 |
+
if myval is None:
|
| 99 |
+
return getattr(self.distribution, attr)
|
| 100 |
+
else:
|
| 101 |
+
return myval
|
| 102 |
+
else:
|
| 103 |
+
raise AttributeError(attr)
|
| 104 |
+
|
| 105 |
+
def ensure_finalized(self):
|
| 106 |
+
if not self.finalized:
|
| 107 |
+
self.finalize_options()
|
| 108 |
+
self.finalized = 1
|
| 109 |
+
|
| 110 |
+
# Subclasses must define:
|
| 111 |
+
# initialize_options()
|
| 112 |
+
# provide default values for all options; may be customized by
|
| 113 |
+
# setup script, by options from config file(s), or by command-line
|
| 114 |
+
# options
|
| 115 |
+
# finalize_options()
|
| 116 |
+
# decide on the final values for all options; this is called
|
| 117 |
+
# after all possible intervention from the outside world
|
| 118 |
+
# (command-line, option file, etc.) has been processed
|
| 119 |
+
# run()
|
| 120 |
+
# run the command: do whatever it is we're here to do,
|
| 121 |
+
# controlled by the command's various option values
|
| 122 |
+
|
| 123 |
+
def initialize_options(self):
|
| 124 |
+
"""Set default values for all the options that this command
|
| 125 |
+
supports. Note that these defaults may be overridden by other
|
| 126 |
+
commands, by the setup script, by config files, or by the
|
| 127 |
+
command-line. Thus, this is not the place to code dependencies
|
| 128 |
+
between options; generally, 'initialize_options()' implementations
|
| 129 |
+
are just a bunch of "self.foo = None" assignments.
|
| 130 |
+
|
| 131 |
+
This method must be implemented by all command classes.
|
| 132 |
+
"""
|
| 133 |
+
raise RuntimeError("abstract method -- subclass %s must override"
|
| 134 |
+
% self.__class__)
|
| 135 |
+
|
| 136 |
+
def finalize_options(self):
|
| 137 |
+
"""Set final values for all the options that this command supports.
|
| 138 |
+
This is always called as late as possible, ie. after any option
|
| 139 |
+
assignments from the command-line or from other commands have been
|
| 140 |
+
done. Thus, this is the place to code option dependencies: if
|
| 141 |
+
'foo' depends on 'bar', then it is safe to set 'foo' from 'bar' as
|
| 142 |
+
long as 'foo' still has the same value it was assigned in
|
| 143 |
+
'initialize_options()'.
|
| 144 |
+
|
| 145 |
+
This method must be implemented by all command classes.
|
| 146 |
+
"""
|
| 147 |
+
raise RuntimeError("abstract method -- subclass %s must override"
|
| 148 |
+
% self.__class__)
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
def dump_options(self, header=None, indent=""):
|
| 152 |
+
from distutils.fancy_getopt import longopt_xlate
|
| 153 |
+
if header is None:
|
| 154 |
+
header = "command options for '%s':" % self.get_command_name()
|
| 155 |
+
self.announce(indent + header, level=log.INFO)
|
| 156 |
+
indent = indent + " "
|
| 157 |
+
for (option, _, _) in self.user_options:
|
| 158 |
+
option = option.translate(longopt_xlate)
|
| 159 |
+
if option[-1] == "=":
|
| 160 |
+
option = option[:-1]
|
| 161 |
+
value = getattr(self, option)
|
| 162 |
+
self.announce(indent + "%s = %s" % (option, value),
|
| 163 |
+
level=log.INFO)
|
| 164 |
+
|
| 165 |
+
def run(self):
|
| 166 |
+
"""A command's raison d'etre: carry out the action it exists to
|
| 167 |
+
perform, controlled by the options initialized in
|
| 168 |
+
'initialize_options()', customized by other commands, the setup
|
| 169 |
+
script, the command-line, and config files, and finalized in
|
| 170 |
+
'finalize_options()'. All terminal output and filesystem
|
| 171 |
+
interaction should be done by 'run()'.
|
| 172 |
+
|
| 173 |
+
This method must be implemented by all command classes.
|
| 174 |
+
"""
|
| 175 |
+
raise RuntimeError("abstract method -- subclass %s must override"
|
| 176 |
+
% self.__class__)
|
| 177 |
+
|
| 178 |
+
def announce(self, msg, level=1):
|
| 179 |
+
"""If the current verbosity level is of greater than or equal to
|
| 180 |
+
'level' print 'msg' to stdout.
|
| 181 |
+
"""
|
| 182 |
+
log.log(level, msg)
|
| 183 |
+
|
| 184 |
+
def debug_print(self, msg):
|
| 185 |
+
"""Print 'msg' to stdout if the global DEBUG (taken from the
|
| 186 |
+
DISTUTILS_DEBUG environment variable) flag is true.
|
| 187 |
+
"""
|
| 188 |
+
from distutils.debug import DEBUG
|
| 189 |
+
if DEBUG:
|
| 190 |
+
print(msg)
|
| 191 |
+
sys.stdout.flush()
|
| 192 |
+
|
| 193 |
+
|
| 194 |
+
# -- Option validation methods -------------------------------------
|
| 195 |
+
# (these are very handy in writing the 'finalize_options()' method)
|
| 196 |
+
#
|
| 197 |
+
# NB. the general philosophy here is to ensure that a particular option
|
| 198 |
+
# value meets certain type and value constraints. If not, we try to
|
| 199 |
+
# force it into conformance (eg. if we expect a list but have a string,
|
| 200 |
+
# split the string on comma and/or whitespace). If we can't force the
|
| 201 |
+
# option into conformance, raise DistutilsOptionError. Thus, command
|
| 202 |
+
# classes need do nothing more than (eg.)
|
| 203 |
+
# self.ensure_string_list('foo')
|
| 204 |
+
# and they can be guaranteed that thereafter, self.foo will be
|
| 205 |
+
# a list of strings.
|
| 206 |
+
|
| 207 |
+
def _ensure_stringlike(self, option, what, default=None):
|
| 208 |
+
val = getattr(self, option)
|
| 209 |
+
if val is None:
|
| 210 |
+
setattr(self, option, default)
|
| 211 |
+
return default
|
| 212 |
+
elif not isinstance(val, str):
|
| 213 |
+
raise DistutilsOptionError("'%s' must be a %s (got `%s`)"
|
| 214 |
+
% (option, what, val))
|
| 215 |
+
return val
|
| 216 |
+
|
| 217 |
+
def ensure_string(self, option, default=None):
|
| 218 |
+
"""Ensure that 'option' is a string; if not defined, set it to
|
| 219 |
+
'default'.
|
| 220 |
+
"""
|
| 221 |
+
self._ensure_stringlike(option, "string", default)
|
| 222 |
+
|
| 223 |
+
def ensure_string_list(self, option):
|
| 224 |
+
r"""Ensure that 'option' is a list of strings. If 'option' is
|
| 225 |
+
currently a string, we split it either on /,\s*/ or /\s+/, so
|
| 226 |
+
"foo bar baz", "foo,bar,baz", and "foo, bar baz" all become
|
| 227 |
+
["foo", "bar", "baz"].
|
| 228 |
+
"""
|
| 229 |
+
val = getattr(self, option)
|
| 230 |
+
if val is None:
|
| 231 |
+
return
|
| 232 |
+
elif isinstance(val, str):
|
| 233 |
+
setattr(self, option, re.split(r',\s*|\s+', val))
|
| 234 |
+
else:
|
| 235 |
+
if isinstance(val, list):
|
| 236 |
+
ok = all(isinstance(v, str) for v in val)
|
| 237 |
+
else:
|
| 238 |
+
ok = False
|
| 239 |
+
if not ok:
|
| 240 |
+
raise DistutilsOptionError(
|
| 241 |
+
"'%s' must be a list of strings (got %r)"
|
| 242 |
+
% (option, val))
|
| 243 |
+
|
| 244 |
+
def _ensure_tested_string(self, option, tester, what, error_fmt,
|
| 245 |
+
default=None):
|
| 246 |
+
val = self._ensure_stringlike(option, what, default)
|
| 247 |
+
if val is not None and not tester(val):
|
| 248 |
+
raise DistutilsOptionError(("error in '%s' option: " + error_fmt)
|
| 249 |
+
% (option, val))
|
| 250 |
+
|
| 251 |
+
def ensure_filename(self, option):
|
| 252 |
+
"""Ensure that 'option' is the name of an existing file."""
|
| 253 |
+
self._ensure_tested_string(option, os.path.isfile,
|
| 254 |
+
"filename",
|
| 255 |
+
"'%s' does not exist or is not a file")
|
| 256 |
+
|
| 257 |
+
def ensure_dirname(self, option):
|
| 258 |
+
self._ensure_tested_string(option, os.path.isdir,
|
| 259 |
+
"directory name",
|
| 260 |
+
"'%s' does not exist or is not a directory")
|
| 261 |
+
|
| 262 |
+
|
| 263 |
+
# -- Convenience methods for commands ------------------------------
|
| 264 |
+
|
| 265 |
+
def get_command_name(self):
|
| 266 |
+
if hasattr(self, 'command_name'):
|
| 267 |
+
return self.command_name
|
| 268 |
+
else:
|
| 269 |
+
return self.__class__.__name__
|
| 270 |
+
|
| 271 |
+
def set_undefined_options(self, src_cmd, *option_pairs):
|
| 272 |
+
"""Set the values of any "undefined" options from corresponding
|
| 273 |
+
option values in some other command object. "Undefined" here means
|
| 274 |
+
"is None", which is the convention used to indicate that an option
|
| 275 |
+
has not been changed between 'initialize_options()' and
|
| 276 |
+
'finalize_options()'. Usually called from 'finalize_options()' for
|
| 277 |
+
options that depend on some other command rather than another
|
| 278 |
+
option of the same command. 'src_cmd' is the other command from
|
| 279 |
+
which option values will be taken (a command object will be created
|
| 280 |
+
for it if necessary); the remaining arguments are
|
| 281 |
+
'(src_option,dst_option)' tuples which mean "take the value of
|
| 282 |
+
'src_option' in the 'src_cmd' command object, and copy it to
|
| 283 |
+
'dst_option' in the current command object".
|
| 284 |
+
"""
|
| 285 |
+
# Option_pairs: list of (src_option, dst_option) tuples
|
| 286 |
+
src_cmd_obj = self.distribution.get_command_obj(src_cmd)
|
| 287 |
+
src_cmd_obj.ensure_finalized()
|
| 288 |
+
for (src_option, dst_option) in option_pairs:
|
| 289 |
+
if getattr(self, dst_option) is None:
|
| 290 |
+
setattr(self, dst_option, getattr(src_cmd_obj, src_option))
|
| 291 |
+
|
| 292 |
+
def get_finalized_command(self, command, create=1):
|
| 293 |
+
"""Wrapper around Distribution's 'get_command_obj()' method: find
|
| 294 |
+
(create if necessary and 'create' is true) the command object for
|
| 295 |
+
'command', call its 'ensure_finalized()' method, and return the
|
| 296 |
+
finalized command object.
|
| 297 |
+
"""
|
| 298 |
+
cmd_obj = self.distribution.get_command_obj(command, create)
|
| 299 |
+
cmd_obj.ensure_finalized()
|
| 300 |
+
return cmd_obj
|
| 301 |
+
|
| 302 |
+
# XXX rename to 'get_reinitialized_command()'? (should do the
|
| 303 |
+
# same in dist.py, if so)
|
| 304 |
+
def reinitialize_command(self, command, reinit_subcommands=0):
|
| 305 |
+
return self.distribution.reinitialize_command(command,
|
| 306 |
+
reinit_subcommands)
|
| 307 |
+
|
| 308 |
+
def run_command(self, command):
|
| 309 |
+
"""Run some other command: uses the 'run_command()' method of
|
| 310 |
+
Distribution, which creates and finalizes the command object if
|
| 311 |
+
necessary and then invokes its 'run()' method.
|
| 312 |
+
"""
|
| 313 |
+
self.distribution.run_command(command)
|
| 314 |
+
|
| 315 |
+
def get_sub_commands(self):
|
| 316 |
+
"""Determine the sub-commands that are relevant in the current
|
| 317 |
+
distribution (ie., that need to be run). This is based on the
|
| 318 |
+
'sub_commands' class attribute: each tuple in that list may include
|
| 319 |
+
a method that we call to determine if the subcommand needs to be
|
| 320 |
+
run for the current distribution. Return a list of command names.
|
| 321 |
+
"""
|
| 322 |
+
commands = []
|
| 323 |
+
for (cmd_name, method) in self.sub_commands:
|
| 324 |
+
if method is None or method(self):
|
| 325 |
+
commands.append(cmd_name)
|
| 326 |
+
return commands
|
| 327 |
+
|
| 328 |
+
|
| 329 |
+
# -- External world manipulation -----------------------------------
|
| 330 |
+
|
| 331 |
+
def warn(self, msg):
|
| 332 |
+
log.warn("warning: %s: %s\n", self.get_command_name(), msg)
|
| 333 |
+
|
| 334 |
+
def execute(self, func, args, msg=None, level=1):
|
| 335 |
+
util.execute(func, args, msg, dry_run=self.dry_run)
|
| 336 |
+
|
| 337 |
+
def mkpath(self, name, mode=0o777):
|
| 338 |
+
dir_util.mkpath(name, mode, dry_run=self.dry_run)
|
| 339 |
+
|
| 340 |
+
def copy_file(self, infile, outfile, preserve_mode=1, preserve_times=1,
|
| 341 |
+
link=None, level=1):
|
| 342 |
+
"""Copy a file respecting verbose, dry-run and force flags. (The
|
| 343 |
+
former two default to whatever is in the Distribution object, and
|
| 344 |
+
the latter defaults to false for commands that don't define it.)"""
|
| 345 |
+
return file_util.copy_file(infile, outfile, preserve_mode,
|
| 346 |
+
preserve_times, not self.force, link,
|
| 347 |
+
dry_run=self.dry_run)
|
| 348 |
+
|
| 349 |
+
def copy_tree(self, infile, outfile, preserve_mode=1, preserve_times=1,
|
| 350 |
+
preserve_symlinks=0, level=1):
|
| 351 |
+
"""Copy an entire directory tree respecting verbose, dry-run,
|
| 352 |
+
and force flags.
|
| 353 |
+
"""
|
| 354 |
+
return dir_util.copy_tree(infile, outfile, preserve_mode,
|
| 355 |
+
preserve_times, preserve_symlinks,
|
| 356 |
+
not self.force, dry_run=self.dry_run)
|
| 357 |
+
|
| 358 |
+
def move_file (self, src, dst, level=1):
|
| 359 |
+
"""Move a file respecting dry-run flag."""
|
| 360 |
+
return file_util.move_file(src, dst, dry_run=self.dry_run)
|
| 361 |
+
|
| 362 |
+
def spawn(self, cmd, search_path=1, level=1):
|
| 363 |
+
"""Spawn an external command respecting dry-run flag."""
|
| 364 |
+
from distutils.spawn import spawn
|
| 365 |
+
spawn(cmd, search_path, dry_run=self.dry_run)
|
| 366 |
+
|
| 367 |
+
def make_archive(self, base_name, format, root_dir=None, base_dir=None,
|
| 368 |
+
owner=None, group=None):
|
| 369 |
+
return archive_util.make_archive(base_name, format, root_dir, base_dir,
|
| 370 |
+
dry_run=self.dry_run,
|
| 371 |
+
owner=owner, group=group)
|
| 372 |
+
|
| 373 |
+
def make_file(self, infiles, outfile, func, args,
|
| 374 |
+
exec_msg=None, skip_msg=None, level=1):
|
| 375 |
+
"""Special case of 'execute()' for operations that process one or
|
| 376 |
+
more input files and generate one output file. Works just like
|
| 377 |
+
'execute()', except the operation is skipped and a different
|
| 378 |
+
message printed if 'outfile' already exists and is newer than all
|
| 379 |
+
files listed in 'infiles'. If the command defined 'self.force',
|
| 380 |
+
and it is true, then the command is unconditionally run -- does no
|
| 381 |
+
timestamp checks.
|
| 382 |
+
"""
|
| 383 |
+
if skip_msg is None:
|
| 384 |
+
skip_msg = "skipping %s (inputs unchanged)" % outfile
|
| 385 |
+
|
| 386 |
+
# Allow 'infiles' to be a single string
|
| 387 |
+
if isinstance(infiles, str):
|
| 388 |
+
infiles = (infiles,)
|
| 389 |
+
elif not isinstance(infiles, (list, tuple)):
|
| 390 |
+
raise TypeError(
|
| 391 |
+
"'infiles' must be a string, or a list or tuple of strings")
|
| 392 |
+
|
| 393 |
+
if exec_msg is None:
|
| 394 |
+
exec_msg = "generating %s from %s" % (outfile, ', '.join(infiles))
|
| 395 |
+
|
| 396 |
+
# If 'outfile' must be regenerated (either because it doesn't
|
| 397 |
+
# exist, is out-of-date, or the 'force' flag is true) then
|
| 398 |
+
# perform the action that presumably regenerates it
|
| 399 |
+
if self.force or dep_util.newer_group(infiles, outfile):
|
| 400 |
+
self.execute(func, args, exec_msg, level)
|
| 401 |
+
# Otherwise, print the "skip" message
|
| 402 |
+
else:
|
| 403 |
+
log.debug(skip_msg)
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/distutils/config.py
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""distutils.pypirc
|
| 2 |
+
|
| 3 |
+
Provides the PyPIRCCommand class, the base class for the command classes
|
| 4 |
+
that uses .pypirc in the distutils.command package.
|
| 5 |
+
"""
|
| 6 |
+
import os
|
| 7 |
+
from configparser import RawConfigParser
|
| 8 |
+
|
| 9 |
+
from distutils.cmd import Command
|
| 10 |
+
|
| 11 |
+
DEFAULT_PYPIRC = """\
|
| 12 |
+
[distutils]
|
| 13 |
+
index-servers =
|
| 14 |
+
pypi
|
| 15 |
+
|
| 16 |
+
[pypi]
|
| 17 |
+
username:%s
|
| 18 |
+
password:%s
|
| 19 |
+
"""
|
| 20 |
+
|
| 21 |
+
class PyPIRCCommand(Command):
|
| 22 |
+
"""Base command that knows how to handle the .pypirc file
|
| 23 |
+
"""
|
| 24 |
+
DEFAULT_REPOSITORY = 'https://upload.pypi.org/legacy/'
|
| 25 |
+
DEFAULT_REALM = 'pypi'
|
| 26 |
+
repository = None
|
| 27 |
+
realm = None
|
| 28 |
+
|
| 29 |
+
user_options = [
|
| 30 |
+
('repository=', 'r',
|
| 31 |
+
"url of repository [default: %s]" % \
|
| 32 |
+
DEFAULT_REPOSITORY),
|
| 33 |
+
('show-response', None,
|
| 34 |
+
'display full response text from server')]
|
| 35 |
+
|
| 36 |
+
boolean_options = ['show-response']
|
| 37 |
+
|
| 38 |
+
def _get_rc_file(self):
|
| 39 |
+
"""Returns rc file path."""
|
| 40 |
+
return os.path.join(os.path.expanduser('~'), '.pypirc')
|
| 41 |
+
|
| 42 |
+
def _store_pypirc(self, username, password):
|
| 43 |
+
"""Creates a default .pypirc file."""
|
| 44 |
+
rc = self._get_rc_file()
|
| 45 |
+
with os.fdopen(os.open(rc, os.O_CREAT | os.O_WRONLY, 0o600), 'w') as f:
|
| 46 |
+
f.write(DEFAULT_PYPIRC % (username, password))
|
| 47 |
+
|
| 48 |
+
def _read_pypirc(self):
|
| 49 |
+
"""Reads the .pypirc file."""
|
| 50 |
+
rc = self._get_rc_file()
|
| 51 |
+
if os.path.exists(rc):
|
| 52 |
+
self.announce('Using PyPI login from %s' % rc)
|
| 53 |
+
repository = self.repository or self.DEFAULT_REPOSITORY
|
| 54 |
+
|
| 55 |
+
config = RawConfigParser()
|
| 56 |
+
config.read(rc)
|
| 57 |
+
sections = config.sections()
|
| 58 |
+
if 'distutils' in sections:
|
| 59 |
+
# let's get the list of servers
|
| 60 |
+
index_servers = config.get('distutils', 'index-servers')
|
| 61 |
+
_servers = [server.strip() for server in
|
| 62 |
+
index_servers.split('\n')
|
| 63 |
+
if server.strip() != '']
|
| 64 |
+
if _servers == []:
|
| 65 |
+
# nothing set, let's try to get the default pypi
|
| 66 |
+
if 'pypi' in sections:
|
| 67 |
+
_servers = ['pypi']
|
| 68 |
+
else:
|
| 69 |
+
# the file is not properly defined, returning
|
| 70 |
+
# an empty dict
|
| 71 |
+
return {}
|
| 72 |
+
for server in _servers:
|
| 73 |
+
current = {'server': server}
|
| 74 |
+
current['username'] = config.get(server, 'username')
|
| 75 |
+
|
| 76 |
+
# optional params
|
| 77 |
+
for key, default in (('repository',
|
| 78 |
+
self.DEFAULT_REPOSITORY),
|
| 79 |
+
('realm', self.DEFAULT_REALM),
|
| 80 |
+
('password', None)):
|
| 81 |
+
if config.has_option(server, key):
|
| 82 |
+
current[key] = config.get(server, key)
|
| 83 |
+
else:
|
| 84 |
+
current[key] = default
|
| 85 |
+
|
| 86 |
+
# work around people having "repository" for the "pypi"
|
| 87 |
+
# section of their config set to the HTTP (rather than
|
| 88 |
+
# HTTPS) URL
|
| 89 |
+
if (server == 'pypi' and
|
| 90 |
+
repository in (self.DEFAULT_REPOSITORY, 'pypi')):
|
| 91 |
+
current['repository'] = self.DEFAULT_REPOSITORY
|
| 92 |
+
return current
|
| 93 |
+
|
| 94 |
+
if (current['server'] == repository or
|
| 95 |
+
current['repository'] == repository):
|
| 96 |
+
return current
|
| 97 |
+
elif 'server-login' in sections:
|
| 98 |
+
# old format
|
| 99 |
+
server = 'server-login'
|
| 100 |
+
if config.has_option(server, 'repository'):
|
| 101 |
+
repository = config.get(server, 'repository')
|
| 102 |
+
else:
|
| 103 |
+
repository = self.DEFAULT_REPOSITORY
|
| 104 |
+
return {'username': config.get(server, 'username'),
|
| 105 |
+
'password': config.get(server, 'password'),
|
| 106 |
+
'repository': repository,
|
| 107 |
+
'server': server,
|
| 108 |
+
'realm': self.DEFAULT_REALM}
|
| 109 |
+
|
| 110 |
+
return {}
|
| 111 |
+
|
| 112 |
+
def _read_pypi_response(self, response):
|
| 113 |
+
"""Read and decode a PyPI HTTP response."""
|
| 114 |
+
import cgi
|
| 115 |
+
content_type = response.getheader('content-type', 'text/plain')
|
| 116 |
+
encoding = cgi.parse_header(content_type)[1].get('charset', 'ascii')
|
| 117 |
+
return response.read().decode(encoding)
|
| 118 |
+
|
| 119 |
+
def initialize_options(self):
|
| 120 |
+
"""Initialize options."""
|
| 121 |
+
self.repository = None
|
| 122 |
+
self.realm = None
|
| 123 |
+
self.show_response = 0
|
| 124 |
+
|
| 125 |
+
def finalize_options(self):
|
| 126 |
+
"""Finalizes options."""
|
| 127 |
+
if self.repository is None:
|
| 128 |
+
self.repository = self.DEFAULT_REPOSITORY
|
| 129 |
+
if self.realm is None:
|
| 130 |
+
self.realm = self.DEFAULT_REALM
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/distutils/dir_util.py
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""distutils.dir_util
|
| 2 |
+
|
| 3 |
+
Utility functions for manipulating directories and directory trees."""
|
| 4 |
+
|
| 5 |
+
import os
|
| 6 |
+
import errno
|
| 7 |
+
from distutils.errors import DistutilsFileError, DistutilsInternalError
|
| 8 |
+
from distutils import log
|
| 9 |
+
|
| 10 |
+
# cache for by mkpath() -- in addition to cheapening redundant calls,
|
| 11 |
+
# eliminates redundant "creating /foo/bar/baz" messages in dry-run mode
|
| 12 |
+
_path_created = {}
|
| 13 |
+
|
| 14 |
+
# I don't use os.makedirs because a) it's new to Python 1.5.2, and
|
| 15 |
+
# b) it blows up if the directory already exists (I want to silently
|
| 16 |
+
# succeed in that case).
|
| 17 |
+
def mkpath(name, mode=0o777, verbose=1, dry_run=0):
|
| 18 |
+
"""Create a directory and any missing ancestor directories.
|
| 19 |
+
|
| 20 |
+
If the directory already exists (or if 'name' is the empty string, which
|
| 21 |
+
means the current directory, which of course exists), then do nothing.
|
| 22 |
+
Raise DistutilsFileError if unable to create some directory along the way
|
| 23 |
+
(eg. some sub-path exists, but is a file rather than a directory).
|
| 24 |
+
If 'verbose' is true, print a one-line summary of each mkdir to stdout.
|
| 25 |
+
Return the list of directories actually created.
|
| 26 |
+
"""
|
| 27 |
+
|
| 28 |
+
global _path_created
|
| 29 |
+
|
| 30 |
+
# Detect a common bug -- name is None
|
| 31 |
+
if not isinstance(name, str):
|
| 32 |
+
raise DistutilsInternalError(
|
| 33 |
+
"mkpath: 'name' must be a string (got %r)" % (name,))
|
| 34 |
+
|
| 35 |
+
# XXX what's the better way to handle verbosity? print as we create
|
| 36 |
+
# each directory in the path (the current behaviour), or only announce
|
| 37 |
+
# the creation of the whole path? (quite easy to do the latter since
|
| 38 |
+
# we're not using a recursive algorithm)
|
| 39 |
+
|
| 40 |
+
name = os.path.normpath(name)
|
| 41 |
+
created_dirs = []
|
| 42 |
+
if os.path.isdir(name) or name == '':
|
| 43 |
+
return created_dirs
|
| 44 |
+
if _path_created.get(os.path.abspath(name)):
|
| 45 |
+
return created_dirs
|
| 46 |
+
|
| 47 |
+
(head, tail) = os.path.split(name)
|
| 48 |
+
tails = [tail] # stack of lone dirs to create
|
| 49 |
+
|
| 50 |
+
while head and tail and not os.path.isdir(head):
|
| 51 |
+
(head, tail) = os.path.split(head)
|
| 52 |
+
tails.insert(0, tail) # push next higher dir onto stack
|
| 53 |
+
|
| 54 |
+
# now 'head' contains the deepest directory that already exists
|
| 55 |
+
# (that is, the child of 'head' in 'name' is the highest directory
|
| 56 |
+
# that does *not* exist)
|
| 57 |
+
for d in tails:
|
| 58 |
+
#print "head = %s, d = %s: " % (head, d),
|
| 59 |
+
head = os.path.join(head, d)
|
| 60 |
+
abs_head = os.path.abspath(head)
|
| 61 |
+
|
| 62 |
+
if _path_created.get(abs_head):
|
| 63 |
+
continue
|
| 64 |
+
|
| 65 |
+
if verbose >= 1:
|
| 66 |
+
log.info("creating %s", head)
|
| 67 |
+
|
| 68 |
+
if not dry_run:
|
| 69 |
+
try:
|
| 70 |
+
os.mkdir(head, mode)
|
| 71 |
+
except OSError as exc:
|
| 72 |
+
if not (exc.errno == errno.EEXIST and os.path.isdir(head)):
|
| 73 |
+
raise DistutilsFileError(
|
| 74 |
+
"could not create '%s': %s" % (head, exc.args[-1]))
|
| 75 |
+
created_dirs.append(head)
|
| 76 |
+
|
| 77 |
+
_path_created[abs_head] = 1
|
| 78 |
+
return created_dirs
|
| 79 |
+
|
| 80 |
+
def create_tree(base_dir, files, mode=0o777, verbose=1, dry_run=0):
|
| 81 |
+
"""Create all the empty directories under 'base_dir' needed to put 'files'
|
| 82 |
+
there.
|
| 83 |
+
|
| 84 |
+
'base_dir' is just the name of a directory which doesn't necessarily
|
| 85 |
+
exist yet; 'files' is a list of filenames to be interpreted relative to
|
| 86 |
+
'base_dir'. 'base_dir' + the directory portion of every file in 'files'
|
| 87 |
+
will be created if it doesn't already exist. 'mode', 'verbose' and
|
| 88 |
+
'dry_run' flags are as for 'mkpath()'.
|
| 89 |
+
"""
|
| 90 |
+
# First get the list of directories to create
|
| 91 |
+
need_dir = set()
|
| 92 |
+
for file in files:
|
| 93 |
+
need_dir.add(os.path.join(base_dir, os.path.dirname(file)))
|
| 94 |
+
|
| 95 |
+
# Now create them
|
| 96 |
+
for dir in sorted(need_dir):
|
| 97 |
+
mkpath(dir, mode, verbose=verbose, dry_run=dry_run)
|
| 98 |
+
|
| 99 |
+
def copy_tree(src, dst, preserve_mode=1, preserve_times=1,
|
| 100 |
+
preserve_symlinks=0, update=0, verbose=1, dry_run=0):
|
| 101 |
+
"""Copy an entire directory tree 'src' to a new location 'dst'.
|
| 102 |
+
|
| 103 |
+
Both 'src' and 'dst' must be directory names. If 'src' is not a
|
| 104 |
+
directory, raise DistutilsFileError. If 'dst' does not exist, it is
|
| 105 |
+
created with 'mkpath()'. The end result of the copy is that every
|
| 106 |
+
file in 'src' is copied to 'dst', and directories under 'src' are
|
| 107 |
+
recursively copied to 'dst'. Return the list of files that were
|
| 108 |
+
copied or might have been copied, using their output name. The
|
| 109 |
+
return value is unaffected by 'update' or 'dry_run': it is simply
|
| 110 |
+
the list of all files under 'src', with the names changed to be
|
| 111 |
+
under 'dst'.
|
| 112 |
+
|
| 113 |
+
'preserve_mode' and 'preserve_times' are the same as for
|
| 114 |
+
'copy_file'; note that they only apply to regular files, not to
|
| 115 |
+
directories. If 'preserve_symlinks' is true, symlinks will be
|
| 116 |
+
copied as symlinks (on platforms that support them!); otherwise
|
| 117 |
+
(the default), the destination of the symlink will be copied.
|
| 118 |
+
'update' and 'verbose' are the same as for 'copy_file'.
|
| 119 |
+
"""
|
| 120 |
+
from distutils.file_util import copy_file
|
| 121 |
+
|
| 122 |
+
if not dry_run and not os.path.isdir(src):
|
| 123 |
+
raise DistutilsFileError(
|
| 124 |
+
"cannot copy tree '%s': not a directory" % src)
|
| 125 |
+
try:
|
| 126 |
+
names = os.listdir(src)
|
| 127 |
+
except OSError as e:
|
| 128 |
+
if dry_run:
|
| 129 |
+
names = []
|
| 130 |
+
else:
|
| 131 |
+
raise DistutilsFileError(
|
| 132 |
+
"error listing files in '%s': %s" % (src, e.strerror))
|
| 133 |
+
|
| 134 |
+
if not dry_run:
|
| 135 |
+
mkpath(dst, verbose=verbose)
|
| 136 |
+
|
| 137 |
+
outputs = []
|
| 138 |
+
|
| 139 |
+
for n in names:
|
| 140 |
+
src_name = os.path.join(src, n)
|
| 141 |
+
dst_name = os.path.join(dst, n)
|
| 142 |
+
|
| 143 |
+
if n.startswith('.nfs'):
|
| 144 |
+
# skip NFS rename files
|
| 145 |
+
continue
|
| 146 |
+
|
| 147 |
+
if preserve_symlinks and os.path.islink(src_name):
|
| 148 |
+
link_dest = os.readlink(src_name)
|
| 149 |
+
if verbose >= 1:
|
| 150 |
+
log.info("linking %s -> %s", dst_name, link_dest)
|
| 151 |
+
if not dry_run:
|
| 152 |
+
os.symlink(link_dest, dst_name)
|
| 153 |
+
outputs.append(dst_name)
|
| 154 |
+
|
| 155 |
+
elif os.path.isdir(src_name):
|
| 156 |
+
outputs.extend(
|
| 157 |
+
copy_tree(src_name, dst_name, preserve_mode,
|
| 158 |
+
preserve_times, preserve_symlinks, update,
|
| 159 |
+
verbose=verbose, dry_run=dry_run))
|
| 160 |
+
else:
|
| 161 |
+
copy_file(src_name, dst_name, preserve_mode,
|
| 162 |
+
preserve_times, update, verbose=verbose,
|
| 163 |
+
dry_run=dry_run)
|
| 164 |
+
outputs.append(dst_name)
|
| 165 |
+
|
| 166 |
+
return outputs
|
| 167 |
+
|
| 168 |
+
def _build_cmdtuple(path, cmdtuples):
|
| 169 |
+
"""Helper for remove_tree()."""
|
| 170 |
+
for f in os.listdir(path):
|
| 171 |
+
real_f = os.path.join(path,f)
|
| 172 |
+
if os.path.isdir(real_f) and not os.path.islink(real_f):
|
| 173 |
+
_build_cmdtuple(real_f, cmdtuples)
|
| 174 |
+
else:
|
| 175 |
+
cmdtuples.append((os.remove, real_f))
|
| 176 |
+
cmdtuples.append((os.rmdir, path))
|
| 177 |
+
|
| 178 |
+
def remove_tree(directory, verbose=1, dry_run=0):
|
| 179 |
+
"""Recursively remove an entire directory tree.
|
| 180 |
+
|
| 181 |
+
Any errors are ignored (apart from being reported to stdout if 'verbose'
|
| 182 |
+
is true).
|
| 183 |
+
"""
|
| 184 |
+
global _path_created
|
| 185 |
+
|
| 186 |
+
if verbose >= 1:
|
| 187 |
+
log.info("removing '%s' (and everything under it)", directory)
|
| 188 |
+
if dry_run:
|
| 189 |
+
return
|
| 190 |
+
cmdtuples = []
|
| 191 |
+
_build_cmdtuple(directory, cmdtuples)
|
| 192 |
+
for cmd in cmdtuples:
|
| 193 |
+
try:
|
| 194 |
+
cmd[0](cmd[1])
|
| 195 |
+
# remove dir from cache if it's already there
|
| 196 |
+
abspath = os.path.abspath(cmd[1])
|
| 197 |
+
if abspath in _path_created:
|
| 198 |
+
del _path_created[abspath]
|
| 199 |
+
except OSError as exc:
|
| 200 |
+
log.warn("error removing %s: %s", directory, exc)
|
| 201 |
+
|
| 202 |
+
def ensure_relative(path):
|
| 203 |
+
"""Take the full path 'path', and make it a relative path.
|
| 204 |
+
|
| 205 |
+
This is useful to make 'path' the second argument to os.path.join().
|
| 206 |
+
"""
|
| 207 |
+
drive, path = os.path.splitdrive(path)
|
| 208 |
+
if path[0:1] == os.sep:
|
| 209 |
+
path = drive + path[1:]
|
| 210 |
+
return path
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/distutils/extension.py
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""distutils.extension
|
| 2 |
+
|
| 3 |
+
Provides the Extension class, used to describe C/C++ extension
|
| 4 |
+
modules in setup scripts."""
|
| 5 |
+
|
| 6 |
+
import os
|
| 7 |
+
import warnings
|
| 8 |
+
|
| 9 |
+
# This class is really only used by the "build_ext" command, so it might
|
| 10 |
+
# make sense to put it in distutils.command.build_ext. However, that
|
| 11 |
+
# module is already big enough, and I want to make this class a bit more
|
| 12 |
+
# complex to simplify some common cases ("foo" module in "foo.c") and do
|
| 13 |
+
# better error-checking ("foo.c" actually exists).
|
| 14 |
+
#
|
| 15 |
+
# Also, putting this in build_ext.py means every setup script would have to
|
| 16 |
+
# import that large-ish module (indirectly, through distutils.core) in
|
| 17 |
+
# order to do anything.
|
| 18 |
+
|
| 19 |
+
class Extension:
|
| 20 |
+
"""Just a collection of attributes that describes an extension
|
| 21 |
+
module and everything needed to build it (hopefully in a portable
|
| 22 |
+
way, but there are hooks that let you be as unportable as you need).
|
| 23 |
+
|
| 24 |
+
Instance attributes:
|
| 25 |
+
name : string
|
| 26 |
+
the full name of the extension, including any packages -- ie.
|
| 27 |
+
*not* a filename or pathname, but Python dotted name
|
| 28 |
+
sources : [string]
|
| 29 |
+
list of source filenames, relative to the distribution root
|
| 30 |
+
(where the setup script lives), in Unix form (slash-separated)
|
| 31 |
+
for portability. Source files may be C, C++, SWIG (.i),
|
| 32 |
+
platform-specific resource files, or whatever else is recognized
|
| 33 |
+
by the "build_ext" command as source for a Python extension.
|
| 34 |
+
include_dirs : [string]
|
| 35 |
+
list of directories to search for C/C++ header files (in Unix
|
| 36 |
+
form for portability)
|
| 37 |
+
define_macros : [(name : string, value : string|None)]
|
| 38 |
+
list of macros to define; each macro is defined using a 2-tuple,
|
| 39 |
+
where 'value' is either the string to define it to or None to
|
| 40 |
+
define it without a particular value (equivalent of "#define
|
| 41 |
+
FOO" in source or -DFOO on Unix C compiler command line)
|
| 42 |
+
undef_macros : [string]
|
| 43 |
+
list of macros to undefine explicitly
|
| 44 |
+
library_dirs : [string]
|
| 45 |
+
list of directories to search for C/C++ libraries at link time
|
| 46 |
+
libraries : [string]
|
| 47 |
+
list of library names (not filenames or paths) to link against
|
| 48 |
+
runtime_library_dirs : [string]
|
| 49 |
+
list of directories to search for C/C++ libraries at run time
|
| 50 |
+
(for shared extensions, this is when the extension is loaded)
|
| 51 |
+
extra_objects : [string]
|
| 52 |
+
list of extra files to link with (eg. object files not implied
|
| 53 |
+
by 'sources', static library that must be explicitly specified,
|
| 54 |
+
binary resource files, etc.)
|
| 55 |
+
extra_compile_args : [string]
|
| 56 |
+
any extra platform- and compiler-specific information to use
|
| 57 |
+
when compiling the source files in 'sources'. For platforms and
|
| 58 |
+
compilers where "command line" makes sense, this is typically a
|
| 59 |
+
list of command-line arguments, but for other platforms it could
|
| 60 |
+
be anything.
|
| 61 |
+
extra_link_args : [string]
|
| 62 |
+
any extra platform- and compiler-specific information to use
|
| 63 |
+
when linking object files together to create the extension (or
|
| 64 |
+
to create a new static Python interpreter). Similar
|
| 65 |
+
interpretation as for 'extra_compile_args'.
|
| 66 |
+
export_symbols : [string]
|
| 67 |
+
list of symbols to be exported from a shared extension. Not
|
| 68 |
+
used on all platforms, and not generally necessary for Python
|
| 69 |
+
extensions, which typically export exactly one symbol: "init" +
|
| 70 |
+
extension_name.
|
| 71 |
+
swig_opts : [string]
|
| 72 |
+
any extra options to pass to SWIG if a source file has the .i
|
| 73 |
+
extension.
|
| 74 |
+
depends : [string]
|
| 75 |
+
list of files that the extension depends on
|
| 76 |
+
language : string
|
| 77 |
+
extension language (i.e. "c", "c++", "objc"). Will be detected
|
| 78 |
+
from the source extensions if not provided.
|
| 79 |
+
optional : boolean
|
| 80 |
+
specifies that a build failure in the extension should not abort the
|
| 81 |
+
build process, but simply not install the failing extension.
|
| 82 |
+
"""
|
| 83 |
+
|
| 84 |
+
# When adding arguments to this constructor, be sure to update
|
| 85 |
+
# setup_keywords in core.py.
|
| 86 |
+
def __init__(self, name, sources,
|
| 87 |
+
include_dirs=None,
|
| 88 |
+
define_macros=None,
|
| 89 |
+
undef_macros=None,
|
| 90 |
+
library_dirs=None,
|
| 91 |
+
libraries=None,
|
| 92 |
+
runtime_library_dirs=None,
|
| 93 |
+
extra_objects=None,
|
| 94 |
+
extra_compile_args=None,
|
| 95 |
+
extra_link_args=None,
|
| 96 |
+
export_symbols=None,
|
| 97 |
+
swig_opts = None,
|
| 98 |
+
depends=None,
|
| 99 |
+
language=None,
|
| 100 |
+
optional=None,
|
| 101 |
+
**kw # To catch unknown keywords
|
| 102 |
+
):
|
| 103 |
+
if not isinstance(name, str):
|
| 104 |
+
raise AssertionError("'name' must be a string")
|
| 105 |
+
if not (isinstance(sources, list) and
|
| 106 |
+
all(isinstance(v, str) for v in sources)):
|
| 107 |
+
raise AssertionError("'sources' must be a list of strings")
|
| 108 |
+
|
| 109 |
+
self.name = name
|
| 110 |
+
self.sources = sources
|
| 111 |
+
self.include_dirs = include_dirs or []
|
| 112 |
+
self.define_macros = define_macros or []
|
| 113 |
+
self.undef_macros = undef_macros or []
|
| 114 |
+
self.library_dirs = library_dirs or []
|
| 115 |
+
self.libraries = libraries or []
|
| 116 |
+
self.runtime_library_dirs = runtime_library_dirs or []
|
| 117 |
+
self.extra_objects = extra_objects or []
|
| 118 |
+
self.extra_compile_args = extra_compile_args or []
|
| 119 |
+
self.extra_link_args = extra_link_args or []
|
| 120 |
+
self.export_symbols = export_symbols or []
|
| 121 |
+
self.swig_opts = swig_opts or []
|
| 122 |
+
self.depends = depends or []
|
| 123 |
+
self.language = language
|
| 124 |
+
self.optional = optional
|
| 125 |
+
|
| 126 |
+
# If there are unknown keyword options, warn about them
|
| 127 |
+
if len(kw) > 0:
|
| 128 |
+
options = [repr(option) for option in kw]
|
| 129 |
+
options = ', '.join(sorted(options))
|
| 130 |
+
msg = "Unknown Extension options: %s" % options
|
| 131 |
+
warnings.warn(msg)
|
| 132 |
+
|
| 133 |
+
def __repr__(self):
|
| 134 |
+
return '<%s.%s(%r) at %#x>' % (
|
| 135 |
+
self.__class__.__module__,
|
| 136 |
+
self.__class__.__qualname__,
|
| 137 |
+
self.name,
|
| 138 |
+
id(self))
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
def read_setup_file(filename):
|
| 142 |
+
"""Reads a Setup file and returns Extension instances."""
|
| 143 |
+
from distutils.sysconfig import (parse_makefile, expand_makefile_vars,
|
| 144 |
+
_variable_rx)
|
| 145 |
+
|
| 146 |
+
from distutils.text_file import TextFile
|
| 147 |
+
from distutils.util import split_quoted
|
| 148 |
+
|
| 149 |
+
# First pass over the file to gather "VAR = VALUE" assignments.
|
| 150 |
+
vars = parse_makefile(filename)
|
| 151 |
+
|
| 152 |
+
# Second pass to gobble up the real content: lines of the form
|
| 153 |
+
# <module> ... [<sourcefile> ...] [<cpparg> ...] [<library> ...]
|
| 154 |
+
file = TextFile(filename,
|
| 155 |
+
strip_comments=1, skip_blanks=1, join_lines=1,
|
| 156 |
+
lstrip_ws=1, rstrip_ws=1)
|
| 157 |
+
try:
|
| 158 |
+
extensions = []
|
| 159 |
+
|
| 160 |
+
while True:
|
| 161 |
+
line = file.readline()
|
| 162 |
+
if line is None: # eof
|
| 163 |
+
break
|
| 164 |
+
if _variable_rx.match(line): # VAR=VALUE, handled in first pass
|
| 165 |
+
continue
|
| 166 |
+
|
| 167 |
+
if line[0] == line[-1] == "*":
|
| 168 |
+
file.warn("'%s' lines not handled yet" % line)
|
| 169 |
+
continue
|
| 170 |
+
|
| 171 |
+
line = expand_makefile_vars(line, vars)
|
| 172 |
+
words = split_quoted(line)
|
| 173 |
+
|
| 174 |
+
# NB. this parses a slightly different syntax than the old
|
| 175 |
+
# makesetup script: here, there must be exactly one extension per
|
| 176 |
+
# line, and it must be the first word of the line. I have no idea
|
| 177 |
+
# why the old syntax supported multiple extensions per line, as
|
| 178 |
+
# they all wind up being the same.
|
| 179 |
+
|
| 180 |
+
module = words[0]
|
| 181 |
+
ext = Extension(module, [])
|
| 182 |
+
append_next_word = None
|
| 183 |
+
|
| 184 |
+
for word in words[1:]:
|
| 185 |
+
if append_next_word is not None:
|
| 186 |
+
append_next_word.append(word)
|
| 187 |
+
append_next_word = None
|
| 188 |
+
continue
|
| 189 |
+
|
| 190 |
+
suffix = os.path.splitext(word)[1]
|
| 191 |
+
switch = word[0:2] ; value = word[2:]
|
| 192 |
+
|
| 193 |
+
if suffix in (".c", ".cc", ".cpp", ".cxx", ".c++", ".m", ".mm"):
|
| 194 |
+
# hmm, should we do something about C vs. C++ sources?
|
| 195 |
+
# or leave it up to the CCompiler implementation to
|
| 196 |
+
# worry about?
|
| 197 |
+
ext.sources.append(word)
|
| 198 |
+
elif switch == "-I":
|
| 199 |
+
ext.include_dirs.append(value)
|
| 200 |
+
elif switch == "-D":
|
| 201 |
+
equals = value.find("=")
|
| 202 |
+
if equals == -1: # bare "-DFOO" -- no value
|
| 203 |
+
ext.define_macros.append((value, None))
|
| 204 |
+
else: # "-DFOO=blah"
|
| 205 |
+
ext.define_macros.append((value[0:equals],
|
| 206 |
+
value[equals+2:]))
|
| 207 |
+
elif switch == "-U":
|
| 208 |
+
ext.undef_macros.append(value)
|
| 209 |
+
elif switch == "-C": # only here 'cause makesetup has it!
|
| 210 |
+
ext.extra_compile_args.append(word)
|
| 211 |
+
elif switch == "-l":
|
| 212 |
+
ext.libraries.append(value)
|
| 213 |
+
elif switch == "-L":
|
| 214 |
+
ext.library_dirs.append(value)
|
| 215 |
+
elif switch == "-R":
|
| 216 |
+
ext.runtime_library_dirs.append(value)
|
| 217 |
+
elif word == "-rpath":
|
| 218 |
+
append_next_word = ext.runtime_library_dirs
|
| 219 |
+
elif word == "-Xlinker":
|
| 220 |
+
append_next_word = ext.extra_link_args
|
| 221 |
+
elif word == "-Xcompiler":
|
| 222 |
+
append_next_word = ext.extra_compile_args
|
| 223 |
+
elif switch == "-u":
|
| 224 |
+
ext.extra_link_args.append(word)
|
| 225 |
+
if not value:
|
| 226 |
+
append_next_word = ext.extra_link_args
|
| 227 |
+
elif suffix in (".a", ".so", ".sl", ".o", ".dylib"):
|
| 228 |
+
# NB. a really faithful emulation of makesetup would
|
| 229 |
+
# append a .o file to extra_objects only if it
|
| 230 |
+
# had a slash in it; otherwise, it would s/.o/.c/
|
| 231 |
+
# and append it to sources. Hmmmm.
|
| 232 |
+
ext.extra_objects.append(word)
|
| 233 |
+
else:
|
| 234 |
+
file.warn("unrecognized argument '%s'" % word)
|
| 235 |
+
|
| 236 |
+
extensions.append(ext)
|
| 237 |
+
finally:
|
| 238 |
+
file.close()
|
| 239 |
+
|
| 240 |
+
return extensions
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/distutils/log.py
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""A simple log mechanism styled after PEP 282."""
|
| 2 |
+
|
| 3 |
+
# The class here is styled after PEP 282 so that it could later be
|
| 4 |
+
# replaced with a standard Python logging implementation.
|
| 5 |
+
|
| 6 |
+
DEBUG = 1
|
| 7 |
+
INFO = 2
|
| 8 |
+
WARN = 3
|
| 9 |
+
ERROR = 4
|
| 10 |
+
FATAL = 5
|
| 11 |
+
|
| 12 |
+
import sys
|
| 13 |
+
|
| 14 |
+
class Log:
|
| 15 |
+
|
| 16 |
+
def __init__(self, threshold=WARN):
|
| 17 |
+
self.threshold = threshold
|
| 18 |
+
|
| 19 |
+
def _log(self, level, msg, args):
|
| 20 |
+
if level not in (DEBUG, INFO, WARN, ERROR, FATAL):
|
| 21 |
+
raise ValueError('%s wrong log level' % str(level))
|
| 22 |
+
|
| 23 |
+
if level >= self.threshold:
|
| 24 |
+
if args:
|
| 25 |
+
msg = msg % args
|
| 26 |
+
if level in (WARN, ERROR, FATAL):
|
| 27 |
+
stream = sys.stderr
|
| 28 |
+
else:
|
| 29 |
+
stream = sys.stdout
|
| 30 |
+
try:
|
| 31 |
+
stream.write('%s\n' % msg)
|
| 32 |
+
except UnicodeEncodeError:
|
| 33 |
+
# emulate backslashreplace error handler
|
| 34 |
+
encoding = stream.encoding
|
| 35 |
+
msg = msg.encode(encoding, "backslashreplace").decode(encoding)
|
| 36 |
+
stream.write('%s\n' % msg)
|
| 37 |
+
stream.flush()
|
| 38 |
+
|
| 39 |
+
def log(self, level, msg, *args):
|
| 40 |
+
self._log(level, msg, args)
|
| 41 |
+
|
| 42 |
+
def debug(self, msg, *args):
|
| 43 |
+
self._log(DEBUG, msg, args)
|
| 44 |
+
|
| 45 |
+
def info(self, msg, *args):
|
| 46 |
+
self._log(INFO, msg, args)
|
| 47 |
+
|
| 48 |
+
def warn(self, msg, *args):
|
| 49 |
+
self._log(WARN, msg, args)
|
| 50 |
+
|
| 51 |
+
def error(self, msg, *args):
|
| 52 |
+
self._log(ERROR, msg, args)
|
| 53 |
+
|
| 54 |
+
def fatal(self, msg, *args):
|
| 55 |
+
self._log(FATAL, msg, args)
|
| 56 |
+
|
| 57 |
+
_global_log = Log()
|
| 58 |
+
log = _global_log.log
|
| 59 |
+
debug = _global_log.debug
|
| 60 |
+
info = _global_log.info
|
| 61 |
+
warn = _global_log.warn
|
| 62 |
+
error = _global_log.error
|
| 63 |
+
fatal = _global_log.fatal
|
| 64 |
+
|
| 65 |
+
def set_threshold(level):
|
| 66 |
+
# return the old threshold for use from tests
|
| 67 |
+
old = _global_log.threshold
|
| 68 |
+
_global_log.threshold = level
|
| 69 |
+
return old
|
| 70 |
+
|
| 71 |
+
def set_verbosity(v):
|
| 72 |
+
if v <= 0:
|
| 73 |
+
set_threshold(WARN)
|
| 74 |
+
elif v == 1:
|
| 75 |
+
set_threshold(INFO)
|
| 76 |
+
elif v >= 2:
|
| 77 |
+
set_threshold(DEBUG)
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/distutils/msvc9compiler.py
ADDED
|
@@ -0,0 +1,789 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""distutils.msvc9compiler
|
| 2 |
+
|
| 3 |
+
Contains MSVCCompiler, an implementation of the abstract CCompiler class
|
| 4 |
+
for the Microsoft Visual Studio 2008.
|
| 5 |
+
|
| 6 |
+
The module is compatible with VS 2005 and VS 2008. You can find legacy support
|
| 7 |
+
for older versions of VS in distutils.msvccompiler.
|
| 8 |
+
"""
|
| 9 |
+
|
| 10 |
+
# Written by Perry Stoll
|
| 11 |
+
# hacked by Robin Becker and Thomas Heller to do a better job of
|
| 12 |
+
# finding DevStudio (through the registry)
|
| 13 |
+
# ported to VS2005 and VS 2008 by Christian Heimes
|
| 14 |
+
|
| 15 |
+
import os
|
| 16 |
+
import subprocess
|
| 17 |
+
import sys
|
| 18 |
+
import re
|
| 19 |
+
|
| 20 |
+
from distutils.errors import DistutilsExecError, DistutilsPlatformError, \
|
| 21 |
+
CompileError, LibError, LinkError
|
| 22 |
+
from distutils.ccompiler import CCompiler, gen_preprocess_options, \
|
| 23 |
+
gen_lib_options
|
| 24 |
+
from distutils import log
|
| 25 |
+
from distutils.util import get_platform
|
| 26 |
+
|
| 27 |
+
import winreg
|
| 28 |
+
|
| 29 |
+
RegOpenKeyEx = winreg.OpenKeyEx
|
| 30 |
+
RegEnumKey = winreg.EnumKey
|
| 31 |
+
RegEnumValue = winreg.EnumValue
|
| 32 |
+
RegError = winreg.error
|
| 33 |
+
|
| 34 |
+
HKEYS = (winreg.HKEY_USERS,
|
| 35 |
+
winreg.HKEY_CURRENT_USER,
|
| 36 |
+
winreg.HKEY_LOCAL_MACHINE,
|
| 37 |
+
winreg.HKEY_CLASSES_ROOT)
|
| 38 |
+
|
| 39 |
+
NATIVE_WIN64 = (sys.platform == 'win32' and sys.maxsize > 2**32)
|
| 40 |
+
if NATIVE_WIN64:
|
| 41 |
+
# Visual C++ is a 32-bit application, so we need to look in
|
| 42 |
+
# the corresponding registry branch, if we're running a
|
| 43 |
+
# 64-bit Python on Win64
|
| 44 |
+
VS_BASE = r"Software\Wow6432Node\Microsoft\VisualStudio\%0.1f"
|
| 45 |
+
WINSDK_BASE = r"Software\Wow6432Node\Microsoft\Microsoft SDKs\Windows"
|
| 46 |
+
NET_BASE = r"Software\Wow6432Node\Microsoft\.NETFramework"
|
| 47 |
+
else:
|
| 48 |
+
VS_BASE = r"Software\Microsoft\VisualStudio\%0.1f"
|
| 49 |
+
WINSDK_BASE = r"Software\Microsoft\Microsoft SDKs\Windows"
|
| 50 |
+
NET_BASE = r"Software\Microsoft\.NETFramework"
|
| 51 |
+
|
| 52 |
+
# A map keyed by get_platform() return values to values accepted by
|
| 53 |
+
# 'vcvarsall.bat'. Note a cross-compile may combine these (eg, 'x86_amd64' is
|
| 54 |
+
# the param to cross-compile on x86 targeting amd64.)
|
| 55 |
+
PLAT_TO_VCVARS = {
|
| 56 |
+
'win32' : 'x86',
|
| 57 |
+
'win-amd64' : 'amd64',
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
class Reg:
|
| 61 |
+
"""Helper class to read values from the registry
|
| 62 |
+
"""
|
| 63 |
+
|
| 64 |
+
def get_value(cls, path, key):
|
| 65 |
+
for base in HKEYS:
|
| 66 |
+
d = cls.read_values(base, path)
|
| 67 |
+
if d and key in d:
|
| 68 |
+
return d[key]
|
| 69 |
+
raise KeyError(key)
|
| 70 |
+
get_value = classmethod(get_value)
|
| 71 |
+
|
| 72 |
+
def read_keys(cls, base, key):
|
| 73 |
+
"""Return list of registry keys."""
|
| 74 |
+
try:
|
| 75 |
+
handle = RegOpenKeyEx(base, key)
|
| 76 |
+
except RegError:
|
| 77 |
+
return None
|
| 78 |
+
L = []
|
| 79 |
+
i = 0
|
| 80 |
+
while True:
|
| 81 |
+
try:
|
| 82 |
+
k = RegEnumKey(handle, i)
|
| 83 |
+
except RegError:
|
| 84 |
+
break
|
| 85 |
+
L.append(k)
|
| 86 |
+
i += 1
|
| 87 |
+
return L
|
| 88 |
+
read_keys = classmethod(read_keys)
|
| 89 |
+
|
| 90 |
+
def read_values(cls, base, key):
|
| 91 |
+
"""Return dict of registry keys and values.
|
| 92 |
+
|
| 93 |
+
All names are converted to lowercase.
|
| 94 |
+
"""
|
| 95 |
+
try:
|
| 96 |
+
handle = RegOpenKeyEx(base, key)
|
| 97 |
+
except RegError:
|
| 98 |
+
return None
|
| 99 |
+
d = {}
|
| 100 |
+
i = 0
|
| 101 |
+
while True:
|
| 102 |
+
try:
|
| 103 |
+
name, value, type = RegEnumValue(handle, i)
|
| 104 |
+
except RegError:
|
| 105 |
+
break
|
| 106 |
+
name = name.lower()
|
| 107 |
+
d[cls.convert_mbcs(name)] = cls.convert_mbcs(value)
|
| 108 |
+
i += 1
|
| 109 |
+
return d
|
| 110 |
+
read_values = classmethod(read_values)
|
| 111 |
+
|
| 112 |
+
def convert_mbcs(s):
|
| 113 |
+
dec = getattr(s, "decode", None)
|
| 114 |
+
if dec is not None:
|
| 115 |
+
try:
|
| 116 |
+
s = dec("mbcs")
|
| 117 |
+
except UnicodeError:
|
| 118 |
+
pass
|
| 119 |
+
return s
|
| 120 |
+
convert_mbcs = staticmethod(convert_mbcs)
|
| 121 |
+
|
| 122 |
+
class MacroExpander:
|
| 123 |
+
|
| 124 |
+
def __init__(self, version):
|
| 125 |
+
self.macros = {}
|
| 126 |
+
self.vsbase = VS_BASE % version
|
| 127 |
+
self.load_macros(version)
|
| 128 |
+
|
| 129 |
+
def set_macro(self, macro, path, key):
|
| 130 |
+
self.macros["$(%s)" % macro] = Reg.get_value(path, key)
|
| 131 |
+
|
| 132 |
+
def load_macros(self, version):
|
| 133 |
+
self.set_macro("VCInstallDir", self.vsbase + r"\Setup\VC", "productdir")
|
| 134 |
+
self.set_macro("VSInstallDir", self.vsbase + r"\Setup\VS", "productdir")
|
| 135 |
+
self.set_macro("FrameworkDir", NET_BASE, "installroot")
|
| 136 |
+
try:
|
| 137 |
+
if version >= 8.0:
|
| 138 |
+
self.set_macro("FrameworkSDKDir", NET_BASE,
|
| 139 |
+
"sdkinstallrootv2.0")
|
| 140 |
+
else:
|
| 141 |
+
raise KeyError("sdkinstallrootv2.0")
|
| 142 |
+
except KeyError:
|
| 143 |
+
raise DistutilsPlatformError(
|
| 144 |
+
"""Python was built with Visual Studio 2008;
|
| 145 |
+
extensions must be built with a compiler than can generate compatible binaries.
|
| 146 |
+
Visual Studio 2008 was not found on this system. If you have Cygwin installed,
|
| 147 |
+
you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""")
|
| 148 |
+
|
| 149 |
+
if version >= 9.0:
|
| 150 |
+
self.set_macro("FrameworkVersion", self.vsbase, "clr version")
|
| 151 |
+
self.set_macro("WindowsSdkDir", WINSDK_BASE, "currentinstallfolder")
|
| 152 |
+
else:
|
| 153 |
+
p = r"Software\Microsoft\NET Framework Setup\Product"
|
| 154 |
+
for base in HKEYS:
|
| 155 |
+
try:
|
| 156 |
+
h = RegOpenKeyEx(base, p)
|
| 157 |
+
except RegError:
|
| 158 |
+
continue
|
| 159 |
+
key = RegEnumKey(h, 0)
|
| 160 |
+
d = Reg.get_value(base, r"%s\%s" % (p, key))
|
| 161 |
+
self.macros["$(FrameworkVersion)"] = d["version"]
|
| 162 |
+
|
| 163 |
+
def sub(self, s):
|
| 164 |
+
for k, v in self.macros.items():
|
| 165 |
+
s = s.replace(k, v)
|
| 166 |
+
return s
|
| 167 |
+
|
| 168 |
+
def get_build_version():
|
| 169 |
+
"""Return the version of MSVC that was used to build Python.
|
| 170 |
+
|
| 171 |
+
For Python 2.3 and up, the version number is included in
|
| 172 |
+
sys.version. For earlier versions, assume the compiler is MSVC 6.
|
| 173 |
+
"""
|
| 174 |
+
prefix = "MSC v."
|
| 175 |
+
i = sys.version.find(prefix)
|
| 176 |
+
if i == -1:
|
| 177 |
+
return 6
|
| 178 |
+
i = i + len(prefix)
|
| 179 |
+
s, rest = sys.version[i:].split(" ", 1)
|
| 180 |
+
majorVersion = int(s[:-2]) - 6
|
| 181 |
+
if majorVersion >= 13:
|
| 182 |
+
# v13 was skipped and should be v14
|
| 183 |
+
majorVersion += 1
|
| 184 |
+
minorVersion = int(s[2:3]) / 10.0
|
| 185 |
+
# I don't think paths are affected by minor version in version 6
|
| 186 |
+
if majorVersion == 6:
|
| 187 |
+
minorVersion = 0
|
| 188 |
+
if majorVersion >= 6:
|
| 189 |
+
return majorVersion + minorVersion
|
| 190 |
+
# else we don't know what version of the compiler this is
|
| 191 |
+
return None
|
| 192 |
+
|
| 193 |
+
def normalize_and_reduce_paths(paths):
|
| 194 |
+
"""Return a list of normalized paths with duplicates removed.
|
| 195 |
+
|
| 196 |
+
The current order of paths is maintained.
|
| 197 |
+
"""
|
| 198 |
+
# Paths are normalized so things like: /a and /a/ aren't both preserved.
|
| 199 |
+
reduced_paths = []
|
| 200 |
+
for p in paths:
|
| 201 |
+
np = os.path.normpath(p)
|
| 202 |
+
# XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set.
|
| 203 |
+
if np not in reduced_paths:
|
| 204 |
+
reduced_paths.append(np)
|
| 205 |
+
return reduced_paths
|
| 206 |
+
|
| 207 |
+
def removeDuplicates(variable):
|
| 208 |
+
"""Remove duplicate values of an environment variable.
|
| 209 |
+
"""
|
| 210 |
+
oldList = variable.split(os.pathsep)
|
| 211 |
+
newList = []
|
| 212 |
+
for i in oldList:
|
| 213 |
+
if i not in newList:
|
| 214 |
+
newList.append(i)
|
| 215 |
+
newVariable = os.pathsep.join(newList)
|
| 216 |
+
return newVariable
|
| 217 |
+
|
| 218 |
+
def find_vcvarsall(version):
|
| 219 |
+
"""Find the vcvarsall.bat file
|
| 220 |
+
|
| 221 |
+
At first it tries to find the productdir of VS 2008 in the registry. If
|
| 222 |
+
that fails it falls back to the VS90COMNTOOLS env var.
|
| 223 |
+
"""
|
| 224 |
+
vsbase = VS_BASE % version
|
| 225 |
+
try:
|
| 226 |
+
productdir = Reg.get_value(r"%s\Setup\VC" % vsbase,
|
| 227 |
+
"productdir")
|
| 228 |
+
except KeyError:
|
| 229 |
+
log.debug("Unable to find productdir in registry")
|
| 230 |
+
productdir = None
|
| 231 |
+
|
| 232 |
+
if not productdir or not os.path.isdir(productdir):
|
| 233 |
+
toolskey = "VS%0.f0COMNTOOLS" % version
|
| 234 |
+
toolsdir = os.environ.get(toolskey, None)
|
| 235 |
+
|
| 236 |
+
if toolsdir and os.path.isdir(toolsdir):
|
| 237 |
+
productdir = os.path.join(toolsdir, os.pardir, os.pardir, "VC")
|
| 238 |
+
productdir = os.path.abspath(productdir)
|
| 239 |
+
if not os.path.isdir(productdir):
|
| 240 |
+
log.debug("%s is not a valid directory" % productdir)
|
| 241 |
+
return None
|
| 242 |
+
else:
|
| 243 |
+
log.debug("Env var %s is not set or invalid" % toolskey)
|
| 244 |
+
if not productdir:
|
| 245 |
+
log.debug("No productdir found")
|
| 246 |
+
return None
|
| 247 |
+
vcvarsall = os.path.join(productdir, "vcvarsall.bat")
|
| 248 |
+
if os.path.isfile(vcvarsall):
|
| 249 |
+
return vcvarsall
|
| 250 |
+
log.debug("Unable to find vcvarsall.bat")
|
| 251 |
+
return None
|
| 252 |
+
|
| 253 |
+
def query_vcvarsall(version, arch="x86"):
|
| 254 |
+
"""Launch vcvarsall.bat and read the settings from its environment
|
| 255 |
+
"""
|
| 256 |
+
vcvarsall = find_vcvarsall(version)
|
| 257 |
+
interesting = {"include", "lib", "libpath", "path"}
|
| 258 |
+
result = {}
|
| 259 |
+
|
| 260 |
+
if vcvarsall is None:
|
| 261 |
+
raise DistutilsPlatformError("Unable to find vcvarsall.bat")
|
| 262 |
+
log.debug("Calling 'vcvarsall.bat %s' (version=%s)", arch, version)
|
| 263 |
+
popen = subprocess.Popen('"%s" %s & set' % (vcvarsall, arch),
|
| 264 |
+
stdout=subprocess.PIPE,
|
| 265 |
+
stderr=subprocess.PIPE)
|
| 266 |
+
try:
|
| 267 |
+
stdout, stderr = popen.communicate()
|
| 268 |
+
if popen.wait() != 0:
|
| 269 |
+
raise DistutilsPlatformError(stderr.decode("mbcs"))
|
| 270 |
+
|
| 271 |
+
stdout = stdout.decode("mbcs")
|
| 272 |
+
for line in stdout.split("\n"):
|
| 273 |
+
line = Reg.convert_mbcs(line)
|
| 274 |
+
if '=' not in line:
|
| 275 |
+
continue
|
| 276 |
+
line = line.strip()
|
| 277 |
+
key, value = line.split('=', 1)
|
| 278 |
+
key = key.lower()
|
| 279 |
+
if key in interesting:
|
| 280 |
+
if value.endswith(os.pathsep):
|
| 281 |
+
value = value[:-1]
|
| 282 |
+
result[key] = removeDuplicates(value)
|
| 283 |
+
|
| 284 |
+
finally:
|
| 285 |
+
popen.stdout.close()
|
| 286 |
+
popen.stderr.close()
|
| 287 |
+
|
| 288 |
+
if len(result) != len(interesting):
|
| 289 |
+
raise ValueError(str(list(result.keys())))
|
| 290 |
+
|
| 291 |
+
return result
|
| 292 |
+
|
| 293 |
+
# More globals
|
| 294 |
+
VERSION = get_build_version()
|
| 295 |
+
if VERSION < 8.0:
|
| 296 |
+
raise DistutilsPlatformError("VC %0.1f is not supported by this module" % VERSION)
|
| 297 |
+
# MACROS = MacroExpander(VERSION)
|
| 298 |
+
|
| 299 |
+
class MSVCCompiler(CCompiler) :
|
| 300 |
+
"""Concrete class that implements an interface to Microsoft Visual C++,
|
| 301 |
+
as defined by the CCompiler abstract class."""
|
| 302 |
+
|
| 303 |
+
compiler_type = 'msvc'
|
| 304 |
+
|
| 305 |
+
# Just set this so CCompiler's constructor doesn't barf. We currently
|
| 306 |
+
# don't use the 'set_executables()' bureaucracy provided by CCompiler,
|
| 307 |
+
# as it really isn't necessary for this sort of single-compiler class.
|
| 308 |
+
# Would be nice to have a consistent interface with UnixCCompiler,
|
| 309 |
+
# though, so it's worth thinking about.
|
| 310 |
+
executables = {}
|
| 311 |
+
|
| 312 |
+
# Private class data (need to distinguish C from C++ source for compiler)
|
| 313 |
+
_c_extensions = ['.c']
|
| 314 |
+
_cpp_extensions = ['.cc', '.cpp', '.cxx']
|
| 315 |
+
_rc_extensions = ['.rc']
|
| 316 |
+
_mc_extensions = ['.mc']
|
| 317 |
+
|
| 318 |
+
# Needed for the filename generation methods provided by the
|
| 319 |
+
# base class, CCompiler.
|
| 320 |
+
src_extensions = (_c_extensions + _cpp_extensions +
|
| 321 |
+
_rc_extensions + _mc_extensions)
|
| 322 |
+
res_extension = '.res'
|
| 323 |
+
obj_extension = '.obj'
|
| 324 |
+
static_lib_extension = '.lib'
|
| 325 |
+
shared_lib_extension = '.dll'
|
| 326 |
+
static_lib_format = shared_lib_format = '%s%s'
|
| 327 |
+
exe_extension = '.exe'
|
| 328 |
+
|
| 329 |
+
def __init__(self, verbose=0, dry_run=0, force=0):
|
| 330 |
+
CCompiler.__init__ (self, verbose, dry_run, force)
|
| 331 |
+
self.__version = VERSION
|
| 332 |
+
self.__root = r"Software\Microsoft\VisualStudio"
|
| 333 |
+
# self.__macros = MACROS
|
| 334 |
+
self.__paths = []
|
| 335 |
+
# target platform (.plat_name is consistent with 'bdist')
|
| 336 |
+
self.plat_name = None
|
| 337 |
+
self.__arch = None # deprecated name
|
| 338 |
+
self.initialized = False
|
| 339 |
+
|
| 340 |
+
def initialize(self, plat_name=None):
|
| 341 |
+
# multi-init means we would need to check platform same each time...
|
| 342 |
+
assert not self.initialized, "don't init multiple times"
|
| 343 |
+
if plat_name is None:
|
| 344 |
+
plat_name = get_platform()
|
| 345 |
+
# sanity check for platforms to prevent obscure errors later.
|
| 346 |
+
ok_plats = 'win32', 'win-amd64'
|
| 347 |
+
if plat_name not in ok_plats:
|
| 348 |
+
raise DistutilsPlatformError("--plat-name must be one of %s" %
|
| 349 |
+
(ok_plats,))
|
| 350 |
+
|
| 351 |
+
if "DISTUTILS_USE_SDK" in os.environ and "MSSdk" in os.environ and self.find_exe("cl.exe"):
|
| 352 |
+
# Assume that the SDK set up everything alright; don't try to be
|
| 353 |
+
# smarter
|
| 354 |
+
self.cc = "cl.exe"
|
| 355 |
+
self.linker = "link.exe"
|
| 356 |
+
self.lib = "lib.exe"
|
| 357 |
+
self.rc = "rc.exe"
|
| 358 |
+
self.mc = "mc.exe"
|
| 359 |
+
else:
|
| 360 |
+
# On x86, 'vcvars32.bat amd64' creates an env that doesn't work;
|
| 361 |
+
# to cross compile, you use 'x86_amd64'.
|
| 362 |
+
# On AMD64, 'vcvars32.bat amd64' is a native build env; to cross
|
| 363 |
+
# compile use 'x86' (ie, it runs the x86 compiler directly)
|
| 364 |
+
if plat_name == get_platform() or plat_name == 'win32':
|
| 365 |
+
# native build or cross-compile to win32
|
| 366 |
+
plat_spec = PLAT_TO_VCVARS[plat_name]
|
| 367 |
+
else:
|
| 368 |
+
# cross compile from win32 -> some 64bit
|
| 369 |
+
plat_spec = PLAT_TO_VCVARS[get_platform()] + '_' + \
|
| 370 |
+
PLAT_TO_VCVARS[plat_name]
|
| 371 |
+
|
| 372 |
+
vc_env = query_vcvarsall(VERSION, plat_spec)
|
| 373 |
+
|
| 374 |
+
self.__paths = vc_env['path'].split(os.pathsep)
|
| 375 |
+
os.environ['lib'] = vc_env['lib']
|
| 376 |
+
os.environ['include'] = vc_env['include']
|
| 377 |
+
|
| 378 |
+
if len(self.__paths) == 0:
|
| 379 |
+
raise DistutilsPlatformError("Python was built with %s, "
|
| 380 |
+
"and extensions need to be built with the same "
|
| 381 |
+
"version of the compiler, but it isn't installed."
|
| 382 |
+
% self.__product)
|
| 383 |
+
|
| 384 |
+
self.cc = self.find_exe("cl.exe")
|
| 385 |
+
self.linker = self.find_exe("link.exe")
|
| 386 |
+
self.lib = self.find_exe("lib.exe")
|
| 387 |
+
self.rc = self.find_exe("rc.exe") # resource compiler
|
| 388 |
+
self.mc = self.find_exe("mc.exe") # message compiler
|
| 389 |
+
#self.set_path_env_var('lib')
|
| 390 |
+
#self.set_path_env_var('include')
|
| 391 |
+
|
| 392 |
+
# extend the MSVC path with the current path
|
| 393 |
+
try:
|
| 394 |
+
for p in os.environ['path'].split(';'):
|
| 395 |
+
self.__paths.append(p)
|
| 396 |
+
except KeyError:
|
| 397 |
+
pass
|
| 398 |
+
self.__paths = normalize_and_reduce_paths(self.__paths)
|
| 399 |
+
os.environ['path'] = ";".join(self.__paths)
|
| 400 |
+
|
| 401 |
+
self.preprocess_options = None
|
| 402 |
+
if self.__arch == "x86":
|
| 403 |
+
self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3',
|
| 404 |
+
'/DNDEBUG']
|
| 405 |
+
self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3',
|
| 406 |
+
'/Z7', '/D_DEBUG']
|
| 407 |
+
else:
|
| 408 |
+
# Win64
|
| 409 |
+
self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GS-' ,
|
| 410 |
+
'/DNDEBUG']
|
| 411 |
+
self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GS-',
|
| 412 |
+
'/Z7', '/D_DEBUG']
|
| 413 |
+
|
| 414 |
+
self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO']
|
| 415 |
+
if self.__version >= 7:
|
| 416 |
+
self.ldflags_shared_debug = [
|
| 417 |
+
'/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG'
|
| 418 |
+
]
|
| 419 |
+
self.ldflags_static = [ '/nologo']
|
| 420 |
+
|
| 421 |
+
self.initialized = True
|
| 422 |
+
|
| 423 |
+
# -- Worker methods ------------------------------------------------
|
| 424 |
+
|
| 425 |
+
def object_filenames(self,
|
| 426 |
+
source_filenames,
|
| 427 |
+
strip_dir=0,
|
| 428 |
+
output_dir=''):
|
| 429 |
+
# Copied from ccompiler.py, extended to return .res as 'object'-file
|
| 430 |
+
# for .rc input file
|
| 431 |
+
if output_dir is None: output_dir = ''
|
| 432 |
+
obj_names = []
|
| 433 |
+
for src_name in source_filenames:
|
| 434 |
+
(base, ext) = os.path.splitext (src_name)
|
| 435 |
+
base = os.path.splitdrive(base)[1] # Chop off the drive
|
| 436 |
+
base = base[os.path.isabs(base):] # If abs, chop off leading /
|
| 437 |
+
if ext not in self.src_extensions:
|
| 438 |
+
# Better to raise an exception instead of silently continuing
|
| 439 |
+
# and later complain about sources and targets having
|
| 440 |
+
# different lengths
|
| 441 |
+
raise CompileError ("Don't know how to compile %s" % src_name)
|
| 442 |
+
if strip_dir:
|
| 443 |
+
base = os.path.basename (base)
|
| 444 |
+
if ext in self._rc_extensions:
|
| 445 |
+
obj_names.append (os.path.join (output_dir,
|
| 446 |
+
base + self.res_extension))
|
| 447 |
+
elif ext in self._mc_extensions:
|
| 448 |
+
obj_names.append (os.path.join (output_dir,
|
| 449 |
+
base + self.res_extension))
|
| 450 |
+
else:
|
| 451 |
+
obj_names.append (os.path.join (output_dir,
|
| 452 |
+
base + self.obj_extension))
|
| 453 |
+
return obj_names
|
| 454 |
+
|
| 455 |
+
|
| 456 |
+
def compile(self, sources,
|
| 457 |
+
output_dir=None, macros=None, include_dirs=None, debug=0,
|
| 458 |
+
extra_preargs=None, extra_postargs=None, depends=None):
|
| 459 |
+
|
| 460 |
+
if not self.initialized:
|
| 461 |
+
self.initialize()
|
| 462 |
+
compile_info = self._setup_compile(output_dir, macros, include_dirs,
|
| 463 |
+
sources, depends, extra_postargs)
|
| 464 |
+
macros, objects, extra_postargs, pp_opts, build = compile_info
|
| 465 |
+
|
| 466 |
+
compile_opts = extra_preargs or []
|
| 467 |
+
compile_opts.append ('/c')
|
| 468 |
+
if debug:
|
| 469 |
+
compile_opts.extend(self.compile_options_debug)
|
| 470 |
+
else:
|
| 471 |
+
compile_opts.extend(self.compile_options)
|
| 472 |
+
|
| 473 |
+
for obj in objects:
|
| 474 |
+
try:
|
| 475 |
+
src, ext = build[obj]
|
| 476 |
+
except KeyError:
|
| 477 |
+
continue
|
| 478 |
+
if debug:
|
| 479 |
+
# pass the full pathname to MSVC in debug mode,
|
| 480 |
+
# this allows the debugger to find the source file
|
| 481 |
+
# without asking the user to browse for it
|
| 482 |
+
src = os.path.abspath(src)
|
| 483 |
+
|
| 484 |
+
if ext in self._c_extensions:
|
| 485 |
+
input_opt = "/Tc" + src
|
| 486 |
+
elif ext in self._cpp_extensions:
|
| 487 |
+
input_opt = "/Tp" + src
|
| 488 |
+
elif ext in self._rc_extensions:
|
| 489 |
+
# compile .RC to .RES file
|
| 490 |
+
input_opt = src
|
| 491 |
+
output_opt = "/fo" + obj
|
| 492 |
+
try:
|
| 493 |
+
self.spawn([self.rc] + pp_opts +
|
| 494 |
+
[output_opt] + [input_opt])
|
| 495 |
+
except DistutilsExecError as msg:
|
| 496 |
+
raise CompileError(msg)
|
| 497 |
+
continue
|
| 498 |
+
elif ext in self._mc_extensions:
|
| 499 |
+
# Compile .MC to .RC file to .RES file.
|
| 500 |
+
# * '-h dir' specifies the directory for the
|
| 501 |
+
# generated include file
|
| 502 |
+
# * '-r dir' specifies the target directory of the
|
| 503 |
+
# generated RC file and the binary message resource
|
| 504 |
+
# it includes
|
| 505 |
+
#
|
| 506 |
+
# For now (since there are no options to change this),
|
| 507 |
+
# we use the source-directory for the include file and
|
| 508 |
+
# the build directory for the RC file and message
|
| 509 |
+
# resources. This works at least for win32all.
|
| 510 |
+
h_dir = os.path.dirname(src)
|
| 511 |
+
rc_dir = os.path.dirname(obj)
|
| 512 |
+
try:
|
| 513 |
+
# first compile .MC to .RC and .H file
|
| 514 |
+
self.spawn([self.mc] +
|
| 515 |
+
['-h', h_dir, '-r', rc_dir] + [src])
|
| 516 |
+
base, _ = os.path.splitext (os.path.basename (src))
|
| 517 |
+
rc_file = os.path.join (rc_dir, base + '.rc')
|
| 518 |
+
# then compile .RC to .RES file
|
| 519 |
+
self.spawn([self.rc] +
|
| 520 |
+
["/fo" + obj] + [rc_file])
|
| 521 |
+
|
| 522 |
+
except DistutilsExecError as msg:
|
| 523 |
+
raise CompileError(msg)
|
| 524 |
+
continue
|
| 525 |
+
else:
|
| 526 |
+
# how to handle this file?
|
| 527 |
+
raise CompileError("Don't know how to compile %s to %s"
|
| 528 |
+
% (src, obj))
|
| 529 |
+
|
| 530 |
+
output_opt = "/Fo" + obj
|
| 531 |
+
try:
|
| 532 |
+
self.spawn([self.cc] + compile_opts + pp_opts +
|
| 533 |
+
[input_opt, output_opt] +
|
| 534 |
+
extra_postargs)
|
| 535 |
+
except DistutilsExecError as msg:
|
| 536 |
+
raise CompileError(msg)
|
| 537 |
+
|
| 538 |
+
return objects
|
| 539 |
+
|
| 540 |
+
|
| 541 |
+
def create_static_lib(self,
|
| 542 |
+
objects,
|
| 543 |
+
output_libname,
|
| 544 |
+
output_dir=None,
|
| 545 |
+
debug=0,
|
| 546 |
+
target_lang=None):
|
| 547 |
+
|
| 548 |
+
if not self.initialized:
|
| 549 |
+
self.initialize()
|
| 550 |
+
(objects, output_dir) = self._fix_object_args(objects, output_dir)
|
| 551 |
+
output_filename = self.library_filename(output_libname,
|
| 552 |
+
output_dir=output_dir)
|
| 553 |
+
|
| 554 |
+
if self._need_link(objects, output_filename):
|
| 555 |
+
lib_args = objects + ['/OUT:' + output_filename]
|
| 556 |
+
if debug:
|
| 557 |
+
pass # XXX what goes here?
|
| 558 |
+
try:
|
| 559 |
+
self.spawn([self.lib] + lib_args)
|
| 560 |
+
except DistutilsExecError as msg:
|
| 561 |
+
raise LibError(msg)
|
| 562 |
+
else:
|
| 563 |
+
log.debug("skipping %s (up-to-date)", output_filename)
|
| 564 |
+
|
| 565 |
+
|
| 566 |
+
def link(self,
|
| 567 |
+
target_desc,
|
| 568 |
+
objects,
|
| 569 |
+
output_filename,
|
| 570 |
+
output_dir=None,
|
| 571 |
+
libraries=None,
|
| 572 |
+
library_dirs=None,
|
| 573 |
+
runtime_library_dirs=None,
|
| 574 |
+
export_symbols=None,
|
| 575 |
+
debug=0,
|
| 576 |
+
extra_preargs=None,
|
| 577 |
+
extra_postargs=None,
|
| 578 |
+
build_temp=None,
|
| 579 |
+
target_lang=None):
|
| 580 |
+
|
| 581 |
+
if not self.initialized:
|
| 582 |
+
self.initialize()
|
| 583 |
+
(objects, output_dir) = self._fix_object_args(objects, output_dir)
|
| 584 |
+
fixed_args = self._fix_lib_args(libraries, library_dirs,
|
| 585 |
+
runtime_library_dirs)
|
| 586 |
+
(libraries, library_dirs, runtime_library_dirs) = fixed_args
|
| 587 |
+
|
| 588 |
+
if runtime_library_dirs:
|
| 589 |
+
self.warn ("I don't know what to do with 'runtime_library_dirs': "
|
| 590 |
+
+ str (runtime_library_dirs))
|
| 591 |
+
|
| 592 |
+
lib_opts = gen_lib_options(self,
|
| 593 |
+
library_dirs, runtime_library_dirs,
|
| 594 |
+
libraries)
|
| 595 |
+
if output_dir is not None:
|
| 596 |
+
output_filename = os.path.join(output_dir, output_filename)
|
| 597 |
+
|
| 598 |
+
if self._need_link(objects, output_filename):
|
| 599 |
+
if target_desc == CCompiler.EXECUTABLE:
|
| 600 |
+
if debug:
|
| 601 |
+
ldflags = self.ldflags_shared_debug[1:]
|
| 602 |
+
else:
|
| 603 |
+
ldflags = self.ldflags_shared[1:]
|
| 604 |
+
else:
|
| 605 |
+
if debug:
|
| 606 |
+
ldflags = self.ldflags_shared_debug
|
| 607 |
+
else:
|
| 608 |
+
ldflags = self.ldflags_shared
|
| 609 |
+
|
| 610 |
+
export_opts = []
|
| 611 |
+
for sym in (export_symbols or []):
|
| 612 |
+
export_opts.append("/EXPORT:" + sym)
|
| 613 |
+
|
| 614 |
+
ld_args = (ldflags + lib_opts + export_opts +
|
| 615 |
+
objects + ['/OUT:' + output_filename])
|
| 616 |
+
|
| 617 |
+
# The MSVC linker generates .lib and .exp files, which cannot be
|
| 618 |
+
# suppressed by any linker switches. The .lib files may even be
|
| 619 |
+
# needed! Make sure they are generated in the temporary build
|
| 620 |
+
# directory. Since they have different names for debug and release
|
| 621 |
+
# builds, they can go into the same directory.
|
| 622 |
+
build_temp = os.path.dirname(objects[0])
|
| 623 |
+
if export_symbols is not None:
|
| 624 |
+
(dll_name, dll_ext) = os.path.splitext(
|
| 625 |
+
os.path.basename(output_filename))
|
| 626 |
+
implib_file = os.path.join(
|
| 627 |
+
build_temp,
|
| 628 |
+
self.library_filename(dll_name))
|
| 629 |
+
ld_args.append ('/IMPLIB:' + implib_file)
|
| 630 |
+
|
| 631 |
+
self.manifest_setup_ldargs(output_filename, build_temp, ld_args)
|
| 632 |
+
|
| 633 |
+
if extra_preargs:
|
| 634 |
+
ld_args[:0] = extra_preargs
|
| 635 |
+
if extra_postargs:
|
| 636 |
+
ld_args.extend(extra_postargs)
|
| 637 |
+
|
| 638 |
+
self.mkpath(os.path.dirname(output_filename))
|
| 639 |
+
try:
|
| 640 |
+
self.spawn([self.linker] + ld_args)
|
| 641 |
+
except DistutilsExecError as msg:
|
| 642 |
+
raise LinkError(msg)
|
| 643 |
+
|
| 644 |
+
# embed the manifest
|
| 645 |
+
# XXX - this is somewhat fragile - if mt.exe fails, distutils
|
| 646 |
+
# will still consider the DLL up-to-date, but it will not have a
|
| 647 |
+
# manifest. Maybe we should link to a temp file? OTOH, that
|
| 648 |
+
# implies a build environment error that shouldn't go undetected.
|
| 649 |
+
mfinfo = self.manifest_get_embed_info(target_desc, ld_args)
|
| 650 |
+
if mfinfo is not None:
|
| 651 |
+
mffilename, mfid = mfinfo
|
| 652 |
+
out_arg = '-outputresource:%s;%s' % (output_filename, mfid)
|
| 653 |
+
try:
|
| 654 |
+
self.spawn(['mt.exe', '-nologo', '-manifest',
|
| 655 |
+
mffilename, out_arg])
|
| 656 |
+
except DistutilsExecError as msg:
|
| 657 |
+
raise LinkError(msg)
|
| 658 |
+
else:
|
| 659 |
+
log.debug("skipping %s (up-to-date)", output_filename)
|
| 660 |
+
|
| 661 |
+
def manifest_setup_ldargs(self, output_filename, build_temp, ld_args):
|
| 662 |
+
# If we need a manifest at all, an embedded manifest is recommended.
|
| 663 |
+
# See MSDN article titled
|
| 664 |
+
# "How to: Embed a Manifest Inside a C/C++ Application"
|
| 665 |
+
# (currently at http://msdn2.microsoft.com/en-us/library/ms235591(VS.80).aspx)
|
| 666 |
+
# Ask the linker to generate the manifest in the temp dir, so
|
| 667 |
+
# we can check it, and possibly embed it, later.
|
| 668 |
+
temp_manifest = os.path.join(
|
| 669 |
+
build_temp,
|
| 670 |
+
os.path.basename(output_filename) + ".manifest")
|
| 671 |
+
ld_args.append('/MANIFESTFILE:' + temp_manifest)
|
| 672 |
+
|
| 673 |
+
def manifest_get_embed_info(self, target_desc, ld_args):
|
| 674 |
+
# If a manifest should be embedded, return a tuple of
|
| 675 |
+
# (manifest_filename, resource_id). Returns None if no manifest
|
| 676 |
+
# should be embedded. See http://bugs.python.org/issue7833 for why
|
| 677 |
+
# we want to avoid any manifest for extension modules if we can)
|
| 678 |
+
for arg in ld_args:
|
| 679 |
+
if arg.startswith("/MANIFESTFILE:"):
|
| 680 |
+
temp_manifest = arg.split(":", 1)[1]
|
| 681 |
+
break
|
| 682 |
+
else:
|
| 683 |
+
# no /MANIFESTFILE so nothing to do.
|
| 684 |
+
return None
|
| 685 |
+
if target_desc == CCompiler.EXECUTABLE:
|
| 686 |
+
# by default, executables always get the manifest with the
|
| 687 |
+
# CRT referenced.
|
| 688 |
+
mfid = 1
|
| 689 |
+
else:
|
| 690 |
+
# Extension modules try and avoid any manifest if possible.
|
| 691 |
+
mfid = 2
|
| 692 |
+
temp_manifest = self._remove_visual_c_ref(temp_manifest)
|
| 693 |
+
if temp_manifest is None:
|
| 694 |
+
return None
|
| 695 |
+
return temp_manifest, mfid
|
| 696 |
+
|
| 697 |
+
def _remove_visual_c_ref(self, manifest_file):
|
| 698 |
+
try:
|
| 699 |
+
# Remove references to the Visual C runtime, so they will
|
| 700 |
+
# fall through to the Visual C dependency of Python.exe.
|
| 701 |
+
# This way, when installed for a restricted user (e.g.
|
| 702 |
+
# runtimes are not in WinSxS folder, but in Python's own
|
| 703 |
+
# folder), the runtimes do not need to be in every folder
|
| 704 |
+
# with .pyd's.
|
| 705 |
+
# Returns either the filename of the modified manifest or
|
| 706 |
+
# None if no manifest should be embedded.
|
| 707 |
+
manifest_f = open(manifest_file)
|
| 708 |
+
try:
|
| 709 |
+
manifest_buf = manifest_f.read()
|
| 710 |
+
finally:
|
| 711 |
+
manifest_f.close()
|
| 712 |
+
pattern = re.compile(
|
| 713 |
+
r"""<assemblyIdentity.*?name=("|')Microsoft\."""\
|
| 714 |
+
r"""VC\d{2}\.CRT("|').*?(/>|</assemblyIdentity>)""",
|
| 715 |
+
re.DOTALL)
|
| 716 |
+
manifest_buf = re.sub(pattern, "", manifest_buf)
|
| 717 |
+
pattern = r"<dependentAssembly>\s*</dependentAssembly>"
|
| 718 |
+
manifest_buf = re.sub(pattern, "", manifest_buf)
|
| 719 |
+
# Now see if any other assemblies are referenced - if not, we
|
| 720 |
+
# don't want a manifest embedded.
|
| 721 |
+
pattern = re.compile(
|
| 722 |
+
r"""<assemblyIdentity.*?name=(?:"|')(.+?)(?:"|')"""
|
| 723 |
+
r""".*?(?:/>|</assemblyIdentity>)""", re.DOTALL)
|
| 724 |
+
if re.search(pattern, manifest_buf) is None:
|
| 725 |
+
return None
|
| 726 |
+
|
| 727 |
+
manifest_f = open(manifest_file, 'w')
|
| 728 |
+
try:
|
| 729 |
+
manifest_f.write(manifest_buf)
|
| 730 |
+
return manifest_file
|
| 731 |
+
finally:
|
| 732 |
+
manifest_f.close()
|
| 733 |
+
except OSError:
|
| 734 |
+
pass
|
| 735 |
+
|
| 736 |
+
# -- Miscellaneous methods -----------------------------------------
|
| 737 |
+
# These are all used by the 'gen_lib_options() function, in
|
| 738 |
+
# ccompiler.py.
|
| 739 |
+
|
| 740 |
+
def library_dir_option(self, dir):
|
| 741 |
+
return "/LIBPATH:" + dir
|
| 742 |
+
|
| 743 |
+
def runtime_library_dir_option(self, dir):
|
| 744 |
+
raise DistutilsPlatformError(
|
| 745 |
+
"don't know how to set runtime library search path for MSVC++")
|
| 746 |
+
|
| 747 |
+
def library_option(self, lib):
|
| 748 |
+
return self.library_filename(lib)
|
| 749 |
+
|
| 750 |
+
|
| 751 |
+
def find_library_file(self, dirs, lib, debug=0):
|
| 752 |
+
# Prefer a debugging library if found (and requested), but deal
|
| 753 |
+
# with it if we don't have one.
|
| 754 |
+
if debug:
|
| 755 |
+
try_names = [lib + "_d", lib]
|
| 756 |
+
else:
|
| 757 |
+
try_names = [lib]
|
| 758 |
+
for dir in dirs:
|
| 759 |
+
for name in try_names:
|
| 760 |
+
libfile = os.path.join(dir, self.library_filename (name))
|
| 761 |
+
if os.path.exists(libfile):
|
| 762 |
+
return libfile
|
| 763 |
+
else:
|
| 764 |
+
# Oops, didn't find it in *any* of 'dirs'
|
| 765 |
+
return None
|
| 766 |
+
|
| 767 |
+
# Helper methods for using the MSVC registry settings
|
| 768 |
+
|
| 769 |
+
def find_exe(self, exe):
|
| 770 |
+
"""Return path to an MSVC executable program.
|
| 771 |
+
|
| 772 |
+
Tries to find the program in several places: first, one of the
|
| 773 |
+
MSVC program search paths from the registry; next, the directories
|
| 774 |
+
in the PATH environment variable. If any of those work, return an
|
| 775 |
+
absolute path that is known to exist. If none of them work, just
|
| 776 |
+
return the original program name, 'exe'.
|
| 777 |
+
"""
|
| 778 |
+
for p in self.__paths:
|
| 779 |
+
fn = os.path.join(os.path.abspath(p), exe)
|
| 780 |
+
if os.path.isfile(fn):
|
| 781 |
+
return fn
|
| 782 |
+
|
| 783 |
+
# didn't find it; try existing path
|
| 784 |
+
for p in os.environ['Path'].split(';'):
|
| 785 |
+
fn = os.path.join(os.path.abspath(p),exe)
|
| 786 |
+
if os.path.isfile(fn):
|
| 787 |
+
return fn
|
| 788 |
+
|
| 789 |
+
return exe
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/distutils/sysconfig.py
ADDED
|
@@ -0,0 +1,554 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Provide access to Python's configuration information. The specific
|
| 2 |
+
configuration variables available depend heavily on the platform and
|
| 3 |
+
configuration. The values may be retrieved using
|
| 4 |
+
get_config_var(name), and the list of variables is available via
|
| 5 |
+
get_config_vars().keys(). Additional convenience functions are also
|
| 6 |
+
available.
|
| 7 |
+
|
| 8 |
+
Written by: Fred L. Drake, Jr.
|
| 9 |
+
Email: <fdrake@acm.org>
|
| 10 |
+
"""
|
| 11 |
+
|
| 12 |
+
import _imp
|
| 13 |
+
import os
|
| 14 |
+
import re
|
| 15 |
+
import sys
|
| 16 |
+
|
| 17 |
+
from .errors import DistutilsPlatformError
|
| 18 |
+
from .util import get_platform, get_host_platform
|
| 19 |
+
|
| 20 |
+
# These are needed in a couple of spots, so just compute them once.
|
| 21 |
+
PREFIX = os.path.normpath(sys.prefix)
|
| 22 |
+
EXEC_PREFIX = os.path.normpath(sys.exec_prefix)
|
| 23 |
+
BASE_PREFIX = os.path.normpath(sys.base_prefix)
|
| 24 |
+
BASE_EXEC_PREFIX = os.path.normpath(sys.base_exec_prefix)
|
| 25 |
+
|
| 26 |
+
# Path to the base directory of the project. On Windows the binary may
|
| 27 |
+
# live in project/PCbuild/win32 or project/PCbuild/amd64.
|
| 28 |
+
# set for cross builds
|
| 29 |
+
if "_PYTHON_PROJECT_BASE" in os.environ:
|
| 30 |
+
project_base = os.path.abspath(os.environ["_PYTHON_PROJECT_BASE"])
|
| 31 |
+
else:
|
| 32 |
+
if sys.executable:
|
| 33 |
+
project_base = os.path.dirname(os.path.abspath(sys.executable))
|
| 34 |
+
else:
|
| 35 |
+
# sys.executable can be empty if argv[0] has been changed and Python is
|
| 36 |
+
# unable to retrieve the real program name
|
| 37 |
+
project_base = os.getcwd()
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
# python_build: (Boolean) if true, we're either building Python or
|
| 41 |
+
# building an extension with an un-installed Python, so we use
|
| 42 |
+
# different (hard-wired) directories.
|
| 43 |
+
def _is_python_source_dir(d):
|
| 44 |
+
for fn in ("Setup", "Setup.local"):
|
| 45 |
+
if os.path.isfile(os.path.join(d, "Modules", fn)):
|
| 46 |
+
return True
|
| 47 |
+
return False
|
| 48 |
+
|
| 49 |
+
_sys_home = getattr(sys, '_home', None)
|
| 50 |
+
|
| 51 |
+
if os.name == 'nt':
|
| 52 |
+
def _fix_pcbuild(d):
|
| 53 |
+
if d and os.path.normcase(d).startswith(
|
| 54 |
+
os.path.normcase(os.path.join(PREFIX, "PCbuild"))):
|
| 55 |
+
return PREFIX
|
| 56 |
+
return d
|
| 57 |
+
project_base = _fix_pcbuild(project_base)
|
| 58 |
+
_sys_home = _fix_pcbuild(_sys_home)
|
| 59 |
+
|
| 60 |
+
def _python_build():
|
| 61 |
+
if _sys_home:
|
| 62 |
+
return _is_python_source_dir(_sys_home)
|
| 63 |
+
return _is_python_source_dir(project_base)
|
| 64 |
+
|
| 65 |
+
python_build = _python_build()
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
# Calculate the build qualifier flags if they are defined. Adding the flags
|
| 69 |
+
# to the include and lib directories only makes sense for an installation, not
|
| 70 |
+
# an in-source build.
|
| 71 |
+
build_flags = ''
|
| 72 |
+
try:
|
| 73 |
+
if not python_build:
|
| 74 |
+
build_flags = sys.abiflags
|
| 75 |
+
except AttributeError:
|
| 76 |
+
# It's not a configure-based build, so the sys module doesn't have
|
| 77 |
+
# this attribute, which is fine.
|
| 78 |
+
pass
|
| 79 |
+
|
| 80 |
+
def get_python_version():
|
| 81 |
+
"""Return a string containing the major and minor Python version,
|
| 82 |
+
leaving off the patchlevel. Sample return values could be '1.5'
|
| 83 |
+
or '2.2'.
|
| 84 |
+
"""
|
| 85 |
+
return '%d.%d' % sys.version_info[:2]
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
def get_python_inc(plat_specific=0, prefix=None):
|
| 89 |
+
"""Return the directory containing installed Python header files.
|
| 90 |
+
|
| 91 |
+
If 'plat_specific' is false (the default), this is the path to the
|
| 92 |
+
non-platform-specific header files, i.e. Python.h and so on;
|
| 93 |
+
otherwise, this is the path to platform-specific header files
|
| 94 |
+
(namely pyconfig.h).
|
| 95 |
+
|
| 96 |
+
If 'prefix' is supplied, use it instead of sys.base_prefix or
|
| 97 |
+
sys.base_exec_prefix -- i.e., ignore 'plat_specific'.
|
| 98 |
+
"""
|
| 99 |
+
if prefix is None:
|
| 100 |
+
prefix = plat_specific and BASE_EXEC_PREFIX or BASE_PREFIX
|
| 101 |
+
if os.name == "posix":
|
| 102 |
+
if python_build:
|
| 103 |
+
# Assume the executable is in the build directory. The
|
| 104 |
+
# pyconfig.h file should be in the same directory. Since
|
| 105 |
+
# the build directory may not be the source directory, we
|
| 106 |
+
# must use "srcdir" from the makefile to find the "Include"
|
| 107 |
+
# directory.
|
| 108 |
+
if plat_specific:
|
| 109 |
+
return _sys_home or project_base
|
| 110 |
+
else:
|
| 111 |
+
incdir = os.path.join(get_config_var('srcdir'), 'Include')
|
| 112 |
+
return os.path.normpath(incdir)
|
| 113 |
+
python_dir = 'python' + get_python_version() + build_flags
|
| 114 |
+
return os.path.join(prefix, "include", python_dir)
|
| 115 |
+
elif os.name == "nt":
|
| 116 |
+
if python_build:
|
| 117 |
+
# Include both the include and PC dir to ensure we can find
|
| 118 |
+
# pyconfig.h
|
| 119 |
+
return (os.path.join(prefix, "include") + os.path.pathsep +
|
| 120 |
+
os.path.join(prefix, "PC"))
|
| 121 |
+
return os.path.join(prefix, "include")
|
| 122 |
+
else:
|
| 123 |
+
raise DistutilsPlatformError(
|
| 124 |
+
"I don't know where Python installs its C header files "
|
| 125 |
+
"on platform '%s'" % os.name)
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
def get_python_lib(plat_specific=0, standard_lib=0, prefix=None):
|
| 129 |
+
"""Return the directory containing the Python library (standard or
|
| 130 |
+
site additions).
|
| 131 |
+
|
| 132 |
+
If 'plat_specific' is true, return the directory containing
|
| 133 |
+
platform-specific modules, i.e. any module from a non-pure-Python
|
| 134 |
+
module distribution; otherwise, return the platform-shared library
|
| 135 |
+
directory. If 'standard_lib' is true, return the directory
|
| 136 |
+
containing standard Python library modules; otherwise, return the
|
| 137 |
+
directory for site-specific modules.
|
| 138 |
+
|
| 139 |
+
If 'prefix' is supplied, use it instead of sys.base_prefix or
|
| 140 |
+
sys.base_exec_prefix -- i.e., ignore 'plat_specific'.
|
| 141 |
+
"""
|
| 142 |
+
if prefix is None:
|
| 143 |
+
if standard_lib:
|
| 144 |
+
prefix = plat_specific and BASE_EXEC_PREFIX or BASE_PREFIX
|
| 145 |
+
else:
|
| 146 |
+
prefix = plat_specific and EXEC_PREFIX or PREFIX
|
| 147 |
+
|
| 148 |
+
if os.name == "posix":
|
| 149 |
+
libpython = os.path.join(prefix,
|
| 150 |
+
"lib", "python" + get_python_version())
|
| 151 |
+
if standard_lib:
|
| 152 |
+
return libpython
|
| 153 |
+
else:
|
| 154 |
+
return os.path.join(libpython, "site-packages")
|
| 155 |
+
elif os.name == "nt":
|
| 156 |
+
if standard_lib:
|
| 157 |
+
return os.path.join(prefix, "Lib")
|
| 158 |
+
else:
|
| 159 |
+
return os.path.join(prefix, "Lib", "site-packages")
|
| 160 |
+
else:
|
| 161 |
+
raise DistutilsPlatformError(
|
| 162 |
+
"I don't know where Python installs its library "
|
| 163 |
+
"on platform '%s'" % os.name)
|
| 164 |
+
|
| 165 |
+
|
| 166 |
+
|
| 167 |
+
def customize_compiler(compiler):
|
| 168 |
+
"""Do any platform-specific customization of a CCompiler instance.
|
| 169 |
+
|
| 170 |
+
Mainly needed on Unix, so we can plug in the information that
|
| 171 |
+
varies across Unices and is stored in Python's Makefile.
|
| 172 |
+
"""
|
| 173 |
+
if compiler.compiler_type == "unix":
|
| 174 |
+
if sys.platform == "darwin":
|
| 175 |
+
# Perform first-time customization of compiler-related
|
| 176 |
+
# config vars on OS X now that we know we need a compiler.
|
| 177 |
+
# This is primarily to support Pythons from binary
|
| 178 |
+
# installers. The kind and paths to build tools on
|
| 179 |
+
# the user system may vary significantly from the system
|
| 180 |
+
# that Python itself was built on. Also the user OS
|
| 181 |
+
# version and build tools may not support the same set
|
| 182 |
+
# of CPU architectures for universal builds.
|
| 183 |
+
global _config_vars
|
| 184 |
+
# Use get_config_var() to ensure _config_vars is initialized.
|
| 185 |
+
if not get_config_var('CUSTOMIZED_OSX_COMPILER'):
|
| 186 |
+
import _osx_support
|
| 187 |
+
_osx_support.customize_compiler(_config_vars)
|
| 188 |
+
_config_vars['CUSTOMIZED_OSX_COMPILER'] = 'True'
|
| 189 |
+
|
| 190 |
+
(cc, cxx, cflags, ccshared, ldshared, shlib_suffix, ar, ar_flags) = \
|
| 191 |
+
get_config_vars('CC', 'CXX', 'CFLAGS',
|
| 192 |
+
'CCSHARED', 'LDSHARED', 'SHLIB_SUFFIX', 'AR', 'ARFLAGS')
|
| 193 |
+
|
| 194 |
+
if 'CC' in os.environ:
|
| 195 |
+
newcc = os.environ['CC']
|
| 196 |
+
if (sys.platform == 'darwin'
|
| 197 |
+
and 'LDSHARED' not in os.environ
|
| 198 |
+
and ldshared.startswith(cc)):
|
| 199 |
+
# On OS X, if CC is overridden, use that as the default
|
| 200 |
+
# command for LDSHARED as well
|
| 201 |
+
ldshared = newcc + ldshared[len(cc):]
|
| 202 |
+
cc = newcc
|
| 203 |
+
if 'CXX' in os.environ:
|
| 204 |
+
cxx = os.environ['CXX']
|
| 205 |
+
if 'LDSHARED' in os.environ:
|
| 206 |
+
ldshared = os.environ['LDSHARED']
|
| 207 |
+
if 'CPP' in os.environ:
|
| 208 |
+
cpp = os.environ['CPP']
|
| 209 |
+
else:
|
| 210 |
+
cpp = cc + " -E" # not always
|
| 211 |
+
if 'LDFLAGS' in os.environ:
|
| 212 |
+
ldshared = ldshared + ' ' + os.environ['LDFLAGS']
|
| 213 |
+
if 'CFLAGS' in os.environ:
|
| 214 |
+
cflags = cflags + ' ' + os.environ['CFLAGS']
|
| 215 |
+
ldshared = ldshared + ' ' + os.environ['CFLAGS']
|
| 216 |
+
if 'CPPFLAGS' in os.environ:
|
| 217 |
+
cpp = cpp + ' ' + os.environ['CPPFLAGS']
|
| 218 |
+
cflags = cflags + ' ' + os.environ['CPPFLAGS']
|
| 219 |
+
ldshared = ldshared + ' ' + os.environ['CPPFLAGS']
|
| 220 |
+
if 'AR' in os.environ:
|
| 221 |
+
ar = os.environ['AR']
|
| 222 |
+
if 'ARFLAGS' in os.environ:
|
| 223 |
+
archiver = ar + ' ' + os.environ['ARFLAGS']
|
| 224 |
+
else:
|
| 225 |
+
archiver = ar + ' ' + ar_flags
|
| 226 |
+
|
| 227 |
+
cc_cmd = cc + ' ' + cflags
|
| 228 |
+
compiler.set_executables(
|
| 229 |
+
preprocessor=cpp,
|
| 230 |
+
compiler=cc_cmd,
|
| 231 |
+
compiler_so=cc_cmd + ' ' + ccshared,
|
| 232 |
+
compiler_cxx=cxx,
|
| 233 |
+
linker_so=ldshared,
|
| 234 |
+
linker_exe=cc,
|
| 235 |
+
archiver=archiver)
|
| 236 |
+
|
| 237 |
+
if 'RANLIB' in os.environ and 'ranlib' in compiler.executables:
|
| 238 |
+
compiler.set_executables(ranlib=os.environ['RANLIB'])
|
| 239 |
+
|
| 240 |
+
compiler.shared_lib_extension = shlib_suffix
|
| 241 |
+
|
| 242 |
+
|
| 243 |
+
def get_config_h_filename():
|
| 244 |
+
"""Return full pathname of installed pyconfig.h file."""
|
| 245 |
+
if python_build:
|
| 246 |
+
if os.name == "nt":
|
| 247 |
+
inc_dir = os.path.join(_sys_home or project_base, "PC")
|
| 248 |
+
else:
|
| 249 |
+
inc_dir = _sys_home or project_base
|
| 250 |
+
else:
|
| 251 |
+
inc_dir = get_python_inc(plat_specific=1)
|
| 252 |
+
|
| 253 |
+
return os.path.join(inc_dir, 'pyconfig.h')
|
| 254 |
+
|
| 255 |
+
|
| 256 |
+
def get_makefile_filename():
|
| 257 |
+
"""Return full pathname of installed Makefile from the Python build."""
|
| 258 |
+
if python_build:
|
| 259 |
+
return os.path.join(_sys_home or project_base, "Makefile")
|
| 260 |
+
lib_dir = get_python_lib(plat_specific=0, standard_lib=1)
|
| 261 |
+
config_file = 'config-{}{}'.format(get_python_version(), build_flags)
|
| 262 |
+
if hasattr(sys.implementation, '_multiarch'):
|
| 263 |
+
config_file += '-%s' % sys.implementation._multiarch
|
| 264 |
+
return os.path.join(lib_dir, config_file, 'Makefile')
|
| 265 |
+
|
| 266 |
+
|
| 267 |
+
def parse_config_h(fp, g=None):
|
| 268 |
+
"""Parse a config.h-style file.
|
| 269 |
+
|
| 270 |
+
A dictionary containing name/value pairs is returned. If an
|
| 271 |
+
optional dictionary is passed in as the second argument, it is
|
| 272 |
+
used instead of a new dictionary.
|
| 273 |
+
"""
|
| 274 |
+
if g is None:
|
| 275 |
+
g = {}
|
| 276 |
+
define_rx = re.compile("#define ([A-Z][A-Za-z0-9_]+) (.*)\n")
|
| 277 |
+
undef_rx = re.compile("/[*] #undef ([A-Z][A-Za-z0-9_]+) [*]/\n")
|
| 278 |
+
#
|
| 279 |
+
while True:
|
| 280 |
+
line = fp.readline()
|
| 281 |
+
if not line:
|
| 282 |
+
break
|
| 283 |
+
m = define_rx.match(line)
|
| 284 |
+
if m:
|
| 285 |
+
n, v = m.group(1, 2)
|
| 286 |
+
try: v = int(v)
|
| 287 |
+
except ValueError: pass
|
| 288 |
+
g[n] = v
|
| 289 |
+
else:
|
| 290 |
+
m = undef_rx.match(line)
|
| 291 |
+
if m:
|
| 292 |
+
g[m.group(1)] = 0
|
| 293 |
+
return g
|
| 294 |
+
|
| 295 |
+
|
| 296 |
+
# Regexes needed for parsing Makefile (and similar syntaxes,
|
| 297 |
+
# like old-style Setup files).
|
| 298 |
+
_variable_rx = re.compile(r"([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)")
|
| 299 |
+
_findvar1_rx = re.compile(r"\$\(([A-Za-z][A-Za-z0-9_]*)\)")
|
| 300 |
+
_findvar2_rx = re.compile(r"\${([A-Za-z][A-Za-z0-9_]*)}")
|
| 301 |
+
|
| 302 |
+
def parse_makefile(fn, g=None):
|
| 303 |
+
"""Parse a Makefile-style file.
|
| 304 |
+
|
| 305 |
+
A dictionary containing name/value pairs is returned. If an
|
| 306 |
+
optional dictionary is passed in as the second argument, it is
|
| 307 |
+
used instead of a new dictionary.
|
| 308 |
+
"""
|
| 309 |
+
from distutils.text_file import TextFile
|
| 310 |
+
fp = TextFile(fn, strip_comments=1, skip_blanks=1, join_lines=1, errors="surrogateescape")
|
| 311 |
+
|
| 312 |
+
if g is None:
|
| 313 |
+
g = {}
|
| 314 |
+
done = {}
|
| 315 |
+
notdone = {}
|
| 316 |
+
|
| 317 |
+
while True:
|
| 318 |
+
line = fp.readline()
|
| 319 |
+
if line is None: # eof
|
| 320 |
+
break
|
| 321 |
+
m = _variable_rx.match(line)
|
| 322 |
+
if m:
|
| 323 |
+
n, v = m.group(1, 2)
|
| 324 |
+
v = v.strip()
|
| 325 |
+
# `$$' is a literal `$' in make
|
| 326 |
+
tmpv = v.replace('$$', '')
|
| 327 |
+
|
| 328 |
+
if "$" in tmpv:
|
| 329 |
+
notdone[n] = v
|
| 330 |
+
else:
|
| 331 |
+
try:
|
| 332 |
+
v = int(v)
|
| 333 |
+
except ValueError:
|
| 334 |
+
# insert literal `$'
|
| 335 |
+
done[n] = v.replace('$$', '$')
|
| 336 |
+
else:
|
| 337 |
+
done[n] = v
|
| 338 |
+
|
| 339 |
+
# Variables with a 'PY_' prefix in the makefile. These need to
|
| 340 |
+
# be made available without that prefix through sysconfig.
|
| 341 |
+
# Special care is needed to ensure that variable expansion works, even
|
| 342 |
+
# if the expansion uses the name without a prefix.
|
| 343 |
+
renamed_variables = ('CFLAGS', 'LDFLAGS', 'CPPFLAGS')
|
| 344 |
+
|
| 345 |
+
# do variable interpolation here
|
| 346 |
+
while notdone:
|
| 347 |
+
for name in list(notdone):
|
| 348 |
+
value = notdone[name]
|
| 349 |
+
m = _findvar1_rx.search(value) or _findvar2_rx.search(value)
|
| 350 |
+
if m:
|
| 351 |
+
n = m.group(1)
|
| 352 |
+
found = True
|
| 353 |
+
if n in done:
|
| 354 |
+
item = str(done[n])
|
| 355 |
+
elif n in notdone:
|
| 356 |
+
# get it on a subsequent round
|
| 357 |
+
found = False
|
| 358 |
+
elif n in os.environ:
|
| 359 |
+
# do it like make: fall back to environment
|
| 360 |
+
item = os.environ[n]
|
| 361 |
+
|
| 362 |
+
elif n in renamed_variables:
|
| 363 |
+
if name.startswith('PY_') and name[3:] in renamed_variables:
|
| 364 |
+
item = ""
|
| 365 |
+
|
| 366 |
+
elif 'PY_' + n in notdone:
|
| 367 |
+
found = False
|
| 368 |
+
|
| 369 |
+
else:
|
| 370 |
+
item = str(done['PY_' + n])
|
| 371 |
+
else:
|
| 372 |
+
done[n] = item = ""
|
| 373 |
+
if found:
|
| 374 |
+
after = value[m.end():]
|
| 375 |
+
value = value[:m.start()] + item + after
|
| 376 |
+
if "$" in after:
|
| 377 |
+
notdone[name] = value
|
| 378 |
+
else:
|
| 379 |
+
try: value = int(value)
|
| 380 |
+
except ValueError:
|
| 381 |
+
done[name] = value.strip()
|
| 382 |
+
else:
|
| 383 |
+
done[name] = value
|
| 384 |
+
del notdone[name]
|
| 385 |
+
|
| 386 |
+
if name.startswith('PY_') \
|
| 387 |
+
and name[3:] in renamed_variables:
|
| 388 |
+
|
| 389 |
+
name = name[3:]
|
| 390 |
+
if name not in done:
|
| 391 |
+
done[name] = value
|
| 392 |
+
else:
|
| 393 |
+
# bogus variable reference; just drop it since we can't deal
|
| 394 |
+
del notdone[name]
|
| 395 |
+
|
| 396 |
+
fp.close()
|
| 397 |
+
|
| 398 |
+
# strip spurious spaces
|
| 399 |
+
for k, v in done.items():
|
| 400 |
+
if isinstance(v, str):
|
| 401 |
+
done[k] = v.strip()
|
| 402 |
+
|
| 403 |
+
# save the results in the global dictionary
|
| 404 |
+
g.update(done)
|
| 405 |
+
return g
|
| 406 |
+
|
| 407 |
+
|
| 408 |
+
def expand_makefile_vars(s, vars):
|
| 409 |
+
"""Expand Makefile-style variables -- "${foo}" or "$(foo)" -- in
|
| 410 |
+
'string' according to 'vars' (a dictionary mapping variable names to
|
| 411 |
+
values). Variables not present in 'vars' are silently expanded to the
|
| 412 |
+
empty string. The variable values in 'vars' should not contain further
|
| 413 |
+
variable expansions; if 'vars' is the output of 'parse_makefile()',
|
| 414 |
+
you're fine. Returns a variable-expanded version of 's'.
|
| 415 |
+
"""
|
| 416 |
+
|
| 417 |
+
# This algorithm does multiple expansion, so if vars['foo'] contains
|
| 418 |
+
# "${bar}", it will expand ${foo} to ${bar}, and then expand
|
| 419 |
+
# ${bar}... and so forth. This is fine as long as 'vars' comes from
|
| 420 |
+
# 'parse_makefile()', which takes care of such expansions eagerly,
|
| 421 |
+
# according to make's variable expansion semantics.
|
| 422 |
+
|
| 423 |
+
while True:
|
| 424 |
+
m = _findvar1_rx.search(s) or _findvar2_rx.search(s)
|
| 425 |
+
if m:
|
| 426 |
+
(beg, end) = m.span()
|
| 427 |
+
s = s[0:beg] + vars.get(m.group(1)) + s[end:]
|
| 428 |
+
else:
|
| 429 |
+
break
|
| 430 |
+
return s
|
| 431 |
+
|
| 432 |
+
|
| 433 |
+
_config_vars = None
|
| 434 |
+
|
| 435 |
+
def _init_posix():
|
| 436 |
+
"""Initialize the module as appropriate for POSIX systems."""
|
| 437 |
+
# _sysconfigdata is generated at build time, see the sysconfig module
|
| 438 |
+
name = os.environ.get('_PYTHON_SYSCONFIGDATA_NAME',
|
| 439 |
+
os.environ.get('_CONDA_PYTHON_SYSCONFIGDATA_NAME',
|
| 440 |
+
'_sysconfigdata_{abi}_{platform}_{multiarch}'.format(
|
| 441 |
+
abi=sys.abiflags,
|
| 442 |
+
platform=sys.platform,
|
| 443 |
+
multiarch=getattr(sys.implementation, '_multiarch', ''))
|
| 444 |
+
)
|
| 445 |
+
)
|
| 446 |
+
_temp = __import__(name, globals(), locals(), ['build_time_vars'], 0)
|
| 447 |
+
build_time_vars = _temp.build_time_vars
|
| 448 |
+
global _config_vars
|
| 449 |
+
_config_vars = {}
|
| 450 |
+
_config_vars.update(build_time_vars)
|
| 451 |
+
|
| 452 |
+
|
| 453 |
+
def _init_nt():
|
| 454 |
+
"""Initialize the module as appropriate for NT"""
|
| 455 |
+
g = {}
|
| 456 |
+
# set basic install directories
|
| 457 |
+
g['LIBDEST'] = get_python_lib(plat_specific=0, standard_lib=1)
|
| 458 |
+
g['BINLIBDEST'] = get_python_lib(plat_specific=1, standard_lib=1)
|
| 459 |
+
|
| 460 |
+
# XXX hmmm.. a normal install puts include files here
|
| 461 |
+
g['INCLUDEPY'] = get_python_inc(plat_specific=0)
|
| 462 |
+
|
| 463 |
+
g['EXT_SUFFIX'] = _imp.extension_suffixes()[0]
|
| 464 |
+
g['EXE'] = ".exe"
|
| 465 |
+
g['VERSION'] = get_python_version().replace(".", "")
|
| 466 |
+
g['BINDIR'] = os.path.dirname(os.path.abspath(sys.executable))
|
| 467 |
+
|
| 468 |
+
global _config_vars
|
| 469 |
+
_config_vars = g
|
| 470 |
+
|
| 471 |
+
|
| 472 |
+
def get_config_vars(*args):
|
| 473 |
+
"""With no arguments, return a dictionary of all configuration
|
| 474 |
+
variables relevant for the current platform. Generally this includes
|
| 475 |
+
everything needed to build extensions and install both pure modules and
|
| 476 |
+
extensions. On Unix, this means every variable defined in Python's
|
| 477 |
+
installed Makefile; on Windows it's a much smaller set.
|
| 478 |
+
|
| 479 |
+
With arguments, return a list of values that result from looking up
|
| 480 |
+
each argument in the configuration variable dictionary.
|
| 481 |
+
"""
|
| 482 |
+
global _config_vars
|
| 483 |
+
if _config_vars is None:
|
| 484 |
+
func = globals().get("_init_" + os.name)
|
| 485 |
+
if func:
|
| 486 |
+
func()
|
| 487 |
+
else:
|
| 488 |
+
_config_vars = {}
|
| 489 |
+
|
| 490 |
+
# Normalized versions of prefix and exec_prefix are handy to have;
|
| 491 |
+
# in fact, these are the standard versions used most places in the
|
| 492 |
+
# Distutils.
|
| 493 |
+
_config_vars['prefix'] = PREFIX
|
| 494 |
+
_config_vars['exec_prefix'] = EXEC_PREFIX
|
| 495 |
+
|
| 496 |
+
# For backward compatibility, see issue19555
|
| 497 |
+
SO = _config_vars.get('EXT_SUFFIX')
|
| 498 |
+
if SO is not None:
|
| 499 |
+
_config_vars['SO'] = SO
|
| 500 |
+
|
| 501 |
+
# Always convert srcdir to an absolute path
|
| 502 |
+
srcdir = _config_vars.get('srcdir', project_base)
|
| 503 |
+
if os.name == 'posix':
|
| 504 |
+
if python_build:
|
| 505 |
+
# If srcdir is a relative path (typically '.' or '..')
|
| 506 |
+
# then it should be interpreted relative to the directory
|
| 507 |
+
# containing Makefile.
|
| 508 |
+
base = os.path.dirname(get_makefile_filename())
|
| 509 |
+
srcdir = os.path.join(base, srcdir)
|
| 510 |
+
else:
|
| 511 |
+
# srcdir is not meaningful since the installation is
|
| 512 |
+
# spread about the filesystem. We choose the
|
| 513 |
+
# directory containing the Makefile since we know it
|
| 514 |
+
# exists.
|
| 515 |
+
srcdir = os.path.dirname(get_makefile_filename())
|
| 516 |
+
_config_vars['srcdir'] = os.path.abspath(os.path.normpath(srcdir))
|
| 517 |
+
|
| 518 |
+
# Convert srcdir into an absolute path if it appears necessary.
|
| 519 |
+
# Normally it is relative to the build directory. However, during
|
| 520 |
+
# testing, for example, we might be running a non-installed python
|
| 521 |
+
# from a different directory.
|
| 522 |
+
if python_build and os.name == "posix":
|
| 523 |
+
base = project_base
|
| 524 |
+
if (not os.path.isabs(_config_vars['srcdir']) and
|
| 525 |
+
base != os.getcwd()):
|
| 526 |
+
# srcdir is relative and we are not in the same directory
|
| 527 |
+
# as the executable. Assume executable is in the build
|
| 528 |
+
# directory and make srcdir absolute.
|
| 529 |
+
srcdir = os.path.join(base, _config_vars['srcdir'])
|
| 530 |
+
_config_vars['srcdir'] = os.path.normpath(srcdir)
|
| 531 |
+
|
| 532 |
+
# OS X platforms require special customization to handle
|
| 533 |
+
# multi-architecture, multi-os-version installers
|
| 534 |
+
if sys.platform == 'darwin':
|
| 535 |
+
import _osx_support
|
| 536 |
+
_osx_support.customize_config_vars(_config_vars)
|
| 537 |
+
|
| 538 |
+
if args:
|
| 539 |
+
vals = []
|
| 540 |
+
for name in args:
|
| 541 |
+
vals.append(_config_vars.get(name))
|
| 542 |
+
return vals
|
| 543 |
+
else:
|
| 544 |
+
return _config_vars
|
| 545 |
+
|
| 546 |
+
def get_config_var(name):
|
| 547 |
+
"""Return the value of a single variable using the dictionary
|
| 548 |
+
returned by 'get_config_vars()'. Equivalent to
|
| 549 |
+
get_config_vars().get(name)
|
| 550 |
+
"""
|
| 551 |
+
if name == 'SO':
|
| 552 |
+
import warnings
|
| 553 |
+
warnings.warn('SO is deprecated, use EXT_SUFFIX', DeprecationWarning, 2)
|
| 554 |
+
return get_config_vars().get(name)
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/distutils/text_file.py
ADDED
|
@@ -0,0 +1,286 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""text_file
|
| 2 |
+
|
| 3 |
+
provides the TextFile class, which gives an interface to text files
|
| 4 |
+
that (optionally) takes care of stripping comments, ignoring blank
|
| 5 |
+
lines, and joining lines with backslashes."""
|
| 6 |
+
|
| 7 |
+
import sys, io
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
class TextFile:
|
| 11 |
+
"""Provides a file-like object that takes care of all the things you
|
| 12 |
+
commonly want to do when processing a text file that has some
|
| 13 |
+
line-by-line syntax: strip comments (as long as "#" is your
|
| 14 |
+
comment character), skip blank lines, join adjacent lines by
|
| 15 |
+
escaping the newline (ie. backslash at end of line), strip
|
| 16 |
+
leading and/or trailing whitespace. All of these are optional
|
| 17 |
+
and independently controllable.
|
| 18 |
+
|
| 19 |
+
Provides a 'warn()' method so you can generate warning messages that
|
| 20 |
+
report physical line number, even if the logical line in question
|
| 21 |
+
spans multiple physical lines. Also provides 'unreadline()' for
|
| 22 |
+
implementing line-at-a-time lookahead.
|
| 23 |
+
|
| 24 |
+
Constructor is called as:
|
| 25 |
+
|
| 26 |
+
TextFile (filename=None, file=None, **options)
|
| 27 |
+
|
| 28 |
+
It bombs (RuntimeError) if both 'filename' and 'file' are None;
|
| 29 |
+
'filename' should be a string, and 'file' a file object (or
|
| 30 |
+
something that provides 'readline()' and 'close()' methods). It is
|
| 31 |
+
recommended that you supply at least 'filename', so that TextFile
|
| 32 |
+
can include it in warning messages. If 'file' is not supplied,
|
| 33 |
+
TextFile creates its own using 'io.open()'.
|
| 34 |
+
|
| 35 |
+
The options are all boolean, and affect the value returned by
|
| 36 |
+
'readline()':
|
| 37 |
+
strip_comments [default: true]
|
| 38 |
+
strip from "#" to end-of-line, as well as any whitespace
|
| 39 |
+
leading up to the "#" -- unless it is escaped by a backslash
|
| 40 |
+
lstrip_ws [default: false]
|
| 41 |
+
strip leading whitespace from each line before returning it
|
| 42 |
+
rstrip_ws [default: true]
|
| 43 |
+
strip trailing whitespace (including line terminator!) from
|
| 44 |
+
each line before returning it
|
| 45 |
+
skip_blanks [default: true}
|
| 46 |
+
skip lines that are empty *after* stripping comments and
|
| 47 |
+
whitespace. (If both lstrip_ws and rstrip_ws are false,
|
| 48 |
+
then some lines may consist of solely whitespace: these will
|
| 49 |
+
*not* be skipped, even if 'skip_blanks' is true.)
|
| 50 |
+
join_lines [default: false]
|
| 51 |
+
if a backslash is the last non-newline character on a line
|
| 52 |
+
after stripping comments and whitespace, join the following line
|
| 53 |
+
to it to form one "logical line"; if N consecutive lines end
|
| 54 |
+
with a backslash, then N+1 physical lines will be joined to
|
| 55 |
+
form one logical line.
|
| 56 |
+
collapse_join [default: false]
|
| 57 |
+
strip leading whitespace from lines that are joined to their
|
| 58 |
+
predecessor; only matters if (join_lines and not lstrip_ws)
|
| 59 |
+
errors [default: 'strict']
|
| 60 |
+
error handler used to decode the file content
|
| 61 |
+
|
| 62 |
+
Note that since 'rstrip_ws' can strip the trailing newline, the
|
| 63 |
+
semantics of 'readline()' must differ from those of the builtin file
|
| 64 |
+
object's 'readline()' method! In particular, 'readline()' returns
|
| 65 |
+
None for end-of-file: an empty string might just be a blank line (or
|
| 66 |
+
an all-whitespace line), if 'rstrip_ws' is true but 'skip_blanks' is
|
| 67 |
+
not."""
|
| 68 |
+
|
| 69 |
+
default_options = { 'strip_comments': 1,
|
| 70 |
+
'skip_blanks': 1,
|
| 71 |
+
'lstrip_ws': 0,
|
| 72 |
+
'rstrip_ws': 1,
|
| 73 |
+
'join_lines': 0,
|
| 74 |
+
'collapse_join': 0,
|
| 75 |
+
'errors': 'strict',
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
def __init__(self, filename=None, file=None, **options):
|
| 79 |
+
"""Construct a new TextFile object. At least one of 'filename'
|
| 80 |
+
(a string) and 'file' (a file-like object) must be supplied.
|
| 81 |
+
They keyword argument options are described above and affect
|
| 82 |
+
the values returned by 'readline()'."""
|
| 83 |
+
if filename is None and file is None:
|
| 84 |
+
raise RuntimeError("you must supply either or both of 'filename' and 'file'")
|
| 85 |
+
|
| 86 |
+
# set values for all options -- either from client option hash
|
| 87 |
+
# or fallback to default_options
|
| 88 |
+
for opt in self.default_options.keys():
|
| 89 |
+
if opt in options:
|
| 90 |
+
setattr(self, opt, options[opt])
|
| 91 |
+
else:
|
| 92 |
+
setattr(self, opt, self.default_options[opt])
|
| 93 |
+
|
| 94 |
+
# sanity check client option hash
|
| 95 |
+
for opt in options.keys():
|
| 96 |
+
if opt not in self.default_options:
|
| 97 |
+
raise KeyError("invalid TextFile option '%s'" % opt)
|
| 98 |
+
|
| 99 |
+
if file is None:
|
| 100 |
+
self.open(filename)
|
| 101 |
+
else:
|
| 102 |
+
self.filename = filename
|
| 103 |
+
self.file = file
|
| 104 |
+
self.current_line = 0 # assuming that file is at BOF!
|
| 105 |
+
|
| 106 |
+
# 'linebuf' is a stack of lines that will be emptied before we
|
| 107 |
+
# actually read from the file; it's only populated by an
|
| 108 |
+
# 'unreadline()' operation
|
| 109 |
+
self.linebuf = []
|
| 110 |
+
|
| 111 |
+
def open(self, filename):
|
| 112 |
+
"""Open a new file named 'filename'. This overrides both the
|
| 113 |
+
'filename' and 'file' arguments to the constructor."""
|
| 114 |
+
self.filename = filename
|
| 115 |
+
self.file = io.open(self.filename, 'r', errors=self.errors)
|
| 116 |
+
self.current_line = 0
|
| 117 |
+
|
| 118 |
+
def close(self):
|
| 119 |
+
"""Close the current file and forget everything we know about it
|
| 120 |
+
(filename, current line number)."""
|
| 121 |
+
file = self.file
|
| 122 |
+
self.file = None
|
| 123 |
+
self.filename = None
|
| 124 |
+
self.current_line = None
|
| 125 |
+
file.close()
|
| 126 |
+
|
| 127 |
+
def gen_error(self, msg, line=None):
|
| 128 |
+
outmsg = []
|
| 129 |
+
if line is None:
|
| 130 |
+
line = self.current_line
|
| 131 |
+
outmsg.append(self.filename + ", ")
|
| 132 |
+
if isinstance(line, (list, tuple)):
|
| 133 |
+
outmsg.append("lines %d-%d: " % tuple(line))
|
| 134 |
+
else:
|
| 135 |
+
outmsg.append("line %d: " % line)
|
| 136 |
+
outmsg.append(str(msg))
|
| 137 |
+
return "".join(outmsg)
|
| 138 |
+
|
| 139 |
+
def error(self, msg, line=None):
|
| 140 |
+
raise ValueError("error: " + self.gen_error(msg, line))
|
| 141 |
+
|
| 142 |
+
def warn(self, msg, line=None):
|
| 143 |
+
"""Print (to stderr) a warning message tied to the current logical
|
| 144 |
+
line in the current file. If the current logical line in the
|
| 145 |
+
file spans multiple physical lines, the warning refers to the
|
| 146 |
+
whole range, eg. "lines 3-5". If 'line' supplied, it overrides
|
| 147 |
+
the current line number; it may be a list or tuple to indicate a
|
| 148 |
+
range of physical lines, or an integer for a single physical
|
| 149 |
+
line."""
|
| 150 |
+
sys.stderr.write("warning: " + self.gen_error(msg, line) + "\n")
|
| 151 |
+
|
| 152 |
+
def readline(self):
|
| 153 |
+
"""Read and return a single logical line from the current file (or
|
| 154 |
+
from an internal buffer if lines have previously been "unread"
|
| 155 |
+
with 'unreadline()'). If the 'join_lines' option is true, this
|
| 156 |
+
may involve reading multiple physical lines concatenated into a
|
| 157 |
+
single string. Updates the current line number, so calling
|
| 158 |
+
'warn()' after 'readline()' emits a warning about the physical
|
| 159 |
+
line(s) just read. Returns None on end-of-file, since the empty
|
| 160 |
+
string can occur if 'rstrip_ws' is true but 'strip_blanks' is
|
| 161 |
+
not."""
|
| 162 |
+
# If any "unread" lines waiting in 'linebuf', return the top
|
| 163 |
+
# one. (We don't actually buffer read-ahead data -- lines only
|
| 164 |
+
# get put in 'linebuf' if the client explicitly does an
|
| 165 |
+
# 'unreadline()'.
|
| 166 |
+
if self.linebuf:
|
| 167 |
+
line = self.linebuf[-1]
|
| 168 |
+
del self.linebuf[-1]
|
| 169 |
+
return line
|
| 170 |
+
|
| 171 |
+
buildup_line = ''
|
| 172 |
+
|
| 173 |
+
while True:
|
| 174 |
+
# read the line, make it None if EOF
|
| 175 |
+
line = self.file.readline()
|
| 176 |
+
if line == '':
|
| 177 |
+
line = None
|
| 178 |
+
|
| 179 |
+
if self.strip_comments and line:
|
| 180 |
+
|
| 181 |
+
# Look for the first "#" in the line. If none, never
|
| 182 |
+
# mind. If we find one and it's the first character, or
|
| 183 |
+
# is not preceded by "\", then it starts a comment --
|
| 184 |
+
# strip the comment, strip whitespace before it, and
|
| 185 |
+
# carry on. Otherwise, it's just an escaped "#", so
|
| 186 |
+
# unescape it (and any other escaped "#"'s that might be
|
| 187 |
+
# lurking in there) and otherwise leave the line alone.
|
| 188 |
+
|
| 189 |
+
pos = line.find("#")
|
| 190 |
+
if pos == -1: # no "#" -- no comments
|
| 191 |
+
pass
|
| 192 |
+
|
| 193 |
+
# It's definitely a comment -- either "#" is the first
|
| 194 |
+
# character, or it's elsewhere and unescaped.
|
| 195 |
+
elif pos == 0 or line[pos-1] != "\\":
|
| 196 |
+
# Have to preserve the trailing newline, because it's
|
| 197 |
+
# the job of a later step (rstrip_ws) to remove it --
|
| 198 |
+
# and if rstrip_ws is false, we'd better preserve it!
|
| 199 |
+
# (NB. this means that if the final line is all comment
|
| 200 |
+
# and has no trailing newline, we will think that it's
|
| 201 |
+
# EOF; I think that's OK.)
|
| 202 |
+
eol = (line[-1] == '\n') and '\n' or ''
|
| 203 |
+
line = line[0:pos] + eol
|
| 204 |
+
|
| 205 |
+
# If all that's left is whitespace, then skip line
|
| 206 |
+
# *now*, before we try to join it to 'buildup_line' --
|
| 207 |
+
# that way constructs like
|
| 208 |
+
# hello \\
|
| 209 |
+
# # comment that should be ignored
|
| 210 |
+
# there
|
| 211 |
+
# result in "hello there".
|
| 212 |
+
if line.strip() == "":
|
| 213 |
+
continue
|
| 214 |
+
else: # it's an escaped "#"
|
| 215 |
+
line = line.replace("\\#", "#")
|
| 216 |
+
|
| 217 |
+
# did previous line end with a backslash? then accumulate
|
| 218 |
+
if self.join_lines and buildup_line:
|
| 219 |
+
# oops: end of file
|
| 220 |
+
if line is None:
|
| 221 |
+
self.warn("continuation line immediately precedes "
|
| 222 |
+
"end-of-file")
|
| 223 |
+
return buildup_line
|
| 224 |
+
|
| 225 |
+
if self.collapse_join:
|
| 226 |
+
line = line.lstrip()
|
| 227 |
+
line = buildup_line + line
|
| 228 |
+
|
| 229 |
+
# careful: pay attention to line number when incrementing it
|
| 230 |
+
if isinstance(self.current_line, list):
|
| 231 |
+
self.current_line[1] = self.current_line[1] + 1
|
| 232 |
+
else:
|
| 233 |
+
self.current_line = [self.current_line,
|
| 234 |
+
self.current_line + 1]
|
| 235 |
+
# just an ordinary line, read it as usual
|
| 236 |
+
else:
|
| 237 |
+
if line is None: # eof
|
| 238 |
+
return None
|
| 239 |
+
|
| 240 |
+
# still have to be careful about incrementing the line number!
|
| 241 |
+
if isinstance(self.current_line, list):
|
| 242 |
+
self.current_line = self.current_line[1] + 1
|
| 243 |
+
else:
|
| 244 |
+
self.current_line = self.current_line + 1
|
| 245 |
+
|
| 246 |
+
# strip whitespace however the client wants (leading and
|
| 247 |
+
# trailing, or one or the other, or neither)
|
| 248 |
+
if self.lstrip_ws and self.rstrip_ws:
|
| 249 |
+
line = line.strip()
|
| 250 |
+
elif self.lstrip_ws:
|
| 251 |
+
line = line.lstrip()
|
| 252 |
+
elif self.rstrip_ws:
|
| 253 |
+
line = line.rstrip()
|
| 254 |
+
|
| 255 |
+
# blank line (whether we rstrip'ed or not)? skip to next line
|
| 256 |
+
# if appropriate
|
| 257 |
+
if (line == '' or line == '\n') and self.skip_blanks:
|
| 258 |
+
continue
|
| 259 |
+
|
| 260 |
+
if self.join_lines:
|
| 261 |
+
if line[-1] == '\\':
|
| 262 |
+
buildup_line = line[:-1]
|
| 263 |
+
continue
|
| 264 |
+
|
| 265 |
+
if line[-2:] == '\\\n':
|
| 266 |
+
buildup_line = line[0:-2] + '\n'
|
| 267 |
+
continue
|
| 268 |
+
|
| 269 |
+
# well, I guess there's some actual content there: return it
|
| 270 |
+
return line
|
| 271 |
+
|
| 272 |
+
def readlines(self):
|
| 273 |
+
"""Read and return the list of all logical lines remaining in the
|
| 274 |
+
current file."""
|
| 275 |
+
lines = []
|
| 276 |
+
while True:
|
| 277 |
+
line = self.readline()
|
| 278 |
+
if line is None:
|
| 279 |
+
return lines
|
| 280 |
+
lines.append(line)
|
| 281 |
+
|
| 282 |
+
def unreadline(self, line):
|
| 283 |
+
"""Push 'line' (a string) onto an internal buffer that will be
|
| 284 |
+
checked by future 'readline()' calls. Handy for implementing
|
| 285 |
+
a parser with line-at-a-time lookahead."""
|
| 286 |
+
self.linebuf.append(line)
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/HISTORY.txt
ADDED
|
@@ -0,0 +1,296 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
IDLE History
|
| 2 |
+
============
|
| 3 |
+
|
| 4 |
+
This file contains the release messages for previous IDLE releases.
|
| 5 |
+
As you read on you go back to the dark ages of IDLE's history.
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
What's New in IDLEfork 0.8.1?
|
| 9 |
+
=============================
|
| 10 |
+
|
| 11 |
+
*Release date: 22-Jul-2001*
|
| 12 |
+
|
| 13 |
+
- New tarball released as a result of the 'revitalisation' of the IDLEfork
|
| 14 |
+
project.
|
| 15 |
+
|
| 16 |
+
- This release requires python 2.1 or better. Compatibility with earlier
|
| 17 |
+
versions of python (especially ancient ones like 1.5x) is no longer a
|
| 18 |
+
priority in IDLEfork development.
|
| 19 |
+
|
| 20 |
+
- This release is based on a merging of the earlier IDLE fork work with current
|
| 21 |
+
cvs IDLE (post IDLE version 0.8), with some minor additional coding by Kurt
|
| 22 |
+
B. Kaiser and Stephen M. Gava.
|
| 23 |
+
|
| 24 |
+
- This release is basically functional but also contains some known breakages,
|
| 25 |
+
particularly with running things from the shell window. Also the debugger is
|
| 26 |
+
not working, but I believe this was the case with the previous IDLE fork
|
| 27 |
+
release (0.7.1) as well.
|
| 28 |
+
|
| 29 |
+
- This release is being made now to mark the point at which IDLEfork is
|
| 30 |
+
launching into a new stage of development.
|
| 31 |
+
|
| 32 |
+
- IDLEfork CVS will now be branched to enable further development and
|
| 33 |
+
exploration of the two "execution in a remote process" patches submitted by
|
| 34 |
+
David Scherer (David's is currently in IDLEfork) and GvR, while stabilisation
|
| 35 |
+
and development of less heavyweight improvements (like user customisation)
|
| 36 |
+
can continue on the trunk.
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
What's New in IDLEfork 0.7.1?
|
| 40 |
+
==============================
|
| 41 |
+
|
| 42 |
+
*Release date: 15-Aug-2000*
|
| 43 |
+
|
| 44 |
+
- First project tarball released.
|
| 45 |
+
|
| 46 |
+
- This was the first release of IDLE fork, which at this stage was a
|
| 47 |
+
combination of IDLE 0.5 and the VPython idle fork, with additional changes
|
| 48 |
+
coded by David Scherer, Peter Schneider-Kamp and Nicholas Riley.
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
IDLEfork 0.7.1 - 29 May 2000
|
| 53 |
+
-----------------------------
|
| 54 |
+
|
| 55 |
+
David Scherer <dscherer@cmu.edu>
|
| 56 |
+
|
| 57 |
+
- This is a modification of the CVS version of IDLE 0.5, updated as of
|
| 58 |
+
2000-03-09. It is alpha software and might be unstable. If it breaks, you
|
| 59 |
+
get to keep both pieces.
|
| 60 |
+
|
| 61 |
+
- If you have problems or suggestions, you should either contact me or post to
|
| 62 |
+
the list at http://www.python.org/mailman/listinfo/idle-dev (making it clear
|
| 63 |
+
that you are using this modified version of IDLE).
|
| 64 |
+
|
| 65 |
+
- Changes:
|
| 66 |
+
|
| 67 |
+
- The ExecBinding module, a replacement for ScriptBinding, executes programs
|
| 68 |
+
in a separate process, piping standard I/O through an RPC mechanism to an
|
| 69 |
+
OnDemandOutputWindow in IDLE. It supports executing unnamed programs
|
| 70 |
+
(through a temporary file). It does not yet support debugging.
|
| 71 |
+
|
| 72 |
+
- When running programs with ExecBinding, tracebacks will be clipped to
|
| 73 |
+
exclude system modules. If, however, a system module calls back into the
|
| 74 |
+
user program, that part of the traceback will be shown.
|
| 75 |
+
|
| 76 |
+
- The OnDemandOutputWindow class has been improved. In particular, it now
|
| 77 |
+
supports a readline() function used to implement user input, and a
|
| 78 |
+
scroll_clear() operation which is used to hide the output of a previous run
|
| 79 |
+
by scrolling it out of the window.
|
| 80 |
+
|
| 81 |
+
- Startup behavior has been changed. By default IDLE starts up with just a
|
| 82 |
+
blank editor window, rather than an interactive window. Opening a file in
|
| 83 |
+
such a blank window replaces the (nonexistent) contents of that window
|
| 84 |
+
instead of creating another window. Because of the need to have a
|
| 85 |
+
well-known port for the ExecBinding protocol, only one copy of IDLE can be
|
| 86 |
+
running. Additional invocations use the RPC mechanism to report their
|
| 87 |
+
command line arguments to the copy already running.
|
| 88 |
+
|
| 89 |
+
- The menus have been reorganized. In particular, the excessively large
|
| 90 |
+
'edit' menu has been split up into 'edit', 'format', and 'run'.
|
| 91 |
+
|
| 92 |
+
- 'Python Documentation' now works on Windows, if the win32api module is
|
| 93 |
+
present.
|
| 94 |
+
|
| 95 |
+
- A few key bindings have been changed: F1 now loads Python Documentation
|
| 96 |
+
instead of the IDLE help; shift-TAB is now a synonym for unindent.
|
| 97 |
+
|
| 98 |
+
- New modules:
|
| 99 |
+
|
| 100 |
+
ExecBinding.py Executes program through loader
|
| 101 |
+
loader.py Bootstraps user program
|
| 102 |
+
protocol.py RPC protocol
|
| 103 |
+
Remote.py User-process interpreter
|
| 104 |
+
spawn.py OS-specific code to start programs
|
| 105 |
+
|
| 106 |
+
- Files modified:
|
| 107 |
+
|
| 108 |
+
autoindent.py ( bindings tweaked )
|
| 109 |
+
bindings.py ( menus reorganized )
|
| 110 |
+
config.txt ( execbinding enabled )
|
| 111 |
+
editorwindow.py ( new menus, fixed 'Python Documentation' )
|
| 112 |
+
filelist.py ( hook for "open in same window" )
|
| 113 |
+
formatparagraph.py ( bindings tweaked )
|
| 114 |
+
idle.bat ( removed absolute pathname )
|
| 115 |
+
idle.pyw ( weird bug due to import with same name? )
|
| 116 |
+
iobinding.py ( open in same window, EOL convention )
|
| 117 |
+
keydefs.py ( bindings tweaked )
|
| 118 |
+
outputwindow.py ( readline, scroll_clear, etc )
|
| 119 |
+
pyshell.py ( changed startup behavior )
|
| 120 |
+
readme.txt ( <Recursion on file with id=1234567> )
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
IDLE 0.5 - February 2000 - Release Notes
|
| 125 |
+
----------------------------------------
|
| 126 |
+
|
| 127 |
+
This is an early release of IDLE, my own attempt at a Tkinter-based
|
| 128 |
+
IDE for Python.
|
| 129 |
+
|
| 130 |
+
(For a more detailed change log, see the file ChangeLog.)
|
| 131 |
+
|
| 132 |
+
FEATURES
|
| 133 |
+
|
| 134 |
+
IDLE has the following features:
|
| 135 |
+
|
| 136 |
+
- coded in 100% pure Python, using the Tkinter GUI toolkit (i.e. Tcl/Tk)
|
| 137 |
+
|
| 138 |
+
- cross-platform: works on Windows and Unix (on the Mac, there are
|
| 139 |
+
currently problems with Tcl/Tk)
|
| 140 |
+
|
| 141 |
+
- multi-window text editor with multiple undo, Python colorizing
|
| 142 |
+
and many other features, e.g. smart indent and call tips
|
| 143 |
+
|
| 144 |
+
- Python shell window (a.k.a. interactive interpreter)
|
| 145 |
+
|
| 146 |
+
- debugger (not complete, but you can set breakpoints, view and step)
|
| 147 |
+
|
| 148 |
+
USAGE
|
| 149 |
+
|
| 150 |
+
The main program is in the file "idle.py"; on Unix, you should be able
|
| 151 |
+
to run it by typing "./idle.py" to your shell. On Windows, you can
|
| 152 |
+
run it by double-clicking it; you can use idle.pyw to avoid popping up
|
| 153 |
+
a DOS console. If you want to pass command line arguments on Windows,
|
| 154 |
+
use the batch file idle.bat.
|
| 155 |
+
|
| 156 |
+
Command line arguments: files passed on the command line are executed,
|
| 157 |
+
not opened for editing, unless you give the -e command line option.
|
| 158 |
+
Try "./idle.py -h" to see other command line options.
|
| 159 |
+
|
| 160 |
+
IDLE requires Python 1.5.2, so it is currently only usable with a
|
| 161 |
+
Python 1.5.2 distribution. (An older version of IDLE is distributed
|
| 162 |
+
with Python 1.5.2; you can drop this version on top of it.)
|
| 163 |
+
|
| 164 |
+
COPYRIGHT
|
| 165 |
+
|
| 166 |
+
IDLE is covered by the standard Python copyright notice
|
| 167 |
+
(http://www.python.org/doc/Copyright.html).
|
| 168 |
+
|
| 169 |
+
|
| 170 |
+
New in IDLE 0.5 (2/15/2000)
|
| 171 |
+
---------------------------
|
| 172 |
+
|
| 173 |
+
Tons of stuff, much of it contributed by Tim Peters and Mark Hammond:
|
| 174 |
+
|
| 175 |
+
- Status bar, displaying current line/column (Moshe Zadka).
|
| 176 |
+
|
| 177 |
+
- Better stack viewer, using tree widget. (XXX Only used by Stack
|
| 178 |
+
Viewer menu, not by the debugger.)
|
| 179 |
+
|
| 180 |
+
- Format paragraph now recognizes Python block comments and reformats
|
| 181 |
+
them correctly (MH)
|
| 182 |
+
|
| 183 |
+
- New version of pyclbr.py parses top-level functions and understands
|
| 184 |
+
much more of Python's syntax; this is reflected in the class and path
|
| 185 |
+
browsers (TP)
|
| 186 |
+
|
| 187 |
+
- Much better auto-indent; knows how to indent the insides of
|
| 188 |
+
multi-line statements (TP)
|
| 189 |
+
|
| 190 |
+
- Call tip window pops up when you type the name of a known function
|
| 191 |
+
followed by an open parenthesis. Hit ESC or click elsewhere in the
|
| 192 |
+
window to close the tip window (MH)
|
| 193 |
+
|
| 194 |
+
- Comment out region now inserts ## to make it stand out more (TP)
|
| 195 |
+
|
| 196 |
+
- New path and class browsers based on a tree widget that looks
|
| 197 |
+
familiar to Windows users
|
| 198 |
+
|
| 199 |
+
- Reworked script running commands to be more intuitive: I/O now
|
| 200 |
+
always goes to the *Python Shell* window, and raw_input() works
|
| 201 |
+
correctly. You use F5 to import/reload a module: this adds the module
|
| 202 |
+
name to the __main__ namespace. You use Control-F5 to run a script:
|
| 203 |
+
this runs the script *in* the __main__ namespace. The latter also
|
| 204 |
+
sets sys.argv[] to the script name
|
| 205 |
+
|
| 206 |
+
|
| 207 |
+
New in IDLE 0.4 (4/7/99)
|
| 208 |
+
------------------------
|
| 209 |
+
|
| 210 |
+
Most important change: a new menu entry "File -> Path browser", shows
|
| 211 |
+
a 4-column hierarchical browser which lets you browse sys.path,
|
| 212 |
+
directories, modules, and classes. Yes, it's a superset of the Class
|
| 213 |
+
browser menu entry. There's also a new internal module,
|
| 214 |
+
MultiScrolledLists.py, which provides the framework for this dialog.
|
| 215 |
+
|
| 216 |
+
|
| 217 |
+
New in IDLE 0.3 (2/17/99)
|
| 218 |
+
-------------------------
|
| 219 |
+
|
| 220 |
+
Most important changes:
|
| 221 |
+
|
| 222 |
+
- Enabled support for running a module, with or without the debugger.
|
| 223 |
+
Output goes to a new window. Pressing F5 in a module is effectively a
|
| 224 |
+
reload of that module; Control-F5 loads it under the debugger.
|
| 225 |
+
|
| 226 |
+
- Re-enable tearing off the Windows menu, and make a torn-off Windows
|
| 227 |
+
menu update itself whenever a window is opened or closed.
|
| 228 |
+
|
| 229 |
+
- Menu items can now be have a checkbox (when the menu label starts
|
| 230 |
+
with "!"); use this for the Debugger and "Auto-open stack viewer"
|
| 231 |
+
(was: JIT stack viewer) menu items.
|
| 232 |
+
|
| 233 |
+
- Added a Quit button to the Debugger API.
|
| 234 |
+
|
| 235 |
+
- The current directory is explicitly inserted into sys.path.
|
| 236 |
+
|
| 237 |
+
- Fix the debugger (when using Python 1.5.2b2) to use canonical
|
| 238 |
+
filenames for breakpoints, so these actually work. (There's still a
|
| 239 |
+
lot of work to be done to the management of breakpoints in the
|
| 240 |
+
debugger though.)
|
| 241 |
+
|
| 242 |
+
- Closing a window that is still colorizing now actually works.
|
| 243 |
+
|
| 244 |
+
- Allow dragging of the separator between the two list boxes in the
|
| 245 |
+
class browser.
|
| 246 |
+
|
| 247 |
+
- Bind ESC to "close window" of the debugger, stack viewer and class
|
| 248 |
+
browser. It removes the selection highlighting in regular text
|
| 249 |
+
windows. (These are standard Windows conventions.)
|
| 250 |
+
|
| 251 |
+
|
| 252 |
+
New in IDLE 0.2 (1/8/99)
|
| 253 |
+
------------------------
|
| 254 |
+
|
| 255 |
+
Lots of changes; here are the highlights:
|
| 256 |
+
|
| 257 |
+
General:
|
| 258 |
+
|
| 259 |
+
- You can now write and configure your own IDLE extension modules; see
|
| 260 |
+
extend.txt.
|
| 261 |
+
|
| 262 |
+
|
| 263 |
+
File menu:
|
| 264 |
+
|
| 265 |
+
The command to open the Python shell window is now in the File menu.
|
| 266 |
+
|
| 267 |
+
|
| 268 |
+
Edit menu:
|
| 269 |
+
|
| 270 |
+
New Find dialog with more options; replace dialog; find in files dialog.
|
| 271 |
+
|
| 272 |
+
Commands to tabify or untabify a region.
|
| 273 |
+
|
| 274 |
+
Command to format a paragraph.
|
| 275 |
+
|
| 276 |
+
|
| 277 |
+
Debug menu:
|
| 278 |
+
|
| 279 |
+
JIT (Just-In-Time) stack viewer toggle -- if set, the stack viewer
|
| 280 |
+
automaticall pops up when you get a traceback.
|
| 281 |
+
|
| 282 |
+
Windows menu:
|
| 283 |
+
|
| 284 |
+
Zoom height -- make the window full height.
|
| 285 |
+
|
| 286 |
+
|
| 287 |
+
Help menu:
|
| 288 |
+
|
| 289 |
+
The help text now show up in a regular window so you can search and
|
| 290 |
+
even edit it if you like.
|
| 291 |
+
|
| 292 |
+
|
| 293 |
+
|
| 294 |
+
IDLE 0.1 was distributed with the Python 1.5.2b1 release on 12/22/98.
|
| 295 |
+
|
| 296 |
+
======================================================================
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/NEWS2x.txt
ADDED
|
@@ -0,0 +1,660 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
What's New in IDLE 2.7? (Merged into 3.1 before 2.7 release.)
|
| 2 |
+
=======================
|
| 3 |
+
*Release date: XX-XXX-2010*
|
| 4 |
+
|
| 5 |
+
- idle.py modified and simplified to better support developing experimental
|
| 6 |
+
versions of IDLE which are not installed in the standard location.
|
| 7 |
+
|
| 8 |
+
- OutputWindow/PyShell right click menu "Go to file/line" wasn't working with
|
| 9 |
+
file paths containing spaces. Bug 5559.
|
| 10 |
+
|
| 11 |
+
- Windows: Version string for the .chm help file changed, file not being
|
| 12 |
+
accessed Patch 5783 Guilherme Polo
|
| 13 |
+
|
| 14 |
+
- Allow multiple IDLE GUI/subprocess pairs to exist simultaneously. Thanks to
|
| 15 |
+
David Scherer for suggesting the use of an ephemeral port for the GUI.
|
| 16 |
+
Patch 1529142 Weeble.
|
| 17 |
+
|
| 18 |
+
- Remove port spec from run.py and fix bug where subprocess fails to
|
| 19 |
+
extract port from command line when warnings are present.
|
| 20 |
+
|
| 21 |
+
- Tk 8.5 Text widget requires 'wordprocessor' tabstyle attr to handle
|
| 22 |
+
mixed space/tab properly. Issue 5129, patch by Guilherme Polo.
|
| 23 |
+
|
| 24 |
+
- Issue #3549: On MacOS the preferences menu was not present
|
| 25 |
+
|
| 26 |
+
- IDLE would print a "Unhandled server exception!" message when internal
|
| 27 |
+
debugging is enabled.
|
| 28 |
+
|
| 29 |
+
- Issue #4455: IDLE failed to display the windows list when two windows have
|
| 30 |
+
the same title.
|
| 31 |
+
|
| 32 |
+
- Issue #4383: When IDLE cannot make the connection to its subprocess, it would
|
| 33 |
+
fail to properly display the error message.
|
| 34 |
+
|
| 35 |
+
- help() was not paging to the shell. Issue1650.
|
| 36 |
+
|
| 37 |
+
- CodeContext was not importing.
|
| 38 |
+
|
| 39 |
+
- Corrected two 3.0 compatibility errors reported by Mark Summerfield:
|
| 40 |
+
http://mail.python.org/pipermail/python-3000/2007-December/011491.html
|
| 41 |
+
|
| 42 |
+
- Shell was not colorizing due to bug introduced at r57998, Bug 1586.
|
| 43 |
+
|
| 44 |
+
- Issue #1585: IDLE uses non-existent xrange() function.
|
| 45 |
+
|
| 46 |
+
- Windows EOL sequence not converted correctly, encoding error.
|
| 47 |
+
Caused file save to fail. Bug 1130.
|
| 48 |
+
|
| 49 |
+
- IDLE converted to Python 3000 syntax.
|
| 50 |
+
|
| 51 |
+
- Strings became Unicode.
|
| 52 |
+
|
| 53 |
+
- CallTips module now uses the inspect module to produce the argspec.
|
| 54 |
+
|
| 55 |
+
- IDLE modules now use absolute import instead of implied relative import.
|
| 56 |
+
|
| 57 |
+
- atexit call replaces sys.exitfunc. The functionality of delete-exitfunc flag
|
| 58 |
+
in config-main.cfg remains unchanged: if set, registered exit functions will
|
| 59 |
+
be cleared before IDLE exits.
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
What's New in IDLE 2.6
|
| 63 |
+
======================
|
| 64 |
+
*Release date: 01-Oct-2008*, merged into 3.0 releases detailed above (3.0rc2)
|
| 65 |
+
|
| 66 |
+
- Issue #2665: On Windows, an IDLE installation upgraded from an old version
|
| 67 |
+
would not start if a custom theme was defined.
|
| 68 |
+
|
| 69 |
+
- Home / Control-A toggles between left margin and end of leading white
|
| 70 |
+
space. issue1196903, patch by Jeff Shute.
|
| 71 |
+
|
| 72 |
+
- Improved AutoCompleteWindow logic. issue2062, patch by Tal Einat.
|
| 73 |
+
|
| 74 |
+
- Autocompletion of filenames now support alternate separators, e.g. the
|
| 75 |
+
'/' char on Windows. issue2061 Patch by Tal Einat.
|
| 76 |
+
|
| 77 |
+
- Configured selection highlighting colors were ignored; updating highlighting
|
| 78 |
+
in the config dialog would cause non-Python files to be colored as if they
|
| 79 |
+
were Python source; improve use of ColorDelagator. Patch 1334. Tal Einat.
|
| 80 |
+
|
| 81 |
+
- ScriptBinding event handlers weren't returning 'break'. Patch 2050, Tal Einat
|
| 82 |
+
|
| 83 |
+
- There was an error on exit if no sys.exitfunc was defined. Issue 1647.
|
| 84 |
+
|
| 85 |
+
- Could not open files in .idlerc directory if latter was hidden on Windows.
|
| 86 |
+
Issue 1743, Issue 1862.
|
| 87 |
+
|
| 88 |
+
- Configure Dialog: improved layout for keybinding. Patch 1457 Tal Einat.
|
| 89 |
+
|
| 90 |
+
- tabpage.py updated: tabbedPages.py now supports multiple dynamic rows
|
| 91 |
+
of tabs. Patch 1612746 Tal Einat.
|
| 92 |
+
|
| 93 |
+
- Add confirmation dialog before printing. Patch 1717170 Tal Einat.
|
| 94 |
+
|
| 95 |
+
- Show paste position if > 80 col. Patch 1659326 Tal Einat.
|
| 96 |
+
|
| 97 |
+
- Update cursor color without restarting. Patch 1725576 Tal Einat.
|
| 98 |
+
|
| 99 |
+
- Allow keyboard interrupt only when user code is executing in subprocess.
|
| 100 |
+
Patch 1225 Tal Einat (reworked from IDLE-Spoon).
|
| 101 |
+
|
| 102 |
+
- configDialog cleanup. Patch 1730217 Tal Einat.
|
| 103 |
+
|
| 104 |
+
- textView cleanup. Patch 1718043 Tal Einat.
|
| 105 |
+
|
| 106 |
+
- Clean up EditorWindow close.
|
| 107 |
+
|
| 108 |
+
- Patch 1693258: Fix for duplicate "preferences" menu-OS X. Backport of r56204.
|
| 109 |
+
|
| 110 |
+
- OSX: Avoid crash for those versions of Tcl/Tk which don't have a console
|
| 111 |
+
|
| 112 |
+
- Bug in idlelib.MultiCall: Options dialog was crashing IDLE if there was an
|
| 113 |
+
option in config-extensions w/o a value. Patch #1672481, Tal Einat
|
| 114 |
+
|
| 115 |
+
- Corrected some bugs in AutoComplete. Also, Page Up/Down in ACW implemented;
|
| 116 |
+
mouse and cursor selection in ACWindow implemented; double Tab inserts
|
| 117 |
+
current selection and closes ACW (similar to double-click and Return); scroll
|
| 118 |
+
wheel now works in ACW. Added AutoComplete instructions to IDLE Help.
|
| 119 |
+
|
| 120 |
+
- AutoCompleteWindow moved below input line, will move above if there
|
| 121 |
+
isn't enough space. Patch 1621265 Tal Einat
|
| 122 |
+
|
| 123 |
+
- Calltips now 'handle' tuples in the argument list (display '<tuple>' :)
|
| 124 |
+
Suggested solution by Christos Georgiou, Bug 791968.
|
| 125 |
+
|
| 126 |
+
- Add 'raw' support to configHandler. Patch 1650174 Tal Einat.
|
| 127 |
+
|
| 128 |
+
- Avoid hang when encountering a duplicate in a completion list. Bug 1571112.
|
| 129 |
+
|
| 130 |
+
- Patch #1362975: Rework CodeContext indentation algorithm to
|
| 131 |
+
avoid hard-coding pixel widths.
|
| 132 |
+
|
| 133 |
+
- Bug #813342: Start the IDLE subprocess with -Qnew if the parent
|
| 134 |
+
is started with that option.
|
| 135 |
+
|
| 136 |
+
- Honor the "Cancel" action in the save dialog (Debian bug #299092)
|
| 137 |
+
|
| 138 |
+
- Some syntax errors were being caught by tokenize during the tabnanny
|
| 139 |
+
check, resulting in obscure error messages. Do the syntax check
|
| 140 |
+
first. Bug 1562716, 1562719
|
| 141 |
+
|
| 142 |
+
- IDLE's version number takes a big jump to match the version number of
|
| 143 |
+
the Python release of which it's a part.
|
| 144 |
+
|
| 145 |
+
|
| 146 |
+
What's New in IDLE 1.2?
|
| 147 |
+
=======================
|
| 148 |
+
*Release date: 19-SEP-2006*
|
| 149 |
+
|
| 150 |
+
- File menu hotkeys: there were three 'p' assignments. Reassign the
|
| 151 |
+
'Save Copy As' and 'Print' hotkeys to 'y' and 't'. Change the
|
| 152 |
+
Shell hotkey from 's' to 'l'.
|
| 153 |
+
|
| 154 |
+
- IDLE honors new quit() and exit() commands from site.py Quitter() object.
|
| 155 |
+
Patch 1540892, Jim Jewett
|
| 156 |
+
|
| 157 |
+
- The 'with' statement is now a Code Context block opener.
|
| 158 |
+
Patch 1540851, Jim Jewett
|
| 159 |
+
|
| 160 |
+
- Retrieval of previous shell command was not always preserving indentation
|
| 161 |
+
(since 1.2a1) Patch 1528468 Tal Einat.
|
| 162 |
+
|
| 163 |
+
- Changing tokenize (39046) to detect dedent broke tabnanny check (since 1.2a1)
|
| 164 |
+
|
| 165 |
+
- ToggleTab dialog was setting indent to 8 even if cancelled (since 1.2a1).
|
| 166 |
+
|
| 167 |
+
- When used w/o subprocess, all exceptions were preceded by an error
|
| 168 |
+
message claiming they were IDLE internal errors (since 1.2a1).
|
| 169 |
+
|
| 170 |
+
- Bug #1525817: Don't truncate short lines in IDLE's tool tips.
|
| 171 |
+
|
| 172 |
+
- Bug #1517990: IDLE keybindings on MacOS X now work correctly
|
| 173 |
+
|
| 174 |
+
- Bug #1517996: IDLE now longer shows the default Tk menu when a
|
| 175 |
+
path browser, class browser or debugger is the frontmost window on MacOS X
|
| 176 |
+
|
| 177 |
+
- EditorWindow.test() was failing. Bug 1417598
|
| 178 |
+
|
| 179 |
+
- EditorWindow failed when used stand-alone if sys.ps1 not set.
|
| 180 |
+
Bug 1010370 Dave Florek
|
| 181 |
+
|
| 182 |
+
- Tooltips failed on new-syle class __init__ args. Bug 1027566 Loren Guthrie
|
| 183 |
+
|
| 184 |
+
- Avoid occasional failure to detect closing paren properly.
|
| 185 |
+
Patch 1407280 Tal Einat
|
| 186 |
+
|
| 187 |
+
- Rebinding Tab key was inserting 'tab' instead of 'Tab'. Bug 1179168.
|
| 188 |
+
|
| 189 |
+
- Colorizer now handles #<builtin> correctly, also unicode strings and
|
| 190 |
+
'as' keyword in comment directly following import command. Closes 1325071.
|
| 191 |
+
Patch 1479219 Tal Einat
|
| 192 |
+
|
| 193 |
+
- Patch #1162825: Support non-ASCII characters in IDLE window titles.
|
| 194 |
+
|
| 195 |
+
- Source file f.flush() after writing; trying to avoid lossage if user
|
| 196 |
+
kills GUI.
|
| 197 |
+
|
| 198 |
+
- Options / Keys / Advanced dialog made functional. Also, allow binding
|
| 199 |
+
of 'movement' keys.
|
| 200 |
+
|
| 201 |
+
- 'syntax' patch adds improved calltips and a new class attribute listbox.
|
| 202 |
+
MultiCall module allows binding multiple actions to an event.
|
| 203 |
+
Patch 906702 Noam Raphael
|
| 204 |
+
|
| 205 |
+
- Better indentation after first line of string continuation.
|
| 206 |
+
IDLEfork Patch 681992, Noam Raphael
|
| 207 |
+
|
| 208 |
+
- Fixed CodeContext alignment problem, following suggestion from Tal Einat.
|
| 209 |
+
|
| 210 |
+
- Increased performance in CodeContext extension Patch 936169 Noam Raphael
|
| 211 |
+
|
| 212 |
+
- Mac line endings were incorrect when pasting code from some browsers
|
| 213 |
+
when using X11 and the Fink distribution. Python Bug 1263656.
|
| 214 |
+
|
| 215 |
+
- <Enter> when cursor is on a previous command retrieves that command. Instead
|
| 216 |
+
of replacing the input line, the previous command is now appended to the
|
| 217 |
+
input line. Indentation is preserved, and undo is enabled.
|
| 218 |
+
Patch 1196917 Jeff Shute
|
| 219 |
+
|
| 220 |
+
- Clarify "tab/space" Error Dialog and "Tab Width" Dialog associated with
|
| 221 |
+
the Untabify command.
|
| 222 |
+
|
| 223 |
+
- Corrected "tab/space" Error Dialog to show correct menu for Untabify.
|
| 224 |
+
Patch 1196980 Jeff Shute
|
| 225 |
+
|
| 226 |
+
- New files are colorized by default, and colorizing is removed when
|
| 227 |
+
saving as non-Python files. Patch 1196895 Jeff Shute
|
| 228 |
+
Closes Python Bugs 775012 and 800432, partial fix IDLEfork 763524
|
| 229 |
+
|
| 230 |
+
- Improve subprocess link error notification.
|
| 231 |
+
|
| 232 |
+
- run.py: use Queue's blocking feature instead of sleeping in the main
|
| 233 |
+
loop. Patch # 1190163 Michiel de Hoon
|
| 234 |
+
|
| 235 |
+
- Add config-main option to make the 'history' feature non-cyclic.
|
| 236 |
+
Default remains cyclic. Python Patch 914546 Noam Raphael.
|
| 237 |
+
|
| 238 |
+
- Removed ability to configure tabs indent from Options dialog. This 'feature'
|
| 239 |
+
has never worked and no one has complained. It is still possible to set a
|
| 240 |
+
default tabs (v. spaces) indent 'manually' via config-main.def (or to turn on
|
| 241 |
+
tabs for the current EditorWindow via the Format menu) but IDLE will
|
| 242 |
+
encourage indentation via spaces.
|
| 243 |
+
|
| 244 |
+
- Enable setting the indentation width using the Options dialog.
|
| 245 |
+
Bug # 783877
|
| 246 |
+
|
| 247 |
+
- Add keybindings for del-word-left and del-word-right.
|
| 248 |
+
|
| 249 |
+
- Discourage using an indent width other than 8 when using tabs to indent
|
| 250 |
+
Python code.
|
| 251 |
+
|
| 252 |
+
- Restore use of EditorWindow.set_indentation_params(), was dead code since
|
| 253 |
+
Autoindent was merged into EditorWindow. This allows IDLE to conform to the
|
| 254 |
+
indentation width of a loaded file. (But it still will not switch to tabs
|
| 255 |
+
even if the file uses tabs.) Any change in indent width is local to that
|
| 256 |
+
window.
|
| 257 |
+
|
| 258 |
+
- Add Tabnanny check before Run/F5, not just when Checking module.
|
| 259 |
+
|
| 260 |
+
- If an extension can't be loaded, print warning and skip it instead of
|
| 261 |
+
erroring out.
|
| 262 |
+
|
| 263 |
+
- Improve error handling when .idlerc can't be created (warn and exit).
|
| 264 |
+
|
| 265 |
+
- The GUI was hanging if the shell window was closed while a raw_input()
|
| 266 |
+
was pending. Restored the quit() of the readline() mainloop().
|
| 267 |
+
http://mail.python.org/pipermail/idle-dev/2004-December/002307.html
|
| 268 |
+
|
| 269 |
+
- The remote procedure call module rpc.py can now access data attributes of
|
| 270 |
+
remote registered objects. Changes to these attributes are local, however.
|
| 271 |
+
|
| 272 |
+
|
| 273 |
+
What's New in IDLE 1.1?
|
| 274 |
+
=======================
|
| 275 |
+
*Release date: 30-NOV-2004*
|
| 276 |
+
|
| 277 |
+
- On OpenBSD, terminating IDLE with ctrl-c from the command line caused a
|
| 278 |
+
stuck subprocess MainThread because only the SocketThread was exiting.
|
| 279 |
+
|
| 280 |
+
- Saving a Keyset w/o making changes (by using the "Save as New Custom Key Set"
|
| 281 |
+
button) caused IDLE to fail on restart (no new keyset was created in
|
| 282 |
+
config-keys.cfg). Also true for Theme/highlights. Python Bug 1064535.
|
| 283 |
+
|
| 284 |
+
- A change to the linecache.py API caused IDLE to exit when an exception was
|
| 285 |
+
raised while running without the subprocess (-n switch). Python Bug 1063840.
|
| 286 |
+
|
| 287 |
+
- When paragraph reformat width was made configurable, a bug was
|
| 288 |
+
introduced that caused reformatting of comment blocks to ignore how
|
| 289 |
+
far the block was indented, effectively adding the indentation width
|
| 290 |
+
to the reformat width. This has been repaired, and the reformat
|
| 291 |
+
width is again a bound on the total width of reformatted lines.
|
| 292 |
+
|
| 293 |
+
- Improve keyboard focus binding, especially in Windows menu. Improve
|
| 294 |
+
window raising, especially in the Windows menu and in the debugger.
|
| 295 |
+
IDLEfork 763524.
|
| 296 |
+
|
| 297 |
+
- If user passes a non-existent filename on the commandline, just
|
| 298 |
+
open a new file, don't raise a dialog. IDLEfork 854928.
|
| 299 |
+
|
| 300 |
+
- EditorWindow.py was not finding the .chm help file on Windows. Typo
|
| 301 |
+
at Rev 1.54. Python Bug 990954
|
| 302 |
+
|
| 303 |
+
- checking sys.platform for substring 'win' was breaking IDLE docs on Mac
|
| 304 |
+
(darwin). Also, Mac Safari browser requires full file:// URIs. SF 900580.
|
| 305 |
+
|
| 306 |
+
- Redirect the warning stream to the shell during the ScriptBinding check of
|
| 307 |
+
user code and format the warning similarly to an exception for both that
|
| 308 |
+
check and for runtime warnings raised in the subprocess.
|
| 309 |
+
|
| 310 |
+
- CodeContext hint pane visibility state is now persistent across sessions.
|
| 311 |
+
The pane no longer appears in the shell window. Added capability to limit
|
| 312 |
+
extensions to shell window or editor windows. Noam Raphael addition
|
| 313 |
+
to Patch 936169.
|
| 314 |
+
|
| 315 |
+
- Paragraph reformat width is now a configurable parameter in the
|
| 316 |
+
Options GUI.
|
| 317 |
+
|
| 318 |
+
- New Extension: CodeContext. Provides block structuring hints for code
|
| 319 |
+
which has scrolled above an edit window. Patch 936169 Noam Raphael.
|
| 320 |
+
|
| 321 |
+
- If nulls somehow got into the strings in recent-files.lst
|
| 322 |
+
EditorWindow.update_recent_files_list() was failing. Python Bug 931336.
|
| 323 |
+
|
| 324 |
+
- If the normal background is changed via Configure/Highlighting, it will
|
| 325 |
+
update immediately, thanks to the previously mentioned patch by Nigel Rowe.
|
| 326 |
+
|
| 327 |
+
- Add a highlight theme for builtin keywords. Python Patch 805830 Nigel Rowe
|
| 328 |
+
This also fixed IDLEfork bug [ 693418 ] Normal text background color not
|
| 329 |
+
refreshed and Python bug [897872 ] Unknown color name on HP-UX
|
| 330 |
+
|
| 331 |
+
- rpc.py:SocketIO - Large modules were generating large pickles when downloaded
|
| 332 |
+
to the execution server. The return of the OK response from the subprocess
|
| 333 |
+
initialization was interfering and causing the sending socket to be not
|
| 334 |
+
ready. Add an IO ready test to fix this. Moved the polling IO ready test
|
| 335 |
+
into pollpacket().
|
| 336 |
+
|
| 337 |
+
- Fix typo in rpc.py, s/b "pickle.PicklingError" not "pickle.UnpicklingError".
|
| 338 |
+
|
| 339 |
+
- Added a Tk error dialog to run.py inform the user if the subprocess can't
|
| 340 |
+
connect to the user GUI process. Added a timeout to the GUI's listening
|
| 341 |
+
socket. Added Tk error dialogs to PyShell.py to announce a failure to bind
|
| 342 |
+
the port or connect to the subprocess. Clean up error handling during
|
| 343 |
+
connection initiation phase. This is an update of Python Patch 778323.
|
| 344 |
+
|
| 345 |
+
- Print correct exception even if source file changed since shell was
|
| 346 |
+
restarted. IDLEfork Patch 869012 Noam Raphael
|
| 347 |
+
|
| 348 |
+
- Keybindings with the Shift modifier now work correctly. So do bindings which
|
| 349 |
+
use the Space key. Limit unmodified user keybindings to the function keys.
|
| 350 |
+
Python Bug 775353, IDLEfork Bugs 755647, 761557
|
| 351 |
+
|
| 352 |
+
- After an exception, run.py was not setting the exception vector. Noam
|
| 353 |
+
Raphael suggested correcting this so pdb's postmortem pm() would work.
|
| 354 |
+
IDLEfork Patch 844675
|
| 355 |
+
|
| 356 |
+
- IDLE now does not fail to save the file anymore if the Tk buffer is not a
|
| 357 |
+
Unicode string, yet eol_convention is. Python Bugs 774680, 788378
|
| 358 |
+
|
| 359 |
+
- IDLE didn't start correctly when Python was installed in "Program Files" on
|
| 360 |
+
W2K and XP. Python Bugs 780451, 784183
|
| 361 |
+
|
| 362 |
+
- config-main.def documentation incorrectly referred to idle- instead of
|
| 363 |
+
config- filenames. SF 782759 Also added note about .idlerc location.
|
| 364 |
+
|
| 365 |
+
|
| 366 |
+
What's New in IDLE 1.0?
|
| 367 |
+
=======================
|
| 368 |
+
*Release date: 29-Jul-2003*
|
| 369 |
+
|
| 370 |
+
- Added a banner to the shell discussing warnings possibly raised by personal
|
| 371 |
+
firewall software. Added same comment to README.txt.
|
| 372 |
+
|
| 373 |
+
- Calltip error when docstring was None Python Bug 775541
|
| 374 |
+
|
| 375 |
+
- Updated extend.txt, help.txt, and config-extensions.def to correctly
|
| 376 |
+
reflect the current status of the configuration system. Python Bug 768469
|
| 377 |
+
|
| 378 |
+
- Fixed: Call Tip Trimming May Loop Forever. Python Patch 769142 (Daniels)
|
| 379 |
+
|
| 380 |
+
- Replaced apply(f, args, kwds) with f(*args, **kwargs) to improve performance
|
| 381 |
+
Python Patch 768187
|
| 382 |
+
|
| 383 |
+
- Break or continue statements outside a loop were causing IDLE crash
|
| 384 |
+
Python Bug 767794
|
| 385 |
+
|
| 386 |
+
- Convert Unicode strings from readline to IOBinding.encoding. Also set
|
| 387 |
+
sys.std{in|out|err}.encoding, for both the local and the subprocess case.
|
| 388 |
+
SF IDLEfork patch 682347.
|
| 389 |
+
|
| 390 |
+
- Extend AboutDialog.ViewFile() to support file encodings. Make the CREDITS
|
| 391 |
+
file Latin-1.
|
| 392 |
+
|
| 393 |
+
- Updated the About dialog to reflect re-integration into Python. Provide
|
| 394 |
+
buttons to display Python's NEWS, License, and Credits, plus additional
|
| 395 |
+
buttons for IDLE's README and NEWS.
|
| 396 |
+
|
| 397 |
+
- TextViewer() now has a third parameter which allows inserting text into the
|
| 398 |
+
viewer instead of reading from a file.
|
| 399 |
+
|
| 400 |
+
- (Created the .../Lib/idlelib directory in the Python CVS, which is a clone of
|
| 401 |
+
IDLEfork modified to install in the Python environment. The code in the
|
| 402 |
+
interrupt module has been moved to thread.interrupt_main(). )
|
| 403 |
+
|
| 404 |
+
- Printing the Shell window was failing if it was not saved first SF 748975
|
| 405 |
+
|
| 406 |
+
- When using the Search in Files dialog, if the user had a selection
|
| 407 |
+
highlighted in his Editor window, insert it into the dialog search field.
|
| 408 |
+
|
| 409 |
+
- The Python Shell entry was disappearing from the Windows menu.
|
| 410 |
+
|
| 411 |
+
- Update the Windows file list when a file name change occurs
|
| 412 |
+
|
| 413 |
+
- Change to File / Open Module: always pop up the dialog, using the current
|
| 414 |
+
selection as the default value. This is easier to use habitually.
|
| 415 |
+
|
| 416 |
+
- Avoided a problem with starting the subprocess when 'localhost' doesn't
|
| 417 |
+
resolve to the user's loopback interface. SF 747772
|
| 418 |
+
|
| 419 |
+
- Fixed an issue with highlighted errors never de-colorizing. SF 747677. Also
|
| 420 |
+
improved notification of Tabnanny Token Error.
|
| 421 |
+
|
| 422 |
+
- File / New will by default save in the directory of the Edit window from
|
| 423 |
+
which it was initiated. SF 748973 Guido van Rossum patch.
|
| 424 |
+
|
| 425 |
+
|
| 426 |
+
What's New in IDLEfork 0.9b1?
|
| 427 |
+
=============================
|
| 428 |
+
*Release date: 02-Jun-2003*
|
| 429 |
+
|
| 430 |
+
- The current working directory of the execution environment (and shell
|
| 431 |
+
following completion of execution) is now that of the module being run.
|
| 432 |
+
|
| 433 |
+
- Added the delete-exitfunc option to config-main.def. (This option is not
|
| 434 |
+
included in the Options dialog.) Setting this to True (the default) will
|
| 435 |
+
cause IDLE to not run sys.exitfunc/atexit when the subprocess exits.
|
| 436 |
+
|
| 437 |
+
- IDLE now preserves the line ending codes when editing a file produced on
|
| 438 |
+
a different platform. SF 661759, SF 538584
|
| 439 |
+
|
| 440 |
+
- Reduced default editor font size to 10 point and increased window height
|
| 441 |
+
to provide a better initial impression on Windows.
|
| 442 |
+
|
| 443 |
+
- Options / Fonts/Tabs / Set Base Editor Font: List box was not highlighting
|
| 444 |
+
the default font when first installed on Windows. SF 661676
|
| 445 |
+
|
| 446 |
+
- Added Autosave feature: when user runs code from edit window, if the file
|
| 447 |
+
has been modified IDLE will silently save it if Autosave is enabled. The
|
| 448 |
+
option is set in the Options dialog, and the default is to prompt the
|
| 449 |
+
user to save the file. SF 661318 Bruce Sherwood patch.
|
| 450 |
+
|
| 451 |
+
- Improved the RESTART annotation in the shell window when the user restarts
|
| 452 |
+
the shell while it is generating output. Also improved annotation when user
|
| 453 |
+
repeatedly hammers the Ctrl-F6 restart.
|
| 454 |
+
|
| 455 |
+
- Allow IDLE to run when not installed and cwd is not the IDLE directory
|
| 456 |
+
SF Patch 686254 "Run IDLEfork from any directory without set-up" - Raphael
|
| 457 |
+
|
| 458 |
+
- When a module is run from an EditorWindow: if its directory is not in
|
| 459 |
+
sys.path, prepend it. This allows the module to import other modules in
|
| 460 |
+
the same directory. Do the same for a script run from the command line.
|
| 461 |
+
|
| 462 |
+
- Correctly restart the subprocess if it is running user code and the user
|
| 463 |
+
attempts to run some other module or restarts the shell. Do the same if
|
| 464 |
+
the link is broken and it is possible to restart the subprocess and re-
|
| 465 |
+
connect to the GUI. SF RFE 661321.
|
| 466 |
+
|
| 467 |
+
- Improved exception reporting when running commands or scripts from the
|
| 468 |
+
command line.
|
| 469 |
+
|
| 470 |
+
- Added a -n command line switch to start IDLE without the subprocess.
|
| 471 |
+
Removed the Shell menu when running in that mode. Updated help messages.
|
| 472 |
+
|
| 473 |
+
- Added a comment to the shell startup header to indicate when IDLE is not
|
| 474 |
+
using the subprocess.
|
| 475 |
+
|
| 476 |
+
- Restore the ability to run without the subprocess. This can be important for
|
| 477 |
+
some platforms or configurations. (Running without the subprocess allows the
|
| 478 |
+
debugger to trace through parts of IDLE itself, which may or may not be
|
| 479 |
+
desirable, depending on your point of view. In addition, the traditional
|
| 480 |
+
reload/import tricks must be use if user source code is changed.) This is
|
| 481 |
+
helpful for developing IDLE using IDLE, because one instance can be used to
|
| 482 |
+
edit the code and a separate instance run to test changes. (Multiple
|
| 483 |
+
concurrent IDLE instances with subprocesses is a future feature)
|
| 484 |
+
|
| 485 |
+
- Improve the error message a user gets when saving a file with non-ASCII
|
| 486 |
+
characters and no source encoding is specified. Done by adding a dialog
|
| 487 |
+
'EncodingMessage', which contains the line to add in a fixed-font entry
|
| 488 |
+
widget, and which has a button to add that line to the file automatically.
|
| 489 |
+
Also, add a configuration option 'EditorWindow/encoding', which has three
|
| 490 |
+
possible values: none, utf-8, and locale. None is the default: IDLE will show
|
| 491 |
+
this dialog when non-ASCII characters are encountered. utf-8 means that files
|
| 492 |
+
with non-ASCII characters are saved as utf-8-with-bom. locale means that
|
| 493 |
+
files are saved in the locale's encoding; the dialog is only displayed if the
|
| 494 |
+
source contains characters outside the locale's charset. SF 710733 - Loewis
|
| 495 |
+
|
| 496 |
+
- Improved I/O response by tweaking the wait parameter in various
|
| 497 |
+
calls to signal.signal().
|
| 498 |
+
|
| 499 |
+
- Implemented a threaded subprocess which allows interrupting a pass
|
| 500 |
+
loop in user code using the 'interrupt' extension. User code runs
|
| 501 |
+
in MainThread, while the RPCServer is handled by SockThread. This is
|
| 502 |
+
necessary because Windows doesn't support signals.
|
| 503 |
+
|
| 504 |
+
- Implemented the 'interrupt' extension module, which allows a subthread
|
| 505 |
+
to raise a KeyboardInterrupt in the main thread.
|
| 506 |
+
|
| 507 |
+
- Attempting to save the shell raised an error related to saving
|
| 508 |
+
breakpoints, which are not implemented in the shell
|
| 509 |
+
|
| 510 |
+
- Provide a correct message when 'exit' or 'quit' are entered at the
|
| 511 |
+
IDLE command prompt SF 695861
|
| 512 |
+
|
| 513 |
+
- Eliminate extra blank line in shell output caused by not flushing
|
| 514 |
+
stdout when user code ends with an unterminated print. SF 695861
|
| 515 |
+
|
| 516 |
+
- Moved responsibility for exception formatting (i.e. pruning IDLE internal
|
| 517 |
+
calls) out of rpc.py into the client and server.
|
| 518 |
+
|
| 519 |
+
- Exit IDLE cleanly even when doing subprocess I/O
|
| 520 |
+
|
| 521 |
+
- Handle subprocess interrupt with an RPC message.
|
| 522 |
+
|
| 523 |
+
- Restart the subprocess if it terminates itself. (VPython programs do that)
|
| 524 |
+
|
| 525 |
+
- Support subclassing of exceptions, including in the shell, by moving the
|
| 526 |
+
exception formatting to the subprocess.
|
| 527 |
+
|
| 528 |
+
|
| 529 |
+
What's New in IDLEfork 0.9 Alpha 2?
|
| 530 |
+
===================================
|
| 531 |
+
*Release date: 27-Jan-2003*
|
| 532 |
+
|
| 533 |
+
- Updated INSTALL.txt to claify use of the python2 rpm.
|
| 534 |
+
|
| 535 |
+
- Improved formatting in IDLE Help.
|
| 536 |
+
|
| 537 |
+
- Run menu: Replace "Run Script" with "Run Module".
|
| 538 |
+
|
| 539 |
+
- Code encountering an unhandled exception under the debugger now shows
|
| 540 |
+
the correct traceback, with IDLE internal levels pruned out.
|
| 541 |
+
|
| 542 |
+
- If an exception occurs entirely in IDLE, don't prune the IDLE internal
|
| 543 |
+
modules from the traceback displayed.
|
| 544 |
+
|
| 545 |
+
- Class Browser and Path Browser now use Alt-Key-2 for vertical zoom.
|
| 546 |
+
|
| 547 |
+
- IDLE icons will now install correctly even when setup.py is run from the
|
| 548 |
+
build directory
|
| 549 |
+
|
| 550 |
+
- Class Browser now compatible with Python2.3 version of pyclbr.py
|
| 551 |
+
|
| 552 |
+
- Left cursor move in presence of selected text now moves from left end
|
| 553 |
+
of the selection.
|
| 554 |
+
|
| 555 |
+
- Add Meta keybindings to "IDLE Classic Windows" to handle reversed
|
| 556 |
+
Alt/Meta on some Linux distros.
|
| 557 |
+
|
| 558 |
+
- Change default: IDLE now starts with Python Shell.
|
| 559 |
+
|
| 560 |
+
- Removed the File Path from the Additional Help Sources scrolled list.
|
| 561 |
+
|
| 562 |
+
- Add capability to access Additional Help Sources on the web if the
|
| 563 |
+
Help File Path begins with //http or www. (Otherwise local path is
|
| 564 |
+
validated, as before.)
|
| 565 |
+
|
| 566 |
+
- Additional Help Sources were not being posted on the Help menu in the
|
| 567 |
+
order entered. Implement sorting the list by [HelpFiles] 'option'
|
| 568 |
+
number.
|
| 569 |
+
|
| 570 |
+
- Add Browse button to New Help Source dialog. Arrange to start in
|
| 571 |
+
Python/Doc if platform is Windows, otherwise start in current directory.
|
| 572 |
+
|
| 573 |
+
- Put the Additional Help Sources directly on the Help menu instead of in
|
| 574 |
+
an Extra Help cascade menu. Rearrange the Help menu so the Additional
|
| 575 |
+
Help Sources come last. Update help.txt appropriately.
|
| 576 |
+
|
| 577 |
+
- Fix Tk root pop-ups in configSectionNameDialog.py and configDialog.py
|
| 578 |
+
|
| 579 |
+
- Uniform capitalization in General tab of ConfigDialog, update the doc string.
|
| 580 |
+
|
| 581 |
+
- Fix bug in ConfigDialog where SaveAllChangedConfig() was unexpectedly
|
| 582 |
+
deleting Additional Help Sources from the user's config file.
|
| 583 |
+
|
| 584 |
+
- Make configHelpSourceEdit OK button the default and bind <Return>
|
| 585 |
+
|
| 586 |
+
- Fix Tk root pop-ups in configHelpSourceEdit: error dialogs not attached
|
| 587 |
+
to parents.
|
| 588 |
+
|
| 589 |
+
- Use os.startfile() to open both Additional Help and Python Help on the
|
| 590 |
+
Windows platform. The application associated with the file type will act as
|
| 591 |
+
the viewer. Windows help files (.chm) are now supported via the
|
| 592 |
+
Settings/General/Additional Help facility.
|
| 593 |
+
|
| 594 |
+
- If Python Help files are installed locally on Linux, use them instead of
|
| 595 |
+
accessing python.org.
|
| 596 |
+
|
| 597 |
+
- Make the methods for finding the Python help docs more robust, and make
|
| 598 |
+
them work in the installed configuration, also.
|
| 599 |
+
|
| 600 |
+
- On the Save Before Run dialog, make the OK button the default. One
|
| 601 |
+
less mouse action!
|
| 602 |
+
|
| 603 |
+
- Add a method: EditorWindow.get_geometry() for future use in implementing
|
| 604 |
+
window location persistence.
|
| 605 |
+
|
| 606 |
+
- Removed the "Help/Advice" menu entry. Thanks, David! We'll remember!
|
| 607 |
+
|
| 608 |
+
- Change the "Classic Windows" theme's paste key to be <ctrl-v>.
|
| 609 |
+
|
| 610 |
+
- Rearrange the Shell menu to put Stack Viewer entries adjacent.
|
| 611 |
+
|
| 612 |
+
- Add the ability to restart the subprocess interpreter from the shell window;
|
| 613 |
+
add an associated menu entry "Shell/Restart" with binding Control-F6. Update
|
| 614 |
+
IDLE help.
|
| 615 |
+
|
| 616 |
+
- Upon a restart, annotate the shell window with a "restart boundary". Add a
|
| 617 |
+
shell window menu "Shell/View Restart" with binding F6 to jump to the most
|
| 618 |
+
recent restart boundary.
|
| 619 |
+
|
| 620 |
+
- Add Shell menu to Python Shell; change "Settings" to "Options".
|
| 621 |
+
|
| 622 |
+
- Remove incorrect comment in setup.py: IDLEfork is now installed as a package.
|
| 623 |
+
|
| 624 |
+
- Add INSTALL.txt, HISTORY.txt, NEWS.txt to installed configuration.
|
| 625 |
+
|
| 626 |
+
- In installer text, fix reference to Visual Python, should be VPython.
|
| 627 |
+
Properly credit David Scherer.
|
| 628 |
+
|
| 629 |
+
- Modified idle, idle.py, idle.pyw to improve exception handling.
|
| 630 |
+
|
| 631 |
+
|
| 632 |
+
What's New in IDLEfork 0.9 Alpha 1?
|
| 633 |
+
===================================
|
| 634 |
+
*Release date: 31-Dec-2002*
|
| 635 |
+
|
| 636 |
+
- First release of major new functionality. For further details refer to
|
| 637 |
+
Idle-dev and/or the Sourceforge CVS.
|
| 638 |
+
|
| 639 |
+
- Adapted to the Mac platform.
|
| 640 |
+
|
| 641 |
+
- Overhauled the IDLE startup options and revised the idle -h help message,
|
| 642 |
+
which provides details of command line usage.
|
| 643 |
+
|
| 644 |
+
- Multiple bug fixes and usability enhancements.
|
| 645 |
+
|
| 646 |
+
- Introduced the new RPC implementation, which includes a debugger. The output
|
| 647 |
+
of user code is to the shell, and the shell may be used to inspect the
|
| 648 |
+
environment after the run has finished. (In version 0.8.1 the shell
|
| 649 |
+
environment was separate from the environment of the user code.)
|
| 650 |
+
|
| 651 |
+
- Introduced the configuration GUI and a new About dialog.
|
| 652 |
+
|
| 653 |
+
- Removed David Scherer's Remote Procedure Call code and replaced with Guido
|
| 654 |
+
van Rossum's. GvR code has support for the IDLE debugger and uses the shell
|
| 655 |
+
to inspect the environment of code Run from an Edit window. Files removed:
|
| 656 |
+
ExecBinding.py, loader.py, protocol.py, Remote.py, spawn.py
|
| 657 |
+
|
| 658 |
+
--------------------------------------------------------------------
|
| 659 |
+
Refer to HISTORY.txt for additional information on earlier releases.
|
| 660 |
+
--------------------------------------------------------------------
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/TODO.txt
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Original IDLE todo, much of it now outdated:
|
| 2 |
+
============================================
|
| 3 |
+
TO DO:
|
| 4 |
+
|
| 5 |
+
- improve debugger:
|
| 6 |
+
- manage breakpoints globally, allow bp deletion, tbreak, cbreak etc.
|
| 7 |
+
- real object browser
|
| 8 |
+
- help on how to use it (a simple help button will do wonders)
|
| 9 |
+
- performance? (updates of large sets of locals are slow)
|
| 10 |
+
- better integration of "debug module"
|
| 11 |
+
- debugger should be global resource (attached to flist, not to shell)
|
| 12 |
+
- fix the stupid bug where you need to step twice
|
| 13 |
+
- display class name in stack viewer entries for methods
|
| 14 |
+
- suppress tracing through IDLE internals (e.g. print) DONE
|
| 15 |
+
- add a button to suppress through a specific module or class or method
|
| 16 |
+
- more object inspection to stack viewer, e.g. to view all array items
|
| 17 |
+
- insert the initial current directory into sys.path DONE
|
| 18 |
+
- default directory attribute for each window instead of only for windows
|
| 19 |
+
that have an associated filename
|
| 20 |
+
- command expansion from keywords, module contents, other buffers, etc.
|
| 21 |
+
- "Recent documents" menu item DONE
|
| 22 |
+
- Filter region command
|
| 23 |
+
- Optional horizontal scroll bar
|
| 24 |
+
- more Emacsisms:
|
| 25 |
+
- ^K should cut to buffer
|
| 26 |
+
- M-[, M-] to move by paragraphs
|
| 27 |
+
- incremental search?
|
| 28 |
+
- search should indicate wrap-around in some way
|
| 29 |
+
- restructure state sensitive code to avoid testing flags all the time
|
| 30 |
+
- persistent user state (e.g. window and cursor positions, bindings)
|
| 31 |
+
- make backups when saving
|
| 32 |
+
- check file mtimes at various points
|
| 33 |
+
- Pluggable interface with RCS/CVS/Perforce/Clearcase
|
| 34 |
+
- better help?
|
| 35 |
+
- don't open second class browser on same module (nor second path browser)
|
| 36 |
+
- unify class and path browsers
|
| 37 |
+
- Need to define a standard way whereby one can determine one is running
|
| 38 |
+
inside IDLE (needed for Tk mainloop, also handy for $PYTHONSTARTUP)
|
| 39 |
+
- Add more utility methods for use by extensions (a la get_selection)
|
| 40 |
+
- Way to run command in totally separate interpreter (fork+os.system?) DONE
|
| 41 |
+
- Way to find definition of fully-qualified name:
|
| 42 |
+
In other words, select "UserDict.UserDict", hit some magic key and
|
| 43 |
+
it loads up UserDict.py and finds the first def or class for UserDict.
|
| 44 |
+
- need a way to force colorization on/off
|
| 45 |
+
- need a way to force auto-indent on/off
|
| 46 |
+
|
| 47 |
+
Details:
|
| 48 |
+
|
| 49 |
+
- ^O (on Unix -- open-line) should honor autoindent
|
| 50 |
+
- after paste, show end of pasted text
|
| 51 |
+
- on Windows, should turn short filename to long filename (not only in argv!)
|
| 52 |
+
(shouldn't this be done -- or undone -- by ntpath.normpath?)
|
| 53 |
+
- new autoindent after colon even indents when the colon is in a comment!
|
| 54 |
+
- sometimes forward slashes in pathname remain
|
| 55 |
+
- sometimes star in window name remains in Windows menu
|
| 56 |
+
- With unix bindings, ESC by itself is ignored
|
| 57 |
+
- Sometimes for no apparent reason a selection from the cursor to the
|
| 58 |
+
end of the command buffer appears, which is hard to get rid of
|
| 59 |
+
because it stays when you are typing!
|
| 60 |
+
- The Line/Col in the status bar can be wrong initially in PyShell DONE
|
| 61 |
+
|
| 62 |
+
Structural problems:
|
| 63 |
+
|
| 64 |
+
- too much knowledge in FileList about EditorWindow (for example)
|
| 65 |
+
- should add some primitives for accessing the selection etc.
|
| 66 |
+
to repeat cumbersome code over and over
|
| 67 |
+
|
| 68 |
+
======================================================================
|
| 69 |
+
|
| 70 |
+
Jeff Bauer suggests:
|
| 71 |
+
|
| 72 |
+
- Open Module doesn't appear to handle hierarchical packages.
|
| 73 |
+
- Class browser should also allow hierarchical packages.
|
| 74 |
+
- Open and Open Module could benefit from a history, DONE
|
| 75 |
+
either command line style, or Microsoft recent-file
|
| 76 |
+
style.
|
| 77 |
+
- Add a Smalltalk-style inspector (i.e. Tkinspect)
|
| 78 |
+
|
| 79 |
+
The last suggestion is already a reality, but not yet
|
| 80 |
+
integrated into IDLE. I use a module called inspector.py,
|
| 81 |
+
that used to be available from python.org(?) It no longer
|
| 82 |
+
appears to be in the contributed section, and the source
|
| 83 |
+
has no author attribution.
|
| 84 |
+
|
| 85 |
+
In any case, the code is useful for visually navigating
|
| 86 |
+
an object's attributes, including its container hierarchy.
|
| 87 |
+
|
| 88 |
+
>>> from inspector import Tkinspect
|
| 89 |
+
>>> Tkinspect(None, myObject)
|
| 90 |
+
|
| 91 |
+
Tkinspect could probably be extended and refined to
|
| 92 |
+
integrate better into IDLE.
|
| 93 |
+
|
| 94 |
+
======================================================================
|
| 95 |
+
|
| 96 |
+
Comparison to PTUI
|
| 97 |
+
------------------
|
| 98 |
+
|
| 99 |
+
+ PTUI's help is better (HTML!)
|
| 100 |
+
|
| 101 |
+
+ PTUI can attach a shell to any module
|
| 102 |
+
|
| 103 |
+
+ PTUI has some more I/O commands:
|
| 104 |
+
open multiple
|
| 105 |
+
append
|
| 106 |
+
examine (what's that?)
|
| 107 |
+
|
| 108 |
+
======================================================================
|
| 109 |
+
|
| 110 |
+
Notes after trying to run Grail
|
| 111 |
+
-------------------------------
|
| 112 |
+
|
| 113 |
+
- Grail does stuff to sys.path based on sys.argv[0]; you must set
|
| 114 |
+
sys.argv[0] to something decent first (it is normally set to the path of
|
| 115 |
+
the idle script).
|
| 116 |
+
|
| 117 |
+
- Grail must be exec'ed in __main__ because that's imported by some
|
| 118 |
+
other parts of Grail.
|
| 119 |
+
|
| 120 |
+
- Grail uses a module called History and so does idle :-(
|
| 121 |
+
|
| 122 |
+
======================================================================
|
| 123 |
+
|
| 124 |
+
Robin Friedrich's items:
|
| 125 |
+
|
| 126 |
+
Things I'd like to see:
|
| 127 |
+
- I'd like support for shift-click extending the selection. There's a
|
| 128 |
+
bug now that it doesn't work the first time you try it.
|
| 129 |
+
- Printing is needed. How hard can that be on Windows? FIRST CUT DONE
|
| 130 |
+
- The python-mode trick of autoindenting a line with <tab> is neat and
|
| 131 |
+
very handy.
|
| 132 |
+
- (someday) a spellchecker for docstrings and comments.
|
| 133 |
+
- a pagedown/up command key which moves to next class/def statement (top
|
| 134 |
+
level)
|
| 135 |
+
- split window capability
|
| 136 |
+
- DnD text relocation/copying
|
| 137 |
+
|
| 138 |
+
Things I don't want to see.
|
| 139 |
+
- line numbers... will probably slow things down way too much.
|
| 140 |
+
- Please use another icon for the tree browser leaf. The small snake
|
| 141 |
+
isn't cutting it.
|
| 142 |
+
|
| 143 |
+
----------------------------------------------------------------------
|
| 144 |
+
|
| 145 |
+
- Customizable views (multi-window or multi-pane). (Markus Gritsch)
|
| 146 |
+
|
| 147 |
+
- Being able to double click (maybe double right click) on a callable
|
| 148 |
+
object in the editor which shows the source of the object, if
|
| 149 |
+
possible. (Gerrit Holl)
|
| 150 |
+
|
| 151 |
+
- Hooks into the guts, like in Emacs. (Mike Romberg)
|
| 152 |
+
|
| 153 |
+
- Sharing the editor with a remote tutor. (Martijn Faassen)
|
| 154 |
+
|
| 155 |
+
- Multiple views on the same file. (Tony J Ibbs)
|
| 156 |
+
|
| 157 |
+
- Store breakpoints in a global (per-project) database (GvR); Dirk
|
| 158 |
+
Heise adds: save some space-trimmed context and search around when
|
| 159 |
+
reopening a file that might have been edited by someone else.
|
| 160 |
+
|
| 161 |
+
- Capture menu events in extensions without changing the IDLE source.
|
| 162 |
+
(Matthias Barmeier)
|
| 163 |
+
|
| 164 |
+
- Use overlapping panels (a "notebook" in MFC terms I think) for info
|
| 165 |
+
that doesn't need to be accessible simultaneously (e.g. HTML source
|
| 166 |
+
and output). Use multi-pane windows for info that does need to be
|
| 167 |
+
shown together (e.g. class browser and source). (Albert Brandl)
|
| 168 |
+
|
| 169 |
+
- A project should invisibly track all symbols, for instant search,
|
| 170 |
+
replace and cross-ref. Projects should be allowed to span multiple
|
| 171 |
+
directories, hosts, etc. Project management files are placed in a
|
| 172 |
+
directory you specify. A global mapping between project names and
|
| 173 |
+
project directories should exist [not so sure --GvR]. (Tim Peters)
|
| 174 |
+
|
| 175 |
+
- Merge attr-tips and auto-expand. (Mark Hammond, Tim Peters)
|
| 176 |
+
|
| 177 |
+
- Python Shell should behave more like a "shell window" as users know
|
| 178 |
+
it -- i.e. you can only edit the current command, and the cursor can't
|
| 179 |
+
escape from the command area. (Albert Brandl)
|
| 180 |
+
|
| 181 |
+
- Set X11 class to "idle/Idle", set icon and title to something
|
| 182 |
+
beginning with "idle" -- for window manangers. (Randall Hopper)
|
| 183 |
+
|
| 184 |
+
- Config files editable through a preferences dialog. (me) DONE
|
| 185 |
+
|
| 186 |
+
- Config files still editable outside the preferences dialog.
|
| 187 |
+
(Randall Hopper) DONE
|
| 188 |
+
|
| 189 |
+
- When you're editing a command in PyShell, and there are only blank
|
| 190 |
+
lines below the cursor, hitting Return should ignore or delete those
|
| 191 |
+
blank lines rather than deciding you're not on the last line. (me)
|
| 192 |
+
|
| 193 |
+
- Run command (F5 c.s.) should be more like Pythonwin's Run -- a
|
| 194 |
+
dialog with options to give command line arguments, run the debugger,
|
| 195 |
+
etc. (me)
|
| 196 |
+
|
| 197 |
+
- Shouldn't be able to delete part of the prompt (or any text before
|
| 198 |
+
it) in the PyShell. (Martijn Faassen) DONE
|
| 199 |
+
|
| 200 |
+
- Emacs style auto-fill (also smart about comments and strings).
|
| 201 |
+
(Jeremy Hylton)
|
| 202 |
+
|
| 203 |
+
- Output of Run Script should go to a separate output window, not to
|
| 204 |
+
the shell window. Output of separate runs should all go to the same
|
| 205 |
+
window but clearly delimited. (David Scherer) REJECT FIRST, LATTER DONE
|
| 206 |
+
|
| 207 |
+
- GUI form designer to kick VB's butt. (Robert Geiger) THAT'S NOT IDLE
|
| 208 |
+
|
| 209 |
+
- Printing! Possibly via generation of PDF files which the user must
|
| 210 |
+
then send to the printer separately. (Dinu Gherman) FIRST CUT
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/__init__.py
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""The idlelib package implements the Idle application.
|
| 2 |
+
|
| 3 |
+
Idle includes an interactive shell and editor.
|
| 4 |
+
Starting with Python 3.6, IDLE requires tcl/tk 8.5 or later.
|
| 5 |
+
Use the files named idle.* to start Idle.
|
| 6 |
+
|
| 7 |
+
The other files are private implementations. Their details are subject to
|
| 8 |
+
change. See PEP 434 for more. Import them at your own risk.
|
| 9 |
+
"""
|
| 10 |
+
testing = False # Set True by test.test_idle.
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/autocomplete.py
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Complete either attribute names or file names.
|
| 2 |
+
|
| 3 |
+
Either on demand or after a user-selected delay after a key character,
|
| 4 |
+
pop up a list of candidates.
|
| 5 |
+
"""
|
| 6 |
+
import __main__
|
| 7 |
+
import keyword
|
| 8 |
+
import os
|
| 9 |
+
import string
|
| 10 |
+
import sys
|
| 11 |
+
|
| 12 |
+
# Two types of completions; defined here for autocomplete_w import below.
|
| 13 |
+
ATTRS, FILES = 0, 1
|
| 14 |
+
from idlelib import autocomplete_w
|
| 15 |
+
from idlelib.config import idleConf
|
| 16 |
+
from idlelib.hyperparser import HyperParser
|
| 17 |
+
|
| 18 |
+
# Tuples passed to open_completions.
|
| 19 |
+
# EvalFunc, Complete, WantWin, Mode
|
| 20 |
+
FORCE = True, False, True, None # Control-Space.
|
| 21 |
+
TAB = False, True, True, None # Tab.
|
| 22 |
+
TRY_A = False, False, False, ATTRS # '.' for attributes.
|
| 23 |
+
TRY_F = False, False, False, FILES # '/' in quotes for file name.
|
| 24 |
+
|
| 25 |
+
# This string includes all chars that may be in an identifier.
|
| 26 |
+
# TODO Update this here and elsewhere.
|
| 27 |
+
ID_CHARS = string.ascii_letters + string.digits + "_"
|
| 28 |
+
|
| 29 |
+
SEPS = f"{os.sep}{os.altsep if os.altsep else ''}"
|
| 30 |
+
TRIGGERS = f".{SEPS}"
|
| 31 |
+
|
| 32 |
+
class AutoComplete:
|
| 33 |
+
|
| 34 |
+
def __init__(self, editwin=None):
|
| 35 |
+
self.editwin = editwin
|
| 36 |
+
if editwin is not None: # not in subprocess or no-gui test
|
| 37 |
+
self.text = editwin.text
|
| 38 |
+
self.autocompletewindow = None
|
| 39 |
+
# id of delayed call, and the index of the text insert when
|
| 40 |
+
# the delayed call was issued. If _delayed_completion_id is
|
| 41 |
+
# None, there is no delayed call.
|
| 42 |
+
self._delayed_completion_id = None
|
| 43 |
+
self._delayed_completion_index = None
|
| 44 |
+
|
| 45 |
+
@classmethod
|
| 46 |
+
def reload(cls):
|
| 47 |
+
cls.popupwait = idleConf.GetOption(
|
| 48 |
+
"extensions", "AutoComplete", "popupwait", type="int", default=0)
|
| 49 |
+
|
| 50 |
+
def _make_autocomplete_window(self): # Makes mocking easier.
|
| 51 |
+
return autocomplete_w.AutoCompleteWindow(self.text)
|
| 52 |
+
|
| 53 |
+
def _remove_autocomplete_window(self, event=None):
|
| 54 |
+
if self.autocompletewindow:
|
| 55 |
+
self.autocompletewindow.hide_window()
|
| 56 |
+
self.autocompletewindow = None
|
| 57 |
+
|
| 58 |
+
def force_open_completions_event(self, event):
|
| 59 |
+
"(^space) Open completion list, even if a function call is needed."
|
| 60 |
+
self.open_completions(FORCE)
|
| 61 |
+
return "break"
|
| 62 |
+
|
| 63 |
+
def autocomplete_event(self, event):
|
| 64 |
+
"(tab) Complete word or open list if multiple options."
|
| 65 |
+
if hasattr(event, "mc_state") and event.mc_state or\
|
| 66 |
+
not self.text.get("insert linestart", "insert").strip():
|
| 67 |
+
# A modifier was pressed along with the tab or
|
| 68 |
+
# there is only previous whitespace on this line, so tab.
|
| 69 |
+
return None
|
| 70 |
+
if self.autocompletewindow and self.autocompletewindow.is_active():
|
| 71 |
+
self.autocompletewindow.complete()
|
| 72 |
+
return "break"
|
| 73 |
+
else:
|
| 74 |
+
opened = self.open_completions(TAB)
|
| 75 |
+
return "break" if opened else None
|
| 76 |
+
|
| 77 |
+
def try_open_completions_event(self, event=None):
|
| 78 |
+
"(./) Open completion list after pause with no movement."
|
| 79 |
+
lastchar = self.text.get("insert-1c")
|
| 80 |
+
if lastchar in TRIGGERS:
|
| 81 |
+
args = TRY_A if lastchar == "." else TRY_F
|
| 82 |
+
self._delayed_completion_index = self.text.index("insert")
|
| 83 |
+
if self._delayed_completion_id is not None:
|
| 84 |
+
self.text.after_cancel(self._delayed_completion_id)
|
| 85 |
+
self._delayed_completion_id = self.text.after(
|
| 86 |
+
self.popupwait, self._delayed_open_completions, args)
|
| 87 |
+
|
| 88 |
+
def _delayed_open_completions(self, args):
|
| 89 |
+
"Call open_completions if index unchanged."
|
| 90 |
+
self._delayed_completion_id = None
|
| 91 |
+
if self.text.index("insert") == self._delayed_completion_index:
|
| 92 |
+
self.open_completions(args)
|
| 93 |
+
|
| 94 |
+
def open_completions(self, args):
|
| 95 |
+
"""Find the completions and create the AutoCompleteWindow.
|
| 96 |
+
Return True if successful (no syntax error or so found).
|
| 97 |
+
If complete is True, then if there's nothing to complete and no
|
| 98 |
+
start of completion, won't open completions and return False.
|
| 99 |
+
If mode is given, will open a completion list only in this mode.
|
| 100 |
+
"""
|
| 101 |
+
evalfuncs, complete, wantwin, mode = args
|
| 102 |
+
# Cancel another delayed call, if it exists.
|
| 103 |
+
if self._delayed_completion_id is not None:
|
| 104 |
+
self.text.after_cancel(self._delayed_completion_id)
|
| 105 |
+
self._delayed_completion_id = None
|
| 106 |
+
|
| 107 |
+
hp = HyperParser(self.editwin, "insert")
|
| 108 |
+
curline = self.text.get("insert linestart", "insert")
|
| 109 |
+
i = j = len(curline)
|
| 110 |
+
if hp.is_in_string() and (not mode or mode==FILES):
|
| 111 |
+
# Find the beginning of the string.
|
| 112 |
+
# fetch_completions will look at the file system to determine
|
| 113 |
+
# whether the string value constitutes an actual file name
|
| 114 |
+
# XXX could consider raw strings here and unescape the string
|
| 115 |
+
# value if it's not raw.
|
| 116 |
+
self._remove_autocomplete_window()
|
| 117 |
+
mode = FILES
|
| 118 |
+
# Find last separator or string start
|
| 119 |
+
while i and curline[i-1] not in "'\"" + SEPS:
|
| 120 |
+
i -= 1
|
| 121 |
+
comp_start = curline[i:j]
|
| 122 |
+
j = i
|
| 123 |
+
# Find string start
|
| 124 |
+
while i and curline[i-1] not in "'\"":
|
| 125 |
+
i -= 1
|
| 126 |
+
comp_what = curline[i:j]
|
| 127 |
+
elif hp.is_in_code() and (not mode or mode==ATTRS):
|
| 128 |
+
self._remove_autocomplete_window()
|
| 129 |
+
mode = ATTRS
|
| 130 |
+
while i and (curline[i-1] in ID_CHARS or ord(curline[i-1]) > 127):
|
| 131 |
+
i -= 1
|
| 132 |
+
comp_start = curline[i:j]
|
| 133 |
+
if i and curline[i-1] == '.': # Need object with attributes.
|
| 134 |
+
hp.set_index("insert-%dc" % (len(curline)-(i-1)))
|
| 135 |
+
comp_what = hp.get_expression()
|
| 136 |
+
if (not comp_what or
|
| 137 |
+
(not evalfuncs and comp_what.find('(') != -1)):
|
| 138 |
+
return None
|
| 139 |
+
else:
|
| 140 |
+
comp_what = ""
|
| 141 |
+
else:
|
| 142 |
+
return None
|
| 143 |
+
|
| 144 |
+
if complete and not comp_what and not comp_start:
|
| 145 |
+
return None
|
| 146 |
+
comp_lists = self.fetch_completions(comp_what, mode)
|
| 147 |
+
if not comp_lists[0]:
|
| 148 |
+
return None
|
| 149 |
+
self.autocompletewindow = self._make_autocomplete_window()
|
| 150 |
+
return not self.autocompletewindow.show_window(
|
| 151 |
+
comp_lists, "insert-%dc" % len(comp_start),
|
| 152 |
+
complete, mode, wantwin)
|
| 153 |
+
|
| 154 |
+
def fetch_completions(self, what, mode):
|
| 155 |
+
"""Return a pair of lists of completions for something. The first list
|
| 156 |
+
is a sublist of the second. Both are sorted.
|
| 157 |
+
|
| 158 |
+
If there is a Python subprocess, get the comp. list there. Otherwise,
|
| 159 |
+
either fetch_completions() is running in the subprocess itself or it
|
| 160 |
+
was called in an IDLE EditorWindow before any script had been run.
|
| 161 |
+
|
| 162 |
+
The subprocess environment is that of the most recently run script. If
|
| 163 |
+
two unrelated modules are being edited some calltips in the current
|
| 164 |
+
module may be inoperative if the module was not the last to run.
|
| 165 |
+
"""
|
| 166 |
+
try:
|
| 167 |
+
rpcclt = self.editwin.flist.pyshell.interp.rpcclt
|
| 168 |
+
except:
|
| 169 |
+
rpcclt = None
|
| 170 |
+
if rpcclt:
|
| 171 |
+
return rpcclt.remotecall("exec", "get_the_completion_list",
|
| 172 |
+
(what, mode), {})
|
| 173 |
+
else:
|
| 174 |
+
if mode == ATTRS:
|
| 175 |
+
if what == "": # Main module names.
|
| 176 |
+
namespace = {**__main__.__builtins__.__dict__,
|
| 177 |
+
**__main__.__dict__}
|
| 178 |
+
bigl = eval("dir()", namespace)
|
| 179 |
+
kwds = (s for s in keyword.kwlist
|
| 180 |
+
if s not in {'True', 'False', 'None'})
|
| 181 |
+
bigl.extend(kwds)
|
| 182 |
+
bigl.sort()
|
| 183 |
+
if "__all__" in bigl:
|
| 184 |
+
smalll = sorted(eval("__all__", namespace))
|
| 185 |
+
else:
|
| 186 |
+
smalll = [s for s in bigl if s[:1] != '_']
|
| 187 |
+
else:
|
| 188 |
+
try:
|
| 189 |
+
entity = self.get_entity(what)
|
| 190 |
+
bigl = dir(entity)
|
| 191 |
+
bigl.sort()
|
| 192 |
+
if "__all__" in bigl:
|
| 193 |
+
smalll = sorted(entity.__all__)
|
| 194 |
+
else:
|
| 195 |
+
smalll = [s for s in bigl if s[:1] != '_']
|
| 196 |
+
except:
|
| 197 |
+
return [], []
|
| 198 |
+
|
| 199 |
+
elif mode == FILES:
|
| 200 |
+
if what == "":
|
| 201 |
+
what = "."
|
| 202 |
+
try:
|
| 203 |
+
expandedpath = os.path.expanduser(what)
|
| 204 |
+
bigl = os.listdir(expandedpath)
|
| 205 |
+
bigl.sort()
|
| 206 |
+
smalll = [s for s in bigl if s[:1] != '.']
|
| 207 |
+
except OSError:
|
| 208 |
+
return [], []
|
| 209 |
+
|
| 210 |
+
if not smalll:
|
| 211 |
+
smalll = bigl
|
| 212 |
+
return smalll, bigl
|
| 213 |
+
|
| 214 |
+
def get_entity(self, name):
|
| 215 |
+
"Lookup name in a namespace spanning sys.modules and __main.dict__."
|
| 216 |
+
return eval(name, {**sys.modules, **__main__.__dict__})
|
| 217 |
+
|
| 218 |
+
|
| 219 |
+
AutoComplete.reload()
|
| 220 |
+
|
| 221 |
+
if __name__ == '__main__':
|
| 222 |
+
from unittest import main
|
| 223 |
+
main('idlelib.idle_test.test_autocomplete', verbosity=2)
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/autoexpand.py
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
'''Complete the current word before the cursor with words in the editor.
|
| 2 |
+
|
| 3 |
+
Each menu selection or shortcut key selection replaces the word with a
|
| 4 |
+
different word with the same prefix. The search for matches begins
|
| 5 |
+
before the target and moves toward the top of the editor. It then starts
|
| 6 |
+
after the cursor and moves down. It then returns to the original word and
|
| 7 |
+
the cycle starts again.
|
| 8 |
+
|
| 9 |
+
Changing the current text line or leaving the cursor in a different
|
| 10 |
+
place before requesting the next selection causes AutoExpand to reset
|
| 11 |
+
its state.
|
| 12 |
+
|
| 13 |
+
There is only one instance of Autoexpand.
|
| 14 |
+
'''
|
| 15 |
+
import re
|
| 16 |
+
import string
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
class AutoExpand:
|
| 20 |
+
wordchars = string.ascii_letters + string.digits + "_"
|
| 21 |
+
|
| 22 |
+
def __init__(self, editwin):
|
| 23 |
+
self.text = editwin.text
|
| 24 |
+
self.bell = self.text.bell
|
| 25 |
+
self.state = None
|
| 26 |
+
|
| 27 |
+
def expand_word_event(self, event):
|
| 28 |
+
"Replace the current word with the next expansion."
|
| 29 |
+
curinsert = self.text.index("insert")
|
| 30 |
+
curline = self.text.get("insert linestart", "insert lineend")
|
| 31 |
+
if not self.state:
|
| 32 |
+
words = self.getwords()
|
| 33 |
+
index = 0
|
| 34 |
+
else:
|
| 35 |
+
words, index, insert, line = self.state
|
| 36 |
+
if insert != curinsert or line != curline:
|
| 37 |
+
words = self.getwords()
|
| 38 |
+
index = 0
|
| 39 |
+
if not words:
|
| 40 |
+
self.bell()
|
| 41 |
+
return "break"
|
| 42 |
+
word = self.getprevword()
|
| 43 |
+
self.text.delete("insert - %d chars" % len(word), "insert")
|
| 44 |
+
newword = words[index]
|
| 45 |
+
index = (index + 1) % len(words)
|
| 46 |
+
if index == 0:
|
| 47 |
+
self.bell() # Warn we cycled around
|
| 48 |
+
self.text.insert("insert", newword)
|
| 49 |
+
curinsert = self.text.index("insert")
|
| 50 |
+
curline = self.text.get("insert linestart", "insert lineend")
|
| 51 |
+
self.state = words, index, curinsert, curline
|
| 52 |
+
return "break"
|
| 53 |
+
|
| 54 |
+
def getwords(self):
|
| 55 |
+
"Return a list of words that match the prefix before the cursor."
|
| 56 |
+
word = self.getprevword()
|
| 57 |
+
if not word:
|
| 58 |
+
return []
|
| 59 |
+
before = self.text.get("1.0", "insert wordstart")
|
| 60 |
+
wbefore = re.findall(r"\b" + word + r"\w+\b", before)
|
| 61 |
+
del before
|
| 62 |
+
after = self.text.get("insert wordend", "end")
|
| 63 |
+
wafter = re.findall(r"\b" + word + r"\w+\b", after)
|
| 64 |
+
del after
|
| 65 |
+
if not wbefore and not wafter:
|
| 66 |
+
return []
|
| 67 |
+
words = []
|
| 68 |
+
dict = {}
|
| 69 |
+
# search backwards through words before
|
| 70 |
+
wbefore.reverse()
|
| 71 |
+
for w in wbefore:
|
| 72 |
+
if dict.get(w):
|
| 73 |
+
continue
|
| 74 |
+
words.append(w)
|
| 75 |
+
dict[w] = w
|
| 76 |
+
# search onwards through words after
|
| 77 |
+
for w in wafter:
|
| 78 |
+
if dict.get(w):
|
| 79 |
+
continue
|
| 80 |
+
words.append(w)
|
| 81 |
+
dict[w] = w
|
| 82 |
+
words.append(word)
|
| 83 |
+
return words
|
| 84 |
+
|
| 85 |
+
def getprevword(self):
|
| 86 |
+
"Return the word prefix before the cursor."
|
| 87 |
+
line = self.text.get("insert linestart", "insert")
|
| 88 |
+
i = len(line)
|
| 89 |
+
while i > 0 and line[i-1] in self.wordchars:
|
| 90 |
+
i = i-1
|
| 91 |
+
return line[i:]
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
if __name__ == '__main__':
|
| 95 |
+
from unittest import main
|
| 96 |
+
main('idlelib.idle_test.test_autoexpand', verbosity=2)
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/calltip.py
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Pop up a reminder of how to call a function.
|
| 2 |
+
|
| 3 |
+
Call Tips are floating windows which display function, class, and method
|
| 4 |
+
parameter and docstring information when you type an opening parenthesis, and
|
| 5 |
+
which disappear when you type a closing parenthesis.
|
| 6 |
+
"""
|
| 7 |
+
import __main__
|
| 8 |
+
import inspect
|
| 9 |
+
import re
|
| 10 |
+
import sys
|
| 11 |
+
import textwrap
|
| 12 |
+
import types
|
| 13 |
+
|
| 14 |
+
from idlelib import calltip_w
|
| 15 |
+
from idlelib.hyperparser import HyperParser
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
class Calltip:
|
| 19 |
+
|
| 20 |
+
def __init__(self, editwin=None):
|
| 21 |
+
if editwin is None: # subprocess and test
|
| 22 |
+
self.editwin = None
|
| 23 |
+
else:
|
| 24 |
+
self.editwin = editwin
|
| 25 |
+
self.text = editwin.text
|
| 26 |
+
self.active_calltip = None
|
| 27 |
+
self._calltip_window = self._make_tk_calltip_window
|
| 28 |
+
|
| 29 |
+
def close(self):
|
| 30 |
+
self._calltip_window = None
|
| 31 |
+
|
| 32 |
+
def _make_tk_calltip_window(self):
|
| 33 |
+
# See __init__ for usage
|
| 34 |
+
return calltip_w.CalltipWindow(self.text)
|
| 35 |
+
|
| 36 |
+
def remove_calltip_window(self, event=None):
|
| 37 |
+
if self.active_calltip:
|
| 38 |
+
self.active_calltip.hidetip()
|
| 39 |
+
self.active_calltip = None
|
| 40 |
+
|
| 41 |
+
def force_open_calltip_event(self, event):
|
| 42 |
+
"The user selected the menu entry or hotkey, open the tip."
|
| 43 |
+
self.open_calltip(True)
|
| 44 |
+
return "break"
|
| 45 |
+
|
| 46 |
+
def try_open_calltip_event(self, event):
|
| 47 |
+
"""Happens when it would be nice to open a calltip, but not really
|
| 48 |
+
necessary, for example after an opening bracket, so function calls
|
| 49 |
+
won't be made.
|
| 50 |
+
"""
|
| 51 |
+
self.open_calltip(False)
|
| 52 |
+
|
| 53 |
+
def refresh_calltip_event(self, event):
|
| 54 |
+
if self.active_calltip and self.active_calltip.tipwindow:
|
| 55 |
+
self.open_calltip(False)
|
| 56 |
+
|
| 57 |
+
def open_calltip(self, evalfuncs):
|
| 58 |
+
"""Maybe close an existing calltip and maybe open a new calltip.
|
| 59 |
+
|
| 60 |
+
Called from (force_open|try_open|refresh)_calltip_event functions.
|
| 61 |
+
"""
|
| 62 |
+
hp = HyperParser(self.editwin, "insert")
|
| 63 |
+
sur_paren = hp.get_surrounding_brackets('(')
|
| 64 |
+
|
| 65 |
+
# If not inside parentheses, no calltip.
|
| 66 |
+
if not sur_paren:
|
| 67 |
+
self.remove_calltip_window()
|
| 68 |
+
return
|
| 69 |
+
|
| 70 |
+
# If a calltip is shown for the current parentheses, do
|
| 71 |
+
# nothing.
|
| 72 |
+
if self.active_calltip:
|
| 73 |
+
opener_line, opener_col = map(int, sur_paren[0].split('.'))
|
| 74 |
+
if (
|
| 75 |
+
(opener_line, opener_col) ==
|
| 76 |
+
(self.active_calltip.parenline, self.active_calltip.parencol)
|
| 77 |
+
):
|
| 78 |
+
return
|
| 79 |
+
|
| 80 |
+
hp.set_index(sur_paren[0])
|
| 81 |
+
try:
|
| 82 |
+
expression = hp.get_expression()
|
| 83 |
+
except ValueError:
|
| 84 |
+
expression = None
|
| 85 |
+
if not expression:
|
| 86 |
+
# No expression before the opening parenthesis, e.g.
|
| 87 |
+
# because it's in a string or the opener for a tuple:
|
| 88 |
+
# Do nothing.
|
| 89 |
+
return
|
| 90 |
+
|
| 91 |
+
# At this point, the current index is after an opening
|
| 92 |
+
# parenthesis, in a section of code, preceded by a valid
|
| 93 |
+
# expression. If there is a calltip shown, it's not for the
|
| 94 |
+
# same index and should be closed.
|
| 95 |
+
self.remove_calltip_window()
|
| 96 |
+
|
| 97 |
+
# Simple, fast heuristic: If the preceding expression includes
|
| 98 |
+
# an opening parenthesis, it likely includes a function call.
|
| 99 |
+
if not evalfuncs and (expression.find('(') != -1):
|
| 100 |
+
return
|
| 101 |
+
|
| 102 |
+
argspec = self.fetch_tip(expression)
|
| 103 |
+
if not argspec:
|
| 104 |
+
return
|
| 105 |
+
self.active_calltip = self._calltip_window()
|
| 106 |
+
self.active_calltip.showtip(argspec, sur_paren[0], sur_paren[1])
|
| 107 |
+
|
| 108 |
+
def fetch_tip(self, expression):
|
| 109 |
+
"""Return the argument list and docstring of a function or class.
|
| 110 |
+
|
| 111 |
+
If there is a Python subprocess, get the calltip there. Otherwise,
|
| 112 |
+
either this fetch_tip() is running in the subprocess or it was
|
| 113 |
+
called in an IDLE running without the subprocess.
|
| 114 |
+
|
| 115 |
+
The subprocess environment is that of the most recently run script. If
|
| 116 |
+
two unrelated modules are being edited some calltips in the current
|
| 117 |
+
module may be inoperative if the module was not the last to run.
|
| 118 |
+
|
| 119 |
+
To find methods, fetch_tip must be fed a fully qualified name.
|
| 120 |
+
|
| 121 |
+
"""
|
| 122 |
+
try:
|
| 123 |
+
rpcclt = self.editwin.flist.pyshell.interp.rpcclt
|
| 124 |
+
except AttributeError:
|
| 125 |
+
rpcclt = None
|
| 126 |
+
if rpcclt:
|
| 127 |
+
return rpcclt.remotecall("exec", "get_the_calltip",
|
| 128 |
+
(expression,), {})
|
| 129 |
+
else:
|
| 130 |
+
return get_argspec(get_entity(expression))
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
def get_entity(expression):
|
| 134 |
+
"""Return the object corresponding to expression evaluated
|
| 135 |
+
in a namespace spanning sys.modules and __main.dict__.
|
| 136 |
+
"""
|
| 137 |
+
if expression:
|
| 138 |
+
namespace = {**sys.modules, **__main__.__dict__}
|
| 139 |
+
try:
|
| 140 |
+
return eval(expression, namespace) # Only protect user code.
|
| 141 |
+
except BaseException:
|
| 142 |
+
# An uncaught exception closes idle, and eval can raise any
|
| 143 |
+
# exception, especially if user classes are involved.
|
| 144 |
+
return None
|
| 145 |
+
|
| 146 |
+
# The following are used in get_argspec and some in tests
|
| 147 |
+
_MAX_COLS = 85
|
| 148 |
+
_MAX_LINES = 5 # enough for bytes
|
| 149 |
+
_INDENT = ' '*4 # for wrapped signatures
|
| 150 |
+
_first_param = re.compile(r'(?<=\()\w*\,?\s*')
|
| 151 |
+
_default_callable_argspec = "See source or doc"
|
| 152 |
+
_invalid_method = "invalid method signature"
|
| 153 |
+
|
| 154 |
+
def get_argspec(ob):
|
| 155 |
+
'''Return a string describing the signature of a callable object, or ''.
|
| 156 |
+
|
| 157 |
+
For Python-coded functions and methods, the first line is introspected.
|
| 158 |
+
Delete 'self' parameter for classes (.__init__) and bound methods.
|
| 159 |
+
The next lines are the first lines of the doc string up to the first
|
| 160 |
+
empty line or _MAX_LINES. For builtins, this typically includes
|
| 161 |
+
the arguments in addition to the return value.
|
| 162 |
+
'''
|
| 163 |
+
# Determine function object fob to inspect.
|
| 164 |
+
try:
|
| 165 |
+
ob_call = ob.__call__
|
| 166 |
+
except BaseException: # Buggy user object could raise anything.
|
| 167 |
+
return '' # No popup for non-callables.
|
| 168 |
+
# For Get_argspecTest.test_buggy_getattr_class, CallA() & CallB().
|
| 169 |
+
fob = ob_call if isinstance(ob_call, types.MethodType) else ob
|
| 170 |
+
|
| 171 |
+
# Initialize argspec and wrap it to get lines.
|
| 172 |
+
try:
|
| 173 |
+
argspec = str(inspect.signature(fob))
|
| 174 |
+
except Exception as err:
|
| 175 |
+
msg = str(err)
|
| 176 |
+
if msg.startswith(_invalid_method):
|
| 177 |
+
return _invalid_method
|
| 178 |
+
else:
|
| 179 |
+
argspec = ''
|
| 180 |
+
|
| 181 |
+
if isinstance(fob, type) and argspec == '()':
|
| 182 |
+
# If fob has no argument, use default callable argspec.
|
| 183 |
+
argspec = _default_callable_argspec
|
| 184 |
+
|
| 185 |
+
lines = (textwrap.wrap(argspec, _MAX_COLS, subsequent_indent=_INDENT)
|
| 186 |
+
if len(argspec) > _MAX_COLS else [argspec] if argspec else [])
|
| 187 |
+
|
| 188 |
+
# Augment lines from docstring, if any, and join to get argspec.
|
| 189 |
+
doc = inspect.getdoc(ob)
|
| 190 |
+
if doc:
|
| 191 |
+
for line in doc.split('\n', _MAX_LINES)[:_MAX_LINES]:
|
| 192 |
+
line = line.strip()
|
| 193 |
+
if not line:
|
| 194 |
+
break
|
| 195 |
+
if len(line) > _MAX_COLS:
|
| 196 |
+
line = line[: _MAX_COLS - 3] + '...'
|
| 197 |
+
lines.append(line)
|
| 198 |
+
argspec = '\n'.join(lines)
|
| 199 |
+
|
| 200 |
+
return argspec or _default_callable_argspec
|
| 201 |
+
|
| 202 |
+
|
| 203 |
+
if __name__ == '__main__':
|
| 204 |
+
from unittest import main
|
| 205 |
+
main('idlelib.idle_test.test_calltip', verbosity=2)
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/codecontext.py
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""codecontext - display the block context above the edit window
|
| 2 |
+
|
| 3 |
+
Once code has scrolled off the top of a window, it can be difficult to
|
| 4 |
+
determine which block you are in. This extension implements a pane at the top
|
| 5 |
+
of each IDLE edit window which provides block structure hints. These hints are
|
| 6 |
+
the lines which contain the block opening keywords, e.g. 'if', for the
|
| 7 |
+
enclosing block. The number of hint lines is determined by the maxlines
|
| 8 |
+
variable in the codecontext section of config-extensions.def. Lines which do
|
| 9 |
+
not open blocks are not shown in the context hints pane.
|
| 10 |
+
|
| 11 |
+
For EditorWindows, <<toggle-code-context>> is bound to CodeContext(self).
|
| 12 |
+
toggle_code_context_event.
|
| 13 |
+
"""
|
| 14 |
+
import re
|
| 15 |
+
from sys import maxsize as INFINITY
|
| 16 |
+
|
| 17 |
+
from tkinter import Frame, Text, TclError
|
| 18 |
+
from tkinter.constants import NSEW, SUNKEN
|
| 19 |
+
|
| 20 |
+
from idlelib.config import idleConf
|
| 21 |
+
|
| 22 |
+
BLOCKOPENERS = {'class', 'def', 'if', 'elif', 'else', 'while', 'for',
|
| 23 |
+
'try', 'except', 'finally', 'with', 'async'}
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def get_spaces_firstword(codeline, c=re.compile(r"^(\s*)(\w*)")):
|
| 27 |
+
"Extract the beginning whitespace and first word from codeline."
|
| 28 |
+
return c.match(codeline).groups()
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
def get_line_info(codeline):
|
| 32 |
+
"""Return tuple of (line indent value, codeline, block start keyword).
|
| 33 |
+
|
| 34 |
+
The indentation of empty lines (or comment lines) is INFINITY.
|
| 35 |
+
If the line does not start a block, the keyword value is False.
|
| 36 |
+
"""
|
| 37 |
+
spaces, firstword = get_spaces_firstword(codeline)
|
| 38 |
+
indent = len(spaces)
|
| 39 |
+
if len(codeline) == indent or codeline[indent] == '#':
|
| 40 |
+
indent = INFINITY
|
| 41 |
+
opener = firstword in BLOCKOPENERS and firstword
|
| 42 |
+
return indent, codeline, opener
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
class CodeContext:
|
| 46 |
+
"Display block context above the edit window."
|
| 47 |
+
UPDATEINTERVAL = 100 # millisec
|
| 48 |
+
|
| 49 |
+
def __init__(self, editwin):
|
| 50 |
+
"""Initialize settings for context block.
|
| 51 |
+
|
| 52 |
+
editwin is the Editor window for the context block.
|
| 53 |
+
self.text is the editor window text widget.
|
| 54 |
+
|
| 55 |
+
self.context displays the code context text above the editor text.
|
| 56 |
+
Initially None, it is toggled via <<toggle-code-context>>.
|
| 57 |
+
self.topvisible is the number of the top text line displayed.
|
| 58 |
+
self.info is a list of (line number, indent level, line text,
|
| 59 |
+
block keyword) tuples for the block structure above topvisible.
|
| 60 |
+
self.info[0] is initialized with a 'dummy' line which
|
| 61 |
+
starts the toplevel 'block' of the module.
|
| 62 |
+
|
| 63 |
+
self.t1 and self.t2 are two timer events on the editor text widget to
|
| 64 |
+
monitor for changes to the context text or editor font.
|
| 65 |
+
"""
|
| 66 |
+
self.editwin = editwin
|
| 67 |
+
self.text = editwin.text
|
| 68 |
+
self._reset()
|
| 69 |
+
|
| 70 |
+
def _reset(self):
|
| 71 |
+
self.context = None
|
| 72 |
+
self.cell00 = None
|
| 73 |
+
self.t1 = None
|
| 74 |
+
self.topvisible = 1
|
| 75 |
+
self.info = [(0, -1, "", False)]
|
| 76 |
+
|
| 77 |
+
@classmethod
|
| 78 |
+
def reload(cls):
|
| 79 |
+
"Load class variables from config."
|
| 80 |
+
cls.context_depth = idleConf.GetOption("extensions", "CodeContext",
|
| 81 |
+
"maxlines", type="int",
|
| 82 |
+
default=15)
|
| 83 |
+
|
| 84 |
+
def __del__(self):
|
| 85 |
+
"Cancel scheduled events."
|
| 86 |
+
if self.t1 is not None:
|
| 87 |
+
try:
|
| 88 |
+
self.text.after_cancel(self.t1)
|
| 89 |
+
except TclError: # pragma: no cover
|
| 90 |
+
pass
|
| 91 |
+
self.t1 = None
|
| 92 |
+
|
| 93 |
+
def toggle_code_context_event(self, event=None):
|
| 94 |
+
"""Toggle code context display.
|
| 95 |
+
|
| 96 |
+
If self.context doesn't exist, create it to match the size of the editor
|
| 97 |
+
window text (toggle on). If it does exist, destroy it (toggle off).
|
| 98 |
+
Return 'break' to complete the processing of the binding.
|
| 99 |
+
"""
|
| 100 |
+
if self.context is None:
|
| 101 |
+
# Calculate the border width and horizontal padding required to
|
| 102 |
+
# align the context with the text in the main Text widget.
|
| 103 |
+
#
|
| 104 |
+
# All values are passed through getint(), since some
|
| 105 |
+
# values may be pixel objects, which can't simply be added to ints.
|
| 106 |
+
widgets = self.editwin.text, self.editwin.text_frame
|
| 107 |
+
# Calculate the required horizontal padding and border width.
|
| 108 |
+
padx = 0
|
| 109 |
+
border = 0
|
| 110 |
+
for widget in widgets:
|
| 111 |
+
info = (widget.grid_info()
|
| 112 |
+
if widget is self.editwin.text
|
| 113 |
+
else widget.pack_info())
|
| 114 |
+
padx += widget.tk.getint(info['padx'])
|
| 115 |
+
padx += widget.tk.getint(widget.cget('padx'))
|
| 116 |
+
border += widget.tk.getint(widget.cget('border'))
|
| 117 |
+
context = self.context = Text(
|
| 118 |
+
self.editwin.text_frame,
|
| 119 |
+
height=1,
|
| 120 |
+
width=1, # Don't request more than we get.
|
| 121 |
+
highlightthickness=0,
|
| 122 |
+
padx=padx, border=border, relief=SUNKEN, state='disabled')
|
| 123 |
+
self.update_font()
|
| 124 |
+
self.update_highlight_colors()
|
| 125 |
+
context.bind('<ButtonRelease-1>', self.jumptoline)
|
| 126 |
+
# Get the current context and initiate the recurring update event.
|
| 127 |
+
self.timer_event()
|
| 128 |
+
# Grid the context widget above the text widget.
|
| 129 |
+
context.grid(row=0, column=1, sticky=NSEW)
|
| 130 |
+
|
| 131 |
+
line_number_colors = idleConf.GetHighlight(idleConf.CurrentTheme(),
|
| 132 |
+
'linenumber')
|
| 133 |
+
self.cell00 = Frame(self.editwin.text_frame,
|
| 134 |
+
bg=line_number_colors['background'])
|
| 135 |
+
self.cell00.grid(row=0, column=0, sticky=NSEW)
|
| 136 |
+
menu_status = 'Hide'
|
| 137 |
+
else:
|
| 138 |
+
self.context.destroy()
|
| 139 |
+
self.context = None
|
| 140 |
+
self.cell00.destroy()
|
| 141 |
+
self.cell00 = None
|
| 142 |
+
self.text.after_cancel(self.t1)
|
| 143 |
+
self._reset()
|
| 144 |
+
menu_status = 'Show'
|
| 145 |
+
self.editwin.update_menu_label(menu='options', index='*ode*ontext',
|
| 146 |
+
label=f'{menu_status} Code Context')
|
| 147 |
+
return "break"
|
| 148 |
+
|
| 149 |
+
def get_context(self, new_topvisible, stopline=1, stopindent=0):
|
| 150 |
+
"""Return a list of block line tuples and the 'last' indent.
|
| 151 |
+
|
| 152 |
+
The tuple fields are (linenum, indent, text, opener).
|
| 153 |
+
The list represents header lines from new_topvisible back to
|
| 154 |
+
stopline with successively shorter indents > stopindent.
|
| 155 |
+
The list is returned ordered by line number.
|
| 156 |
+
Last indent returned is the smallest indent observed.
|
| 157 |
+
"""
|
| 158 |
+
assert stopline > 0
|
| 159 |
+
lines = []
|
| 160 |
+
# The indentation level we are currently in.
|
| 161 |
+
lastindent = INFINITY
|
| 162 |
+
# For a line to be interesting, it must begin with a block opening
|
| 163 |
+
# keyword, and have less indentation than lastindent.
|
| 164 |
+
for linenum in range(new_topvisible, stopline-1, -1):
|
| 165 |
+
codeline = self.text.get(f'{linenum}.0', f'{linenum}.end')
|
| 166 |
+
indent, text, opener = get_line_info(codeline)
|
| 167 |
+
if indent < lastindent:
|
| 168 |
+
lastindent = indent
|
| 169 |
+
if opener in ("else", "elif"):
|
| 170 |
+
# Also show the if statement.
|
| 171 |
+
lastindent += 1
|
| 172 |
+
if opener and linenum < new_topvisible and indent >= stopindent:
|
| 173 |
+
lines.append((linenum, indent, text, opener))
|
| 174 |
+
if lastindent <= stopindent:
|
| 175 |
+
break
|
| 176 |
+
lines.reverse()
|
| 177 |
+
return lines, lastindent
|
| 178 |
+
|
| 179 |
+
def update_code_context(self):
|
| 180 |
+
"""Update context information and lines visible in the context pane.
|
| 181 |
+
|
| 182 |
+
No update is done if the text hasn't been scrolled. If the text
|
| 183 |
+
was scrolled, the lines that should be shown in the context will
|
| 184 |
+
be retrieved and the context area will be updated with the code,
|
| 185 |
+
up to the number of maxlines.
|
| 186 |
+
"""
|
| 187 |
+
new_topvisible = self.editwin.getlineno("@0,0")
|
| 188 |
+
if self.topvisible == new_topvisible: # Haven't scrolled.
|
| 189 |
+
return
|
| 190 |
+
if self.topvisible < new_topvisible: # Scroll down.
|
| 191 |
+
lines, lastindent = self.get_context(new_topvisible,
|
| 192 |
+
self.topvisible)
|
| 193 |
+
# Retain only context info applicable to the region
|
| 194 |
+
# between topvisible and new_topvisible.
|
| 195 |
+
while self.info[-1][1] >= lastindent:
|
| 196 |
+
del self.info[-1]
|
| 197 |
+
else: # self.topvisible > new_topvisible: # Scroll up.
|
| 198 |
+
stopindent = self.info[-1][1] + 1
|
| 199 |
+
# Retain only context info associated
|
| 200 |
+
# with lines above new_topvisible.
|
| 201 |
+
while self.info[-1][0] >= new_topvisible:
|
| 202 |
+
stopindent = self.info[-1][1]
|
| 203 |
+
del self.info[-1]
|
| 204 |
+
lines, lastindent = self.get_context(new_topvisible,
|
| 205 |
+
self.info[-1][0]+1,
|
| 206 |
+
stopindent)
|
| 207 |
+
self.info.extend(lines)
|
| 208 |
+
self.topvisible = new_topvisible
|
| 209 |
+
# Last context_depth context lines.
|
| 210 |
+
context_strings = [x[2] for x in self.info[-self.context_depth:]]
|
| 211 |
+
showfirst = 0 if context_strings[0] else 1
|
| 212 |
+
# Update widget.
|
| 213 |
+
self.context['height'] = len(context_strings) - showfirst
|
| 214 |
+
self.context['state'] = 'normal'
|
| 215 |
+
self.context.delete('1.0', 'end')
|
| 216 |
+
self.context.insert('end', '\n'.join(context_strings[showfirst:]))
|
| 217 |
+
self.context['state'] = 'disabled'
|
| 218 |
+
|
| 219 |
+
def jumptoline(self, event=None):
|
| 220 |
+
""" Show clicked context line at top of editor.
|
| 221 |
+
|
| 222 |
+
If a selection was made, don't jump; allow copying.
|
| 223 |
+
If no visible context, show the top line of the file.
|
| 224 |
+
"""
|
| 225 |
+
try:
|
| 226 |
+
self.context.index("sel.first")
|
| 227 |
+
except TclError:
|
| 228 |
+
lines = len(self.info)
|
| 229 |
+
if lines == 1: # No context lines are showing.
|
| 230 |
+
newtop = 1
|
| 231 |
+
else:
|
| 232 |
+
# Line number clicked.
|
| 233 |
+
contextline = int(float(self.context.index('insert')))
|
| 234 |
+
# Lines not displayed due to maxlines.
|
| 235 |
+
offset = max(1, lines - self.context_depth) - 1
|
| 236 |
+
newtop = self.info[offset + contextline][0]
|
| 237 |
+
self.text.yview(f'{newtop}.0')
|
| 238 |
+
self.update_code_context()
|
| 239 |
+
|
| 240 |
+
def timer_event(self):
|
| 241 |
+
"Event on editor text widget triggered every UPDATEINTERVAL ms."
|
| 242 |
+
if self.context is not None:
|
| 243 |
+
self.update_code_context()
|
| 244 |
+
self.t1 = self.text.after(self.UPDATEINTERVAL, self.timer_event)
|
| 245 |
+
|
| 246 |
+
def update_font(self):
|
| 247 |
+
if self.context is not None:
|
| 248 |
+
font = idleConf.GetFont(self.text, 'main', 'EditorWindow')
|
| 249 |
+
self.context['font'] = font
|
| 250 |
+
|
| 251 |
+
def update_highlight_colors(self):
|
| 252 |
+
if self.context is not None:
|
| 253 |
+
colors = idleConf.GetHighlight(idleConf.CurrentTheme(), 'context')
|
| 254 |
+
self.context['background'] = colors['background']
|
| 255 |
+
self.context['foreground'] = colors['foreground']
|
| 256 |
+
|
| 257 |
+
if self.cell00 is not None:
|
| 258 |
+
line_number_colors = idleConf.GetHighlight(idleConf.CurrentTheme(),
|
| 259 |
+
'linenumber')
|
| 260 |
+
self.cell00.config(bg=line_number_colors['background'])
|
| 261 |
+
|
| 262 |
+
|
| 263 |
+
CodeContext.reload()
|
| 264 |
+
|
| 265 |
+
|
| 266 |
+
if __name__ == "__main__":
|
| 267 |
+
from unittest import main
|
| 268 |
+
main('idlelib.idle_test.test_codecontext', verbosity=2, exit=False)
|
| 269 |
+
|
| 270 |
+
# Add htest.
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/colorizer.py
ADDED
|
@@ -0,0 +1,340 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import builtins
|
| 2 |
+
import keyword
|
| 3 |
+
import re
|
| 4 |
+
import time
|
| 5 |
+
|
| 6 |
+
from idlelib.config import idleConf
|
| 7 |
+
from idlelib.delegator import Delegator
|
| 8 |
+
|
| 9 |
+
DEBUG = False
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
def any(name, alternates):
|
| 13 |
+
"Return a named group pattern matching list of alternates."
|
| 14 |
+
return "(?P<%s>" % name + "|".join(alternates) + ")"
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def make_pat():
|
| 18 |
+
kw = r"\b" + any("KEYWORD", keyword.kwlist) + r"\b"
|
| 19 |
+
builtinlist = [str(name) for name in dir(builtins)
|
| 20 |
+
if not name.startswith('_') and
|
| 21 |
+
name not in keyword.kwlist]
|
| 22 |
+
builtin = r"([^.'\"\\#]\b|^)" + any("BUILTIN", builtinlist) + r"\b"
|
| 23 |
+
comment = any("COMMENT", [r"#[^\n]*"])
|
| 24 |
+
stringprefix = r"(?i:r|u|f|fr|rf|b|br|rb)?"
|
| 25 |
+
sqstring = stringprefix + r"'[^'\\\n]*(\\.[^'\\\n]*)*'?"
|
| 26 |
+
dqstring = stringprefix + r'"[^"\\\n]*(\\.[^"\\\n]*)*"?'
|
| 27 |
+
sq3string = stringprefix + r"'''[^'\\]*((\\.|'(?!''))[^'\\]*)*(''')?"
|
| 28 |
+
dq3string = stringprefix + r'"""[^"\\]*((\\.|"(?!""))[^"\\]*)*(""")?'
|
| 29 |
+
string = any("STRING", [sq3string, dq3string, sqstring, dqstring])
|
| 30 |
+
return (kw + "|" + builtin + "|" + comment + "|" + string +
|
| 31 |
+
"|" + any("SYNC", [r"\n"]))
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
prog = re.compile(make_pat(), re.S)
|
| 35 |
+
idprog = re.compile(r"\s+(\w+)", re.S)
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def color_config(text):
|
| 39 |
+
"""Set color options of Text widget.
|
| 40 |
+
|
| 41 |
+
If ColorDelegator is used, this should be called first.
|
| 42 |
+
"""
|
| 43 |
+
# Called from htest, TextFrame, Editor, and Turtledemo.
|
| 44 |
+
# Not automatic because ColorDelegator does not know 'text'.
|
| 45 |
+
theme = idleConf.CurrentTheme()
|
| 46 |
+
normal_colors = idleConf.GetHighlight(theme, 'normal')
|
| 47 |
+
cursor_color = idleConf.GetHighlight(theme, 'cursor')['foreground']
|
| 48 |
+
select_colors = idleConf.GetHighlight(theme, 'hilite')
|
| 49 |
+
text.config(
|
| 50 |
+
foreground=normal_colors['foreground'],
|
| 51 |
+
background=normal_colors['background'],
|
| 52 |
+
insertbackground=cursor_color,
|
| 53 |
+
selectforeground=select_colors['foreground'],
|
| 54 |
+
selectbackground=select_colors['background'],
|
| 55 |
+
inactiveselectbackground=select_colors['background'], # new in 8.5
|
| 56 |
+
)
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
class ColorDelegator(Delegator):
|
| 60 |
+
"""Delegator for syntax highlighting (text coloring).
|
| 61 |
+
|
| 62 |
+
Instance variables:
|
| 63 |
+
delegate: Delegator below this one in the stack, meaning the
|
| 64 |
+
one this one delegates to.
|
| 65 |
+
|
| 66 |
+
Used to track state:
|
| 67 |
+
after_id: Identifier for scheduled after event, which is a
|
| 68 |
+
timer for colorizing the text.
|
| 69 |
+
allow_colorizing: Boolean toggle for applying colorizing.
|
| 70 |
+
colorizing: Boolean flag when colorizing is in process.
|
| 71 |
+
stop_colorizing: Boolean flag to end an active colorizing
|
| 72 |
+
process.
|
| 73 |
+
"""
|
| 74 |
+
|
| 75 |
+
def __init__(self):
|
| 76 |
+
Delegator.__init__(self)
|
| 77 |
+
self.init_state()
|
| 78 |
+
self.prog = prog
|
| 79 |
+
self.idprog = idprog
|
| 80 |
+
self.LoadTagDefs()
|
| 81 |
+
|
| 82 |
+
def init_state(self):
|
| 83 |
+
"Initialize variables that track colorizing state."
|
| 84 |
+
self.after_id = None
|
| 85 |
+
self.allow_colorizing = True
|
| 86 |
+
self.stop_colorizing = False
|
| 87 |
+
self.colorizing = False
|
| 88 |
+
|
| 89 |
+
def setdelegate(self, delegate):
|
| 90 |
+
"""Set the delegate for this instance.
|
| 91 |
+
|
| 92 |
+
A delegate is an instance of a Delegator class and each
|
| 93 |
+
delegate points to the next delegator in the stack. This
|
| 94 |
+
allows multiple delegators to be chained together for a
|
| 95 |
+
widget. The bottom delegate for a colorizer is a Text
|
| 96 |
+
widget.
|
| 97 |
+
|
| 98 |
+
If there is a delegate, also start the colorizing process.
|
| 99 |
+
"""
|
| 100 |
+
if self.delegate is not None:
|
| 101 |
+
self.unbind("<<toggle-auto-coloring>>")
|
| 102 |
+
Delegator.setdelegate(self, delegate)
|
| 103 |
+
if delegate is not None:
|
| 104 |
+
self.config_colors()
|
| 105 |
+
self.bind("<<toggle-auto-coloring>>", self.toggle_colorize_event)
|
| 106 |
+
self.notify_range("1.0", "end")
|
| 107 |
+
else:
|
| 108 |
+
# No delegate - stop any colorizing.
|
| 109 |
+
self.stop_colorizing = True
|
| 110 |
+
self.allow_colorizing = False
|
| 111 |
+
|
| 112 |
+
def config_colors(self):
|
| 113 |
+
"Configure text widget tags with colors from tagdefs."
|
| 114 |
+
for tag, cnf in self.tagdefs.items():
|
| 115 |
+
self.tag_configure(tag, **cnf)
|
| 116 |
+
self.tag_raise('sel')
|
| 117 |
+
|
| 118 |
+
def LoadTagDefs(self):
|
| 119 |
+
"Create dictionary of tag names to text colors."
|
| 120 |
+
theme = idleConf.CurrentTheme()
|
| 121 |
+
self.tagdefs = {
|
| 122 |
+
"COMMENT": idleConf.GetHighlight(theme, "comment"),
|
| 123 |
+
"KEYWORD": idleConf.GetHighlight(theme, "keyword"),
|
| 124 |
+
"BUILTIN": idleConf.GetHighlight(theme, "builtin"),
|
| 125 |
+
"STRING": idleConf.GetHighlight(theme, "string"),
|
| 126 |
+
"DEFINITION": idleConf.GetHighlight(theme, "definition"),
|
| 127 |
+
"SYNC": {'background': None, 'foreground': None},
|
| 128 |
+
"TODO": {'background': None, 'foreground': None},
|
| 129 |
+
"ERROR": idleConf.GetHighlight(theme, "error"),
|
| 130 |
+
# "hit" is used by ReplaceDialog to mark matches. It shouldn't be changed by Colorizer, but
|
| 131 |
+
# that currently isn't technically possible. This should be moved elsewhere in the future
|
| 132 |
+
# when fixing the "hit" tag's visibility, or when the replace dialog is replaced with a
|
| 133 |
+
# non-modal alternative.
|
| 134 |
+
"hit": idleConf.GetHighlight(theme, "hit"),
|
| 135 |
+
}
|
| 136 |
+
|
| 137 |
+
if DEBUG: print('tagdefs', self.tagdefs)
|
| 138 |
+
|
| 139 |
+
def insert(self, index, chars, tags=None):
|
| 140 |
+
"Insert chars into widget at index and mark for colorizing."
|
| 141 |
+
index = self.index(index)
|
| 142 |
+
self.delegate.insert(index, chars, tags)
|
| 143 |
+
self.notify_range(index, index + "+%dc" % len(chars))
|
| 144 |
+
|
| 145 |
+
def delete(self, index1, index2=None):
|
| 146 |
+
"Delete chars between indexes and mark for colorizing."
|
| 147 |
+
index1 = self.index(index1)
|
| 148 |
+
self.delegate.delete(index1, index2)
|
| 149 |
+
self.notify_range(index1)
|
| 150 |
+
|
| 151 |
+
def notify_range(self, index1, index2=None):
|
| 152 |
+
"Mark text changes for processing and restart colorizing, if active."
|
| 153 |
+
self.tag_add("TODO", index1, index2)
|
| 154 |
+
if self.after_id:
|
| 155 |
+
if DEBUG: print("colorizing already scheduled")
|
| 156 |
+
return
|
| 157 |
+
if self.colorizing:
|
| 158 |
+
self.stop_colorizing = True
|
| 159 |
+
if DEBUG: print("stop colorizing")
|
| 160 |
+
if self.allow_colorizing:
|
| 161 |
+
if DEBUG: print("schedule colorizing")
|
| 162 |
+
self.after_id = self.after(1, self.recolorize)
|
| 163 |
+
return
|
| 164 |
+
|
| 165 |
+
def close(self):
|
| 166 |
+
if self.after_id:
|
| 167 |
+
after_id = self.after_id
|
| 168 |
+
self.after_id = None
|
| 169 |
+
if DEBUG: print("cancel scheduled recolorizer")
|
| 170 |
+
self.after_cancel(after_id)
|
| 171 |
+
self.allow_colorizing = False
|
| 172 |
+
self.stop_colorizing = True
|
| 173 |
+
|
| 174 |
+
def toggle_colorize_event(self, event=None):
|
| 175 |
+
"""Toggle colorizing on and off.
|
| 176 |
+
|
| 177 |
+
When toggling off, if colorizing is scheduled or is in
|
| 178 |
+
process, it will be cancelled and/or stopped.
|
| 179 |
+
|
| 180 |
+
When toggling on, colorizing will be scheduled.
|
| 181 |
+
"""
|
| 182 |
+
if self.after_id:
|
| 183 |
+
after_id = self.after_id
|
| 184 |
+
self.after_id = None
|
| 185 |
+
if DEBUG: print("cancel scheduled recolorizer")
|
| 186 |
+
self.after_cancel(after_id)
|
| 187 |
+
if self.allow_colorizing and self.colorizing:
|
| 188 |
+
if DEBUG: print("stop colorizing")
|
| 189 |
+
self.stop_colorizing = True
|
| 190 |
+
self.allow_colorizing = not self.allow_colorizing
|
| 191 |
+
if self.allow_colorizing and not self.colorizing:
|
| 192 |
+
self.after_id = self.after(1, self.recolorize)
|
| 193 |
+
if DEBUG:
|
| 194 |
+
print("auto colorizing turned",
|
| 195 |
+
"on" if self.allow_colorizing else "off")
|
| 196 |
+
return "break"
|
| 197 |
+
|
| 198 |
+
def recolorize(self):
|
| 199 |
+
"""Timer event (every 1ms) to colorize text.
|
| 200 |
+
|
| 201 |
+
Colorizing is only attempted when the text widget exists,
|
| 202 |
+
when colorizing is toggled on, and when the colorizing
|
| 203 |
+
process is not already running.
|
| 204 |
+
|
| 205 |
+
After colorizing is complete, some cleanup is done to
|
| 206 |
+
make sure that all the text has been colorized.
|
| 207 |
+
"""
|
| 208 |
+
self.after_id = None
|
| 209 |
+
if not self.delegate:
|
| 210 |
+
if DEBUG: print("no delegate")
|
| 211 |
+
return
|
| 212 |
+
if not self.allow_colorizing:
|
| 213 |
+
if DEBUG: print("auto colorizing is off")
|
| 214 |
+
return
|
| 215 |
+
if self.colorizing:
|
| 216 |
+
if DEBUG: print("already colorizing")
|
| 217 |
+
return
|
| 218 |
+
try:
|
| 219 |
+
self.stop_colorizing = False
|
| 220 |
+
self.colorizing = True
|
| 221 |
+
if DEBUG: print("colorizing...")
|
| 222 |
+
t0 = time.perf_counter()
|
| 223 |
+
self.recolorize_main()
|
| 224 |
+
t1 = time.perf_counter()
|
| 225 |
+
if DEBUG: print("%.3f seconds" % (t1-t0))
|
| 226 |
+
finally:
|
| 227 |
+
self.colorizing = False
|
| 228 |
+
if self.allow_colorizing and self.tag_nextrange("TODO", "1.0"):
|
| 229 |
+
if DEBUG: print("reschedule colorizing")
|
| 230 |
+
self.after_id = self.after(1, self.recolorize)
|
| 231 |
+
|
| 232 |
+
def recolorize_main(self):
|
| 233 |
+
"Evaluate text and apply colorizing tags."
|
| 234 |
+
next = "1.0"
|
| 235 |
+
while True:
|
| 236 |
+
item = self.tag_nextrange("TODO", next)
|
| 237 |
+
if not item:
|
| 238 |
+
break
|
| 239 |
+
head, tail = item
|
| 240 |
+
self.tag_remove("SYNC", head, tail)
|
| 241 |
+
item = self.tag_prevrange("SYNC", head)
|
| 242 |
+
head = item[1] if item else "1.0"
|
| 243 |
+
|
| 244 |
+
chars = ""
|
| 245 |
+
next = head
|
| 246 |
+
lines_to_get = 1
|
| 247 |
+
ok = False
|
| 248 |
+
while not ok:
|
| 249 |
+
mark = next
|
| 250 |
+
next = self.index(mark + "+%d lines linestart" %
|
| 251 |
+
lines_to_get)
|
| 252 |
+
lines_to_get = min(lines_to_get * 2, 100)
|
| 253 |
+
ok = "SYNC" in self.tag_names(next + "-1c")
|
| 254 |
+
line = self.get(mark, next)
|
| 255 |
+
##print head, "get", mark, next, "->", repr(line)
|
| 256 |
+
if not line:
|
| 257 |
+
return
|
| 258 |
+
for tag in self.tagdefs:
|
| 259 |
+
self.tag_remove(tag, mark, next)
|
| 260 |
+
chars = chars + line
|
| 261 |
+
m = self.prog.search(chars)
|
| 262 |
+
while m:
|
| 263 |
+
for key, value in m.groupdict().items():
|
| 264 |
+
if value:
|
| 265 |
+
a, b = m.span(key)
|
| 266 |
+
self.tag_add(key,
|
| 267 |
+
head + "+%dc" % a,
|
| 268 |
+
head + "+%dc" % b)
|
| 269 |
+
if value in ("def", "class"):
|
| 270 |
+
m1 = self.idprog.match(chars, b)
|
| 271 |
+
if m1:
|
| 272 |
+
a, b = m1.span(1)
|
| 273 |
+
self.tag_add("DEFINITION",
|
| 274 |
+
head + "+%dc" % a,
|
| 275 |
+
head + "+%dc" % b)
|
| 276 |
+
m = self.prog.search(chars, m.end())
|
| 277 |
+
if "SYNC" in self.tag_names(next + "-1c"):
|
| 278 |
+
head = next
|
| 279 |
+
chars = ""
|
| 280 |
+
else:
|
| 281 |
+
ok = False
|
| 282 |
+
if not ok:
|
| 283 |
+
# We're in an inconsistent state, and the call to
|
| 284 |
+
# update may tell us to stop. It may also change
|
| 285 |
+
# the correct value for "next" (since this is a
|
| 286 |
+
# line.col string, not a true mark). So leave a
|
| 287 |
+
# crumb telling the next invocation to resume here
|
| 288 |
+
# in case update tells us to leave.
|
| 289 |
+
self.tag_add("TODO", next)
|
| 290 |
+
self.update()
|
| 291 |
+
if self.stop_colorizing:
|
| 292 |
+
if DEBUG: print("colorizing stopped")
|
| 293 |
+
return
|
| 294 |
+
|
| 295 |
+
def removecolors(self):
|
| 296 |
+
"Remove all colorizing tags."
|
| 297 |
+
for tag in self.tagdefs:
|
| 298 |
+
self.tag_remove(tag, "1.0", "end")
|
| 299 |
+
|
| 300 |
+
|
| 301 |
+
def _color_delegator(parent): # htest #
|
| 302 |
+
from tkinter import Toplevel, Text
|
| 303 |
+
from idlelib.percolator import Percolator
|
| 304 |
+
|
| 305 |
+
top = Toplevel(parent)
|
| 306 |
+
top.title("Test ColorDelegator")
|
| 307 |
+
x, y = map(int, parent.geometry().split('+')[1:])
|
| 308 |
+
top.geometry("700x250+%d+%d" % (x + 20, y + 175))
|
| 309 |
+
source = (
|
| 310 |
+
"if True: int ('1') # keyword, builtin, string, comment\n"
|
| 311 |
+
"elif False: print(0)\n"
|
| 312 |
+
"else: float(None)\n"
|
| 313 |
+
"if iF + If + IF: 'keyword matching must respect case'\n"
|
| 314 |
+
"if'': x or'' # valid keyword-string no-space combinations\n"
|
| 315 |
+
"async def f(): await g()\n"
|
| 316 |
+
"# All valid prefixes for unicode and byte strings should be colored.\n"
|
| 317 |
+
"'x', '''x''', \"x\", \"\"\"x\"\"\"\n"
|
| 318 |
+
"r'x', u'x', R'x', U'x', f'x', F'x'\n"
|
| 319 |
+
"fr'x', Fr'x', fR'x', FR'x', rf'x', rF'x', Rf'x', RF'x'\n"
|
| 320 |
+
"b'x',B'x', br'x',Br'x',bR'x',BR'x', rb'x', rB'x',Rb'x',RB'x'\n"
|
| 321 |
+
"# Invalid combinations of legal characters should be half colored.\n"
|
| 322 |
+
"ur'x', ru'x', uf'x', fu'x', UR'x', ufr'x', rfu'x', xf'x', fx'x'\n"
|
| 323 |
+
)
|
| 324 |
+
text = Text(top, background="white")
|
| 325 |
+
text.pack(expand=1, fill="both")
|
| 326 |
+
text.insert("insert", source)
|
| 327 |
+
text.focus_set()
|
| 328 |
+
|
| 329 |
+
color_config(text)
|
| 330 |
+
p = Percolator(text)
|
| 331 |
+
d = ColorDelegator()
|
| 332 |
+
p.insertfilter(d)
|
| 333 |
+
|
| 334 |
+
|
| 335 |
+
if __name__ == "__main__":
|
| 336 |
+
from unittest import main
|
| 337 |
+
main('idlelib.idle_test.test_colorizer', verbosity=2, exit=False)
|
| 338 |
+
|
| 339 |
+
from idlelib.idle_test.htest import run
|
| 340 |
+
run(_color_delegator)
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/config-extensions.def
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# config-extensions.def
|
| 2 |
+
#
|
| 3 |
+
# The following sections are for features that are no longer extensions.
|
| 4 |
+
# Their options values are left here for back-compatibility.
|
| 5 |
+
|
| 6 |
+
[AutoComplete]
|
| 7 |
+
popupwait= 2000
|
| 8 |
+
|
| 9 |
+
[CodeContext]
|
| 10 |
+
maxlines= 15
|
| 11 |
+
|
| 12 |
+
[FormatParagraph]
|
| 13 |
+
max-width= 72
|
| 14 |
+
|
| 15 |
+
[ParenMatch]
|
| 16 |
+
style= expression
|
| 17 |
+
flash-delay= 500
|
| 18 |
+
bell= True
|
| 19 |
+
|
| 20 |
+
# IDLE reads several config files to determine user preferences. This
|
| 21 |
+
# file is the default configuration file for IDLE extensions settings.
|
| 22 |
+
#
|
| 23 |
+
# Each extension must have at least one section, named after the
|
| 24 |
+
# extension module. This section must contain an 'enable' item (=True to
|
| 25 |
+
# enable the extension, =False to disable it), it may contain
|
| 26 |
+
# 'enable_editor' or 'enable_shell' items, to apply it only to editor ir
|
| 27 |
+
# shell windows, and may also contain any other general configuration
|
| 28 |
+
# items for the extension. Other True/False values will also be
|
| 29 |
+
# recognized as boolean by the Extension Configuration dialog.
|
| 30 |
+
#
|
| 31 |
+
# Each extension must define at least one section named
|
| 32 |
+
# ExtensionName_bindings or ExtensionName_cfgBindings. If present,
|
| 33 |
+
# ExtensionName_bindings defines virtual event bindings for the
|
| 34 |
+
# extension that are not user re-configurable. If present,
|
| 35 |
+
# ExtensionName_cfgBindings defines virtual event bindings for the
|
| 36 |
+
# extension that may be sensibly re-configured.
|
| 37 |
+
#
|
| 38 |
+
# If there are no keybindings for a menus' virtual events, include lines
|
| 39 |
+
# like <<toggle-code-context>>=.
|
| 40 |
+
#
|
| 41 |
+
# Currently it is necessary to manually modify this file to change
|
| 42 |
+
# extension key bindings and default values. To customize, create
|
| 43 |
+
# ~/.idlerc/config-extensions.cfg and append the appropriate customized
|
| 44 |
+
# section(s). Those sections will override the defaults in this file.
|
| 45 |
+
#
|
| 46 |
+
# Note: If a keybinding is already in use when the extension is loaded,
|
| 47 |
+
# the extension's virtual event's keybinding will be set to ''.
|
| 48 |
+
#
|
| 49 |
+
# See config-keys.def for notes on specifying keys and extend.txt for
|
| 50 |
+
# information on creating IDLE extensions.
|
| 51 |
+
|
| 52 |
+
# A fake extension for testing and example purposes. When enabled and
|
| 53 |
+
# invoked, inserts or deletes z-text at beginning of every line.
|
| 54 |
+
[ZzDummy]
|
| 55 |
+
enable= False
|
| 56 |
+
enable_shell = False
|
| 57 |
+
enable_editor = True
|
| 58 |
+
z-text= Z
|
| 59 |
+
[ZzDummy_cfgBindings]
|
| 60 |
+
z-in= <Control-Shift-KeyRelease-Insert>
|
| 61 |
+
[ZzDummy_bindings]
|
| 62 |
+
z-out= <Control-Shift-KeyRelease-Delete>
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/config-highlight.def
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# IDLE reads several config files to determine user preferences. This
|
| 2 |
+
# file is the default config file for idle highlight theme settings.
|
| 3 |
+
|
| 4 |
+
[IDLE Classic]
|
| 5 |
+
normal-foreground= #000000
|
| 6 |
+
normal-background= #ffffff
|
| 7 |
+
keyword-foreground= #ff7700
|
| 8 |
+
keyword-background= #ffffff
|
| 9 |
+
builtin-foreground= #900090
|
| 10 |
+
builtin-background= #ffffff
|
| 11 |
+
comment-foreground= #dd0000
|
| 12 |
+
comment-background= #ffffff
|
| 13 |
+
string-foreground= #00aa00
|
| 14 |
+
string-background= #ffffff
|
| 15 |
+
definition-foreground= #0000ff
|
| 16 |
+
definition-background= #ffffff
|
| 17 |
+
hilite-foreground= #000000
|
| 18 |
+
hilite-background= gray
|
| 19 |
+
break-foreground= black
|
| 20 |
+
break-background= #ffff55
|
| 21 |
+
hit-foreground= #ffffff
|
| 22 |
+
hit-background= #000000
|
| 23 |
+
error-foreground= #000000
|
| 24 |
+
error-background= #ff7777
|
| 25 |
+
context-foreground= #000000
|
| 26 |
+
context-background= lightgray
|
| 27 |
+
linenumber-foreground= gray
|
| 28 |
+
linenumber-background= #ffffff
|
| 29 |
+
#cursor (only foreground can be set, restart IDLE)
|
| 30 |
+
cursor-foreground= black
|
| 31 |
+
#shell window
|
| 32 |
+
stdout-foreground= blue
|
| 33 |
+
stdout-background= #ffffff
|
| 34 |
+
stderr-foreground= red
|
| 35 |
+
stderr-background= #ffffff
|
| 36 |
+
console-foreground= #770000
|
| 37 |
+
console-background= #ffffff
|
| 38 |
+
|
| 39 |
+
[IDLE New]
|
| 40 |
+
normal-foreground= #000000
|
| 41 |
+
normal-background= #ffffff
|
| 42 |
+
keyword-foreground= #ff7700
|
| 43 |
+
keyword-background= #ffffff
|
| 44 |
+
builtin-foreground= #900090
|
| 45 |
+
builtin-background= #ffffff
|
| 46 |
+
comment-foreground= #dd0000
|
| 47 |
+
comment-background= #ffffff
|
| 48 |
+
string-foreground= #00aa00
|
| 49 |
+
string-background= #ffffff
|
| 50 |
+
definition-foreground= #0000ff
|
| 51 |
+
definition-background= #ffffff
|
| 52 |
+
hilite-foreground= #000000
|
| 53 |
+
hilite-background= gray
|
| 54 |
+
break-foreground= black
|
| 55 |
+
break-background= #ffff55
|
| 56 |
+
hit-foreground= #ffffff
|
| 57 |
+
hit-background= #000000
|
| 58 |
+
error-foreground= #000000
|
| 59 |
+
error-background= #ff7777
|
| 60 |
+
context-foreground= #000000
|
| 61 |
+
context-background= lightgray
|
| 62 |
+
linenumber-foreground= gray
|
| 63 |
+
linenumber-background= #ffffff
|
| 64 |
+
#cursor (only foreground can be set, restart IDLE)
|
| 65 |
+
cursor-foreground= black
|
| 66 |
+
#shell window
|
| 67 |
+
stdout-foreground= blue
|
| 68 |
+
stdout-background= #ffffff
|
| 69 |
+
stderr-foreground= red
|
| 70 |
+
stderr-background= #ffffff
|
| 71 |
+
console-foreground= #770000
|
| 72 |
+
console-background= #ffffff
|
| 73 |
+
|
| 74 |
+
[IDLE Dark]
|
| 75 |
+
comment-foreground = #dd0000
|
| 76 |
+
console-foreground = #ff4d4d
|
| 77 |
+
error-foreground = #FFFFFF
|
| 78 |
+
hilite-background = #7e7e7e
|
| 79 |
+
string-foreground = #02ff02
|
| 80 |
+
stderr-background = #002240
|
| 81 |
+
stderr-foreground = #ffb3b3
|
| 82 |
+
console-background = #002240
|
| 83 |
+
hit-background = #fbfbfb
|
| 84 |
+
string-background = #002240
|
| 85 |
+
normal-background = #002240
|
| 86 |
+
hilite-foreground = #FFFFFF
|
| 87 |
+
keyword-foreground = #ff8000
|
| 88 |
+
error-background = #c86464
|
| 89 |
+
keyword-background = #002240
|
| 90 |
+
builtin-background = #002240
|
| 91 |
+
break-background = #808000
|
| 92 |
+
builtin-foreground = #ff00ff
|
| 93 |
+
definition-foreground = #5e5eff
|
| 94 |
+
stdout-foreground = #c2d1fa
|
| 95 |
+
definition-background = #002240
|
| 96 |
+
normal-foreground = #FFFFFF
|
| 97 |
+
cursor-foreground = #ffffff
|
| 98 |
+
stdout-background = #002240
|
| 99 |
+
hit-foreground = #002240
|
| 100 |
+
comment-background = #002240
|
| 101 |
+
break-foreground = #FFFFFF
|
| 102 |
+
context-foreground= #ffffff
|
| 103 |
+
context-background= #454545
|
| 104 |
+
linenumber-foreground= gray
|
| 105 |
+
linenumber-background= #002240
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/config-keys.def
ADDED
|
@@ -0,0 +1,309 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# IDLE reads several config files to determine user preferences. This
|
| 2 |
+
# file is the default config file for idle key binding settings.
|
| 3 |
+
# Where multiple keys are specified for an action: if they are separated
|
| 4 |
+
# by a space (eg. action=<key1> <key2>) then the keys are alternatives, if
|
| 5 |
+
# there is no space (eg. action=<key1><key2>) then the keys comprise a
|
| 6 |
+
# single 'emacs style' multi-keystoke binding. The tk event specifier 'Key'
|
| 7 |
+
# is used in all cases, for consistency in auto key conflict checking in the
|
| 8 |
+
# configuration gui.
|
| 9 |
+
|
| 10 |
+
[IDLE Classic Windows]
|
| 11 |
+
copy=<Control-Key-c> <Control-Key-C>
|
| 12 |
+
cut=<Control-Key-x> <Control-Key-X>
|
| 13 |
+
paste=<Control-Key-v> <Control-Key-V>
|
| 14 |
+
beginning-of-line= <Key-Home>
|
| 15 |
+
center-insert=<Control-Key-l> <Control-Key-L>
|
| 16 |
+
close-all-windows=<Control-Key-q> <Control-Key-Q>
|
| 17 |
+
close-window=<Alt-Key-F4> <Meta-Key-F4>
|
| 18 |
+
do-nothing=<Control-Key-F12>
|
| 19 |
+
end-of-file=<Control-Key-d> <Control-Key-D>
|
| 20 |
+
python-docs=<Key-F1>
|
| 21 |
+
python-context-help=<Shift-Key-F1>
|
| 22 |
+
history-next=<Alt-Key-n> <Meta-Key-n> <Alt-Key-N> <Meta-Key-N>
|
| 23 |
+
history-previous=<Alt-Key-p> <Meta-Key-p> <Alt-Key-P> <Meta-Key-P>
|
| 24 |
+
interrupt-execution=<Control-Key-c> <Control-Key-C>
|
| 25 |
+
view-restart=<Key-F6>
|
| 26 |
+
restart-shell=<Control-Key-F6>
|
| 27 |
+
open-class-browser=<Alt-Key-c> <Meta-Key-c> <Alt-Key-C> <Meta-Key-C>
|
| 28 |
+
open-module=<Alt-Key-m> <Meta-Key-m> <Alt-Key-M> <Meta-Key-M>
|
| 29 |
+
open-new-window=<Control-Key-n> <Control-Key-N>
|
| 30 |
+
open-window-from-file=<Control-Key-o> <Control-Key-O>
|
| 31 |
+
plain-newline-and-indent=<Control-Key-j> <Control-Key-J>
|
| 32 |
+
print-window=<Control-Key-p> <Control-Key-P>
|
| 33 |
+
redo=<Control-Shift-Key-Z> <Control-Shift-Key-z>
|
| 34 |
+
remove-selection=<Key-Escape>
|
| 35 |
+
save-copy-of-window-as-file=<Alt-Shift-Key-S> <Alt-Shift-Key-s>
|
| 36 |
+
save-window-as-file=<Control-Shift-Key-S> <Control-Shift-Key-s>
|
| 37 |
+
save-window=<Control-Key-s> <Control-Key-S>
|
| 38 |
+
select-all=<Control-Key-a> <Control-Key-A>
|
| 39 |
+
toggle-auto-coloring=<Control-Key-slash>
|
| 40 |
+
undo=<Control-Key-z> <Control-Key-Z>
|
| 41 |
+
find=<Control-Key-f> <Control-Key-F>
|
| 42 |
+
find-again=<Control-Key-g> <Key-F3> <Control-Key-G>
|
| 43 |
+
find-in-files=<Alt-Key-F3> <Meta-Key-F3>
|
| 44 |
+
find-selection=<Control-Key-F3>
|
| 45 |
+
replace=<Control-Key-h> <Control-Key-H>
|
| 46 |
+
goto-line=<Alt-Key-g> <Meta-Key-g> <Alt-Key-G> <Meta-Key-G>
|
| 47 |
+
smart-backspace=<Key-BackSpace>
|
| 48 |
+
newline-and-indent=<Key-Return> <Key-KP_Enter>
|
| 49 |
+
smart-indent=<Key-Tab>
|
| 50 |
+
indent-region=<Control-Key-bracketright>
|
| 51 |
+
dedent-region=<Control-Key-bracketleft>
|
| 52 |
+
comment-region=<Alt-Key-3> <Meta-Key-3>
|
| 53 |
+
uncomment-region=<Alt-Key-4> <Meta-Key-4>
|
| 54 |
+
tabify-region=<Alt-Key-5> <Meta-Key-5>
|
| 55 |
+
untabify-region=<Alt-Key-6> <Meta-Key-6>
|
| 56 |
+
toggle-tabs=<Alt-Key-t> <Meta-Key-t> <Alt-Key-T> <Meta-Key-T>
|
| 57 |
+
change-indentwidth=<Alt-Key-u> <Meta-Key-u> <Alt-Key-U> <Meta-Key-U>
|
| 58 |
+
del-word-left=<Control-Key-BackSpace>
|
| 59 |
+
del-word-right=<Control-Key-Delete>
|
| 60 |
+
force-open-completions= <Control-Key-space>
|
| 61 |
+
expand-word= <Alt-Key-slash>
|
| 62 |
+
force-open-calltip= <Control-Key-backslash>
|
| 63 |
+
format-paragraph= <Alt-Key-q>
|
| 64 |
+
flash-paren= <Control-Key-0>
|
| 65 |
+
run-module= <Key-F5>
|
| 66 |
+
run-custom= <Shift-Key-F5>
|
| 67 |
+
check-module= <Alt-Key-x>
|
| 68 |
+
zoom-height= <Alt-Key-2>
|
| 69 |
+
|
| 70 |
+
[IDLE Classic Unix]
|
| 71 |
+
copy=<Alt-Key-w> <Meta-Key-w>
|
| 72 |
+
cut=<Control-Key-w>
|
| 73 |
+
paste=<Control-Key-y>
|
| 74 |
+
beginning-of-line=<Control-Key-a> <Key-Home>
|
| 75 |
+
center-insert=<Control-Key-l>
|
| 76 |
+
close-all-windows=<Control-Key-x><Control-Key-c>
|
| 77 |
+
close-window=<Control-Key-x><Control-Key-0>
|
| 78 |
+
do-nothing=<Control-Key-x>
|
| 79 |
+
end-of-file=<Control-Key-d>
|
| 80 |
+
history-next=<Alt-Key-n> <Meta-Key-n>
|
| 81 |
+
history-previous=<Alt-Key-p> <Meta-Key-p>
|
| 82 |
+
interrupt-execution=<Control-Key-c>
|
| 83 |
+
view-restart=<Key-F6>
|
| 84 |
+
restart-shell=<Control-Key-F6>
|
| 85 |
+
open-class-browser=<Control-Key-x><Control-Key-b>
|
| 86 |
+
open-module=<Control-Key-x><Control-Key-m>
|
| 87 |
+
open-new-window=<Control-Key-x><Control-Key-n>
|
| 88 |
+
open-window-from-file=<Control-Key-x><Control-Key-f>
|
| 89 |
+
plain-newline-and-indent=<Control-Key-j>
|
| 90 |
+
print-window=<Control-x><Control-Key-p>
|
| 91 |
+
python-docs=<Control-Key-h>
|
| 92 |
+
python-context-help=<Control-Shift-Key-H>
|
| 93 |
+
redo=<Alt-Key-z> <Meta-Key-z>
|
| 94 |
+
remove-selection=<Key-Escape>
|
| 95 |
+
save-copy-of-window-as-file=<Control-Key-x><Control-Key-y>
|
| 96 |
+
save-window-as-file=<Control-Key-x><Control-Key-w>
|
| 97 |
+
save-window=<Control-Key-x><Control-Key-s>
|
| 98 |
+
select-all=<Alt-Key-a> <Meta-Key-a>
|
| 99 |
+
toggle-auto-coloring=<Control-Key-slash>
|
| 100 |
+
undo=<Control-Key-z>
|
| 101 |
+
find=<Control-Key-u><Control-Key-u><Control-Key-s>
|
| 102 |
+
find-again=<Control-Key-u><Control-Key-s>
|
| 103 |
+
find-in-files=<Alt-Key-s> <Meta-Key-s>
|
| 104 |
+
find-selection=<Control-Key-s>
|
| 105 |
+
replace=<Control-Key-r>
|
| 106 |
+
goto-line=<Alt-Key-g> <Meta-Key-g>
|
| 107 |
+
smart-backspace=<Key-BackSpace>
|
| 108 |
+
newline-and-indent=<Key-Return> <Key-KP_Enter>
|
| 109 |
+
smart-indent=<Key-Tab>
|
| 110 |
+
indent-region=<Control-Key-bracketright>
|
| 111 |
+
dedent-region=<Control-Key-bracketleft>
|
| 112 |
+
comment-region=<Alt-Key-3>
|
| 113 |
+
uncomment-region=<Alt-Key-4>
|
| 114 |
+
tabify-region=<Alt-Key-5>
|
| 115 |
+
untabify-region=<Alt-Key-6>
|
| 116 |
+
toggle-tabs=<Alt-Key-t>
|
| 117 |
+
change-indentwidth=<Alt-Key-u>
|
| 118 |
+
del-word-left=<Alt-Key-BackSpace>
|
| 119 |
+
del-word-right=<Alt-Key-d>
|
| 120 |
+
force-open-completions= <Control-Key-space>
|
| 121 |
+
expand-word= <Alt-Key-slash>
|
| 122 |
+
force-open-calltip= <Control-Key-backslash>
|
| 123 |
+
format-paragraph= <Alt-Key-q>
|
| 124 |
+
flash-paren= <Control-Key-0>
|
| 125 |
+
run-module= <Key-F5>
|
| 126 |
+
run-custom= <Shift-Key-F5>
|
| 127 |
+
check-module= <Alt-Key-x>
|
| 128 |
+
zoom-height= <Alt-Key-2>
|
| 129 |
+
|
| 130 |
+
[IDLE Modern Unix]
|
| 131 |
+
copy = <Control-Shift-Key-C> <Control-Key-Insert>
|
| 132 |
+
cut = <Control-Key-x> <Shift-Key-Delete>
|
| 133 |
+
paste = <Control-Key-v> <Shift-Key-Insert>
|
| 134 |
+
beginning-of-line = <Key-Home>
|
| 135 |
+
center-insert = <Control-Key-l>
|
| 136 |
+
close-all-windows = <Control-Key-q>
|
| 137 |
+
close-window = <Control-Key-w> <Control-Shift-Key-W>
|
| 138 |
+
do-nothing = <Control-Key-F12>
|
| 139 |
+
end-of-file = <Control-Key-d>
|
| 140 |
+
history-next = <Alt-Key-n> <Meta-Key-n>
|
| 141 |
+
history-previous = <Alt-Key-p> <Meta-Key-p>
|
| 142 |
+
interrupt-execution = <Control-Key-c>
|
| 143 |
+
view-restart = <Key-F6>
|
| 144 |
+
restart-shell = <Control-Key-F6>
|
| 145 |
+
open-class-browser = <Control-Key-b>
|
| 146 |
+
open-module = <Control-Key-m>
|
| 147 |
+
open-new-window = <Control-Key-n>
|
| 148 |
+
open-window-from-file = <Control-Key-o>
|
| 149 |
+
plain-newline-and-indent = <Control-Key-j>
|
| 150 |
+
print-window = <Control-Key-p>
|
| 151 |
+
python-context-help = <Shift-Key-F1>
|
| 152 |
+
python-docs = <Key-F1>
|
| 153 |
+
redo = <Control-Shift-Key-Z>
|
| 154 |
+
remove-selection = <Key-Escape>
|
| 155 |
+
save-copy-of-window-as-file = <Alt-Shift-Key-S>
|
| 156 |
+
save-window-as-file = <Control-Shift-Key-S>
|
| 157 |
+
save-window = <Control-Key-s>
|
| 158 |
+
select-all = <Control-Key-a>
|
| 159 |
+
toggle-auto-coloring = <Control-Key-slash>
|
| 160 |
+
undo = <Control-Key-z>
|
| 161 |
+
find = <Control-Key-f>
|
| 162 |
+
find-again = <Key-F3>
|
| 163 |
+
find-in-files = <Control-Shift-Key-f>
|
| 164 |
+
find-selection = <Control-Key-h>
|
| 165 |
+
replace = <Control-Key-r>
|
| 166 |
+
goto-line = <Control-Key-g>
|
| 167 |
+
smart-backspace = <Key-BackSpace>
|
| 168 |
+
newline-and-indent = <Key-Return> <Key-KP_Enter>
|
| 169 |
+
smart-indent = <Key-Tab>
|
| 170 |
+
indent-region = <Control-Key-bracketright>
|
| 171 |
+
dedent-region = <Control-Key-bracketleft>
|
| 172 |
+
comment-region = <Control-Key-d>
|
| 173 |
+
uncomment-region = <Control-Shift-Key-D>
|
| 174 |
+
tabify-region = <Alt-Key-5>
|
| 175 |
+
untabify-region = <Alt-Key-6>
|
| 176 |
+
toggle-tabs = <Control-Key-T>
|
| 177 |
+
change-indentwidth = <Alt-Key-u>
|
| 178 |
+
del-word-left = <Control-Key-BackSpace>
|
| 179 |
+
del-word-right = <Control-Key-Delete>
|
| 180 |
+
force-open-completions= <Control-Key-space>
|
| 181 |
+
expand-word= <Alt-Key-slash>
|
| 182 |
+
force-open-calltip= <Control-Key-backslash>
|
| 183 |
+
format-paragraph= <Alt-Key-q>
|
| 184 |
+
flash-paren= <Control-Key-0>
|
| 185 |
+
run-module= <Key-F5>
|
| 186 |
+
run-custom= <Shift-Key-F5>
|
| 187 |
+
check-module= <Alt-Key-x>
|
| 188 |
+
zoom-height= <Alt-Key-2>
|
| 189 |
+
|
| 190 |
+
[IDLE Classic Mac]
|
| 191 |
+
copy=<Command-Key-c>
|
| 192 |
+
cut=<Command-Key-x>
|
| 193 |
+
paste=<Command-Key-v>
|
| 194 |
+
beginning-of-line= <Key-Home>
|
| 195 |
+
center-insert=<Control-Key-l>
|
| 196 |
+
close-all-windows=<Command-Key-q>
|
| 197 |
+
close-window=<Command-Key-w>
|
| 198 |
+
do-nothing=<Control-Key-F12>
|
| 199 |
+
end-of-file=<Control-Key-d>
|
| 200 |
+
python-docs=<Key-F1>
|
| 201 |
+
python-context-help=<Shift-Key-F1>
|
| 202 |
+
history-next=<Control-Key-n>
|
| 203 |
+
history-previous=<Control-Key-p>
|
| 204 |
+
interrupt-execution=<Control-Key-c>
|
| 205 |
+
view-restart=<Key-F6>
|
| 206 |
+
restart-shell=<Control-Key-F6>
|
| 207 |
+
open-class-browser=<Command-Key-b>
|
| 208 |
+
open-module=<Command-Key-m>
|
| 209 |
+
open-new-window=<Command-Key-n>
|
| 210 |
+
open-window-from-file=<Command-Key-o>
|
| 211 |
+
plain-newline-and-indent=<Control-Key-j>
|
| 212 |
+
print-window=<Command-Key-p>
|
| 213 |
+
redo=<Shift-Command-Key-Z>
|
| 214 |
+
remove-selection=<Key-Escape>
|
| 215 |
+
save-window-as-file=<Shift-Command-Key-S>
|
| 216 |
+
save-window=<Command-Key-s>
|
| 217 |
+
save-copy-of-window-as-file=<Option-Command-Key-s>
|
| 218 |
+
select-all=<Command-Key-a>
|
| 219 |
+
toggle-auto-coloring=<Control-Key-slash>
|
| 220 |
+
undo=<Command-Key-z>
|
| 221 |
+
find=<Command-Key-f>
|
| 222 |
+
find-again=<Command-Key-g> <Key-F3>
|
| 223 |
+
find-in-files=<Command-Key-F3>
|
| 224 |
+
find-selection=<Shift-Command-Key-F3>
|
| 225 |
+
replace=<Command-Key-r>
|
| 226 |
+
goto-line=<Command-Key-j>
|
| 227 |
+
smart-backspace=<Key-BackSpace>
|
| 228 |
+
newline-and-indent=<Key-Return> <Key-KP_Enter>
|
| 229 |
+
smart-indent=<Key-Tab>
|
| 230 |
+
indent-region=<Command-Key-bracketright>
|
| 231 |
+
dedent-region=<Command-Key-bracketleft>
|
| 232 |
+
comment-region=<Control-Key-3>
|
| 233 |
+
uncomment-region=<Control-Key-4>
|
| 234 |
+
tabify-region=<Control-Key-5>
|
| 235 |
+
untabify-region=<Control-Key-6>
|
| 236 |
+
toggle-tabs=<Control-Key-t>
|
| 237 |
+
change-indentwidth=<Control-Key-u>
|
| 238 |
+
del-word-left=<Control-Key-BackSpace>
|
| 239 |
+
del-word-right=<Control-Key-Delete>
|
| 240 |
+
force-open-completions= <Control-Key-space>
|
| 241 |
+
expand-word= <Option-Key-slash>
|
| 242 |
+
force-open-calltip= <Control-Key-backslash>
|
| 243 |
+
format-paragraph= <Option-Key-q>
|
| 244 |
+
flash-paren= <Control-Key-0>
|
| 245 |
+
run-module= <Key-F5>
|
| 246 |
+
run-custom= <Shift-Key-F5>
|
| 247 |
+
check-module= <Option-Key-x>
|
| 248 |
+
zoom-height= <Option-Key-0>
|
| 249 |
+
|
| 250 |
+
[IDLE Classic OSX]
|
| 251 |
+
toggle-tabs = <Control-Key-t>
|
| 252 |
+
interrupt-execution = <Control-Key-c>
|
| 253 |
+
untabify-region = <Control-Key-6>
|
| 254 |
+
remove-selection = <Key-Escape>
|
| 255 |
+
print-window = <Command-Key-p>
|
| 256 |
+
replace = <Command-Key-r>
|
| 257 |
+
goto-line = <Command-Key-j>
|
| 258 |
+
plain-newline-and-indent = <Control-Key-j>
|
| 259 |
+
history-previous = <Control-Key-p>
|
| 260 |
+
beginning-of-line = <Control-Key-Left>
|
| 261 |
+
end-of-line = <Control-Key-Right>
|
| 262 |
+
comment-region = <Control-Key-3>
|
| 263 |
+
redo = <Shift-Command-Key-Z>
|
| 264 |
+
close-window = <Command-Key-w>
|
| 265 |
+
restart-shell = <Control-Key-F6>
|
| 266 |
+
save-window-as-file = <Shift-Command-Key-S>
|
| 267 |
+
close-all-windows = <Command-Key-q>
|
| 268 |
+
view-restart = <Key-F6>
|
| 269 |
+
tabify-region = <Control-Key-5>
|
| 270 |
+
find-again = <Command-Key-g> <Key-F3>
|
| 271 |
+
find = <Command-Key-f>
|
| 272 |
+
toggle-auto-coloring = <Control-Key-slash>
|
| 273 |
+
select-all = <Command-Key-a>
|
| 274 |
+
smart-backspace = <Key-BackSpace>
|
| 275 |
+
change-indentwidth = <Control-Key-u>
|
| 276 |
+
do-nothing = <Control-Key-F12>
|
| 277 |
+
smart-indent = <Key-Tab>
|
| 278 |
+
center-insert = <Control-Key-l>
|
| 279 |
+
history-next = <Control-Key-n>
|
| 280 |
+
del-word-right = <Option-Key-Delete>
|
| 281 |
+
undo = <Command-Key-z>
|
| 282 |
+
save-window = <Command-Key-s>
|
| 283 |
+
uncomment-region = <Control-Key-4>
|
| 284 |
+
cut = <Command-Key-x>
|
| 285 |
+
find-in-files = <Command-Key-F3>
|
| 286 |
+
dedent-region = <Command-Key-bracketleft>
|
| 287 |
+
copy = <Command-Key-c>
|
| 288 |
+
paste = <Command-Key-v>
|
| 289 |
+
indent-region = <Command-Key-bracketright>
|
| 290 |
+
del-word-left = <Option-Key-BackSpace> <Option-Command-Key-BackSpace>
|
| 291 |
+
newline-and-indent = <Key-Return> <Key-KP_Enter>
|
| 292 |
+
end-of-file = <Control-Key-d>
|
| 293 |
+
open-class-browser = <Command-Key-b>
|
| 294 |
+
open-new-window = <Command-Key-n>
|
| 295 |
+
open-module = <Command-Key-m>
|
| 296 |
+
find-selection = <Shift-Command-Key-F3>
|
| 297 |
+
python-context-help = <Shift-Key-F1>
|
| 298 |
+
save-copy-of-window-as-file = <Option-Command-Key-s>
|
| 299 |
+
open-window-from-file = <Command-Key-o>
|
| 300 |
+
python-docs = <Key-F1>
|
| 301 |
+
force-open-completions= <Control-Key-space>
|
| 302 |
+
expand-word= <Option-Key-slash>
|
| 303 |
+
force-open-calltip= <Control-Key-backslash>
|
| 304 |
+
format-paragraph= <Option-Key-q>
|
| 305 |
+
flash-paren= <Control-Key-0>
|
| 306 |
+
run-module= <Key-F5>
|
| 307 |
+
run-custom= <Shift-Key-F5>
|
| 308 |
+
check-module= <Option-Key-x>
|
| 309 |
+
zoom-height= <Option-Key-0>
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/config-main.def
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# IDLE reads several config files to determine user preferences. This
|
| 2 |
+
# file is the default config file for general idle settings.
|
| 3 |
+
#
|
| 4 |
+
# When IDLE starts, it will look in
|
| 5 |
+
# the following two sets of files, in order:
|
| 6 |
+
#
|
| 7 |
+
# default configuration files in idlelib
|
| 8 |
+
# --------------------------------------
|
| 9 |
+
# config-main.def default general config file
|
| 10 |
+
# config-extensions.def default extension config file
|
| 11 |
+
# config-highlight.def default highlighting config file
|
| 12 |
+
# config-keys.def default keybinding config file
|
| 13 |
+
#
|
| 14 |
+
# user configuration files in ~/.idlerc
|
| 15 |
+
# -------------------------------------
|
| 16 |
+
# config-main.cfg user general config file
|
| 17 |
+
# config-extensions.cfg user extension config file
|
| 18 |
+
# config-highlight.cfg user highlighting config file
|
| 19 |
+
# config-keys.cfg user keybinding config file
|
| 20 |
+
#
|
| 21 |
+
# On Windows, the default location of the home directory ('~' above)
|
| 22 |
+
# depends on the version. For Windows 10, it is C:\Users\<username>.
|
| 23 |
+
#
|
| 24 |
+
# Any options the user saves through the config dialog will be saved to
|
| 25 |
+
# the relevant user config file. Reverting any general or extension
|
| 26 |
+
# setting to the default causes that entry to be wiped from the user
|
| 27 |
+
# file and re-read from the default file. This rule applies to each
|
| 28 |
+
# item, except that the three editor font items are saved as a group.
|
| 29 |
+
#
|
| 30 |
+
# User highlighting themes and keybinding sets must have (section) names
|
| 31 |
+
# distinct from the default names. All items are added and saved as a
|
| 32 |
+
# group. They are retained unless specifically deleted within the config
|
| 33 |
+
# dialog. Choosing one of the default themes or keysets just applies the
|
| 34 |
+
# relevant settings from the default file.
|
| 35 |
+
#
|
| 36 |
+
# Additional help sources are listed in the [HelpFiles] section below
|
| 37 |
+
# and should be viewable by a web browser (or the Windows Help viewer in
|
| 38 |
+
# the case of .chm files). These sources will be listed on the Help
|
| 39 |
+
# menu. The pattern, and two examples, are:
|
| 40 |
+
#
|
| 41 |
+
# <sequence_number = menu item;/path/to/help/source>
|
| 42 |
+
# 1 = IDLE;C:/Programs/Python36/Lib/idlelib/help.html
|
| 43 |
+
# 2 = Pillow;https://pillow.readthedocs.io/en/latest/
|
| 44 |
+
#
|
| 45 |
+
# You can't use a semi-colon in a menu item or path. The path will be
|
| 46 |
+
# platform specific because of path separators, drive specs etc.
|
| 47 |
+
#
|
| 48 |
+
# The default files should not be edited except to add new sections to
|
| 49 |
+
# config-extensions.def for added extensions. The user files should be
|
| 50 |
+
# modified through the Settings dialog.
|
| 51 |
+
|
| 52 |
+
[General]
|
| 53 |
+
editor-on-startup= 0
|
| 54 |
+
autosave= 0
|
| 55 |
+
print-command-posix=lpr %%s
|
| 56 |
+
print-command-win=start /min notepad /p %%s
|
| 57 |
+
delete-exitfunc= 1
|
| 58 |
+
|
| 59 |
+
[EditorWindow]
|
| 60 |
+
width= 80
|
| 61 |
+
height= 40
|
| 62 |
+
cursor-blink= 1
|
| 63 |
+
font= TkFixedFont
|
| 64 |
+
# For TkFixedFont, the actual size and boldness are obtained from tk
|
| 65 |
+
# and override 10 and 0. See idlelib.config.IdleConf.GetFont
|
| 66 |
+
font-size= 10
|
| 67 |
+
font-bold= 0
|
| 68 |
+
encoding= none
|
| 69 |
+
line-numbers-default= 0
|
| 70 |
+
|
| 71 |
+
[PyShell]
|
| 72 |
+
auto-squeeze-min-lines= 50
|
| 73 |
+
|
| 74 |
+
[Indent]
|
| 75 |
+
use-spaces= 1
|
| 76 |
+
num-spaces= 4
|
| 77 |
+
|
| 78 |
+
[Theme]
|
| 79 |
+
default= 1
|
| 80 |
+
name= IDLE Classic
|
| 81 |
+
name2=
|
| 82 |
+
# name2 set in user config-main.cfg for themes added after 2015 Oct 1
|
| 83 |
+
|
| 84 |
+
[Keys]
|
| 85 |
+
default= 1
|
| 86 |
+
name=
|
| 87 |
+
name2=
|
| 88 |
+
# name2 set in user config-main.cfg for keys added after 2016 July 1
|
| 89 |
+
|
| 90 |
+
[History]
|
| 91 |
+
cyclic=1
|
| 92 |
+
|
| 93 |
+
[HelpFiles]
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/config.py
ADDED
|
@@ -0,0 +1,911 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""idlelib.config -- Manage IDLE configuration information.
|
| 2 |
+
|
| 3 |
+
The comments at the beginning of config-main.def describe the
|
| 4 |
+
configuration files and the design implemented to update user
|
| 5 |
+
configuration information. In particular, user configuration choices
|
| 6 |
+
which duplicate the defaults will be removed from the user's
|
| 7 |
+
configuration files, and if a user file becomes empty, it will be
|
| 8 |
+
deleted.
|
| 9 |
+
|
| 10 |
+
The configuration database maps options to values. Conceptually, the
|
| 11 |
+
database keys are tuples (config-type, section, item). As implemented,
|
| 12 |
+
there are separate dicts for default and user values. Each has
|
| 13 |
+
config-type keys 'main', 'extensions', 'highlight', and 'keys'. The
|
| 14 |
+
value for each key is a ConfigParser instance that maps section and item
|
| 15 |
+
to values. For 'main' and 'extensions', user values override
|
| 16 |
+
default values. For 'highlight' and 'keys', user sections augment the
|
| 17 |
+
default sections (and must, therefore, have distinct names).
|
| 18 |
+
|
| 19 |
+
Throughout this module there is an emphasis on returning useable defaults
|
| 20 |
+
when a problem occurs in returning a requested configuration value back to
|
| 21 |
+
idle. This is to allow IDLE to continue to function in spite of errors in
|
| 22 |
+
the retrieval of config information. When a default is returned instead of
|
| 23 |
+
a requested config value, a message is printed to stderr to aid in
|
| 24 |
+
configuration problem notification and resolution.
|
| 25 |
+
"""
|
| 26 |
+
# TODOs added Oct 2014, tjr
|
| 27 |
+
|
| 28 |
+
from configparser import ConfigParser
|
| 29 |
+
import os
|
| 30 |
+
import sys
|
| 31 |
+
|
| 32 |
+
from tkinter.font import Font
|
| 33 |
+
import idlelib
|
| 34 |
+
|
| 35 |
+
class InvalidConfigType(Exception): pass
|
| 36 |
+
class InvalidConfigSet(Exception): pass
|
| 37 |
+
class InvalidTheme(Exception): pass
|
| 38 |
+
|
| 39 |
+
class IdleConfParser(ConfigParser):
|
| 40 |
+
"""
|
| 41 |
+
A ConfigParser specialised for idle configuration file handling
|
| 42 |
+
"""
|
| 43 |
+
def __init__(self, cfgFile, cfgDefaults=None):
|
| 44 |
+
"""
|
| 45 |
+
cfgFile - string, fully specified configuration file name
|
| 46 |
+
"""
|
| 47 |
+
self.file = cfgFile # This is currently '' when testing.
|
| 48 |
+
ConfigParser.__init__(self, defaults=cfgDefaults, strict=False)
|
| 49 |
+
|
| 50 |
+
def Get(self, section, option, type=None, default=None, raw=False):
|
| 51 |
+
"""
|
| 52 |
+
Get an option value for given section/option or return default.
|
| 53 |
+
If type is specified, return as type.
|
| 54 |
+
"""
|
| 55 |
+
# TODO Use default as fallback, at least if not None
|
| 56 |
+
# Should also print Warning(file, section, option).
|
| 57 |
+
# Currently may raise ValueError
|
| 58 |
+
if not self.has_option(section, option):
|
| 59 |
+
return default
|
| 60 |
+
if type == 'bool':
|
| 61 |
+
return self.getboolean(section, option)
|
| 62 |
+
elif type == 'int':
|
| 63 |
+
return self.getint(section, option)
|
| 64 |
+
else:
|
| 65 |
+
return self.get(section, option, raw=raw)
|
| 66 |
+
|
| 67 |
+
def GetOptionList(self, section):
|
| 68 |
+
"Return a list of options for given section, else []."
|
| 69 |
+
if self.has_section(section):
|
| 70 |
+
return self.options(section)
|
| 71 |
+
else: #return a default value
|
| 72 |
+
return []
|
| 73 |
+
|
| 74 |
+
def Load(self):
|
| 75 |
+
"Load the configuration file from disk."
|
| 76 |
+
if self.file:
|
| 77 |
+
self.read(self.file)
|
| 78 |
+
|
| 79 |
+
class IdleUserConfParser(IdleConfParser):
|
| 80 |
+
"""
|
| 81 |
+
IdleConfigParser specialised for user configuration handling.
|
| 82 |
+
"""
|
| 83 |
+
|
| 84 |
+
def SetOption(self, section, option, value):
|
| 85 |
+
"""Return True if option is added or changed to value, else False.
|
| 86 |
+
|
| 87 |
+
Add section if required. False means option already had value.
|
| 88 |
+
"""
|
| 89 |
+
if self.has_option(section, option):
|
| 90 |
+
if self.get(section, option) == value:
|
| 91 |
+
return False
|
| 92 |
+
else:
|
| 93 |
+
self.set(section, option, value)
|
| 94 |
+
return True
|
| 95 |
+
else:
|
| 96 |
+
if not self.has_section(section):
|
| 97 |
+
self.add_section(section)
|
| 98 |
+
self.set(section, option, value)
|
| 99 |
+
return True
|
| 100 |
+
|
| 101 |
+
def RemoveOption(self, section, option):
|
| 102 |
+
"""Return True if option is removed from section, else False.
|
| 103 |
+
|
| 104 |
+
False if either section does not exist or did not have option.
|
| 105 |
+
"""
|
| 106 |
+
if self.has_section(section):
|
| 107 |
+
return self.remove_option(section, option)
|
| 108 |
+
return False
|
| 109 |
+
|
| 110 |
+
def AddSection(self, section):
|
| 111 |
+
"If section doesn't exist, add it."
|
| 112 |
+
if not self.has_section(section):
|
| 113 |
+
self.add_section(section)
|
| 114 |
+
|
| 115 |
+
def RemoveEmptySections(self):
|
| 116 |
+
"Remove any sections that have no options."
|
| 117 |
+
for section in self.sections():
|
| 118 |
+
if not self.GetOptionList(section):
|
| 119 |
+
self.remove_section(section)
|
| 120 |
+
|
| 121 |
+
def IsEmpty(self):
|
| 122 |
+
"Return True if no sections after removing empty sections."
|
| 123 |
+
self.RemoveEmptySections()
|
| 124 |
+
return not self.sections()
|
| 125 |
+
|
| 126 |
+
def Save(self):
|
| 127 |
+
"""Update user configuration file.
|
| 128 |
+
|
| 129 |
+
If self not empty after removing empty sections, write the file
|
| 130 |
+
to disk. Otherwise, remove the file from disk if it exists.
|
| 131 |
+
"""
|
| 132 |
+
fname = self.file
|
| 133 |
+
if fname and fname[0] != '#':
|
| 134 |
+
if not self.IsEmpty():
|
| 135 |
+
try:
|
| 136 |
+
cfgFile = open(fname, 'w')
|
| 137 |
+
except OSError:
|
| 138 |
+
os.unlink(fname)
|
| 139 |
+
cfgFile = open(fname, 'w')
|
| 140 |
+
with cfgFile:
|
| 141 |
+
self.write(cfgFile)
|
| 142 |
+
elif os.path.exists(self.file):
|
| 143 |
+
os.remove(self.file)
|
| 144 |
+
|
| 145 |
+
class IdleConf:
|
| 146 |
+
"""Hold config parsers for all idle config files in singleton instance.
|
| 147 |
+
|
| 148 |
+
Default config files, self.defaultCfg --
|
| 149 |
+
for config_type in self.config_types:
|
| 150 |
+
(idle install dir)/config-{config-type}.def
|
| 151 |
+
|
| 152 |
+
User config files, self.userCfg --
|
| 153 |
+
for config_type in self.config_types:
|
| 154 |
+
(user home dir)/.idlerc/config-{config-type}.cfg
|
| 155 |
+
"""
|
| 156 |
+
def __init__(self, _utest=False):
|
| 157 |
+
self.config_types = ('main', 'highlight', 'keys', 'extensions')
|
| 158 |
+
self.defaultCfg = {}
|
| 159 |
+
self.userCfg = {}
|
| 160 |
+
self.cfg = {} # TODO use to select userCfg vs defaultCfg
|
| 161 |
+
# self.blink_off_time = <first editor text>['insertofftime']
|
| 162 |
+
# See https:/bugs.python.org/issue4630, msg356516.
|
| 163 |
+
|
| 164 |
+
if not _utest:
|
| 165 |
+
self.CreateConfigHandlers()
|
| 166 |
+
self.LoadCfgFiles()
|
| 167 |
+
|
| 168 |
+
def CreateConfigHandlers(self):
|
| 169 |
+
"Populate default and user config parser dictionaries."
|
| 170 |
+
idledir = os.path.dirname(__file__)
|
| 171 |
+
self.userdir = userdir = '' if idlelib.testing else self.GetUserCfgDir()
|
| 172 |
+
for cfg_type in self.config_types:
|
| 173 |
+
self.defaultCfg[cfg_type] = IdleConfParser(
|
| 174 |
+
os.path.join(idledir, f'config-{cfg_type}.def'))
|
| 175 |
+
self.userCfg[cfg_type] = IdleUserConfParser(
|
| 176 |
+
os.path.join(userdir or '#', f'config-{cfg_type}.cfg'))
|
| 177 |
+
|
| 178 |
+
def GetUserCfgDir(self):
|
| 179 |
+
"""Return a filesystem directory for storing user config files.
|
| 180 |
+
|
| 181 |
+
Creates it if required.
|
| 182 |
+
"""
|
| 183 |
+
cfgDir = '.idlerc'
|
| 184 |
+
userDir = os.path.expanduser('~')
|
| 185 |
+
if userDir != '~': # expanduser() found user home dir
|
| 186 |
+
if not os.path.exists(userDir):
|
| 187 |
+
if not idlelib.testing:
|
| 188 |
+
warn = ('\n Warning: os.path.expanduser("~") points to\n ' +
|
| 189 |
+
userDir + ',\n but the path does not exist.')
|
| 190 |
+
try:
|
| 191 |
+
print(warn, file=sys.stderr)
|
| 192 |
+
except OSError:
|
| 193 |
+
pass
|
| 194 |
+
userDir = '~'
|
| 195 |
+
if userDir == "~": # still no path to home!
|
| 196 |
+
# traditionally IDLE has defaulted to os.getcwd(), is this adequate?
|
| 197 |
+
userDir = os.getcwd()
|
| 198 |
+
userDir = os.path.join(userDir, cfgDir)
|
| 199 |
+
if not os.path.exists(userDir):
|
| 200 |
+
try:
|
| 201 |
+
os.mkdir(userDir)
|
| 202 |
+
except OSError:
|
| 203 |
+
if not idlelib.testing:
|
| 204 |
+
warn = ('\n Warning: unable to create user config directory\n' +
|
| 205 |
+
userDir + '\n Check path and permissions.\n Exiting!\n')
|
| 206 |
+
try:
|
| 207 |
+
print(warn, file=sys.stderr)
|
| 208 |
+
except OSError:
|
| 209 |
+
pass
|
| 210 |
+
raise SystemExit
|
| 211 |
+
# TODO continue without userDIr instead of exit
|
| 212 |
+
return userDir
|
| 213 |
+
|
| 214 |
+
def GetOption(self, configType, section, option, default=None, type=None,
|
| 215 |
+
warn_on_default=True, raw=False):
|
| 216 |
+
"""Return a value for configType section option, or default.
|
| 217 |
+
|
| 218 |
+
If type is not None, return a value of that type. Also pass raw
|
| 219 |
+
to the config parser. First try to return a valid value
|
| 220 |
+
(including type) from a user configuration. If that fails, try
|
| 221 |
+
the default configuration. If that fails, return default, with a
|
| 222 |
+
default of None.
|
| 223 |
+
|
| 224 |
+
Warn if either user or default configurations have an invalid value.
|
| 225 |
+
Warn if default is returned and warn_on_default is True.
|
| 226 |
+
"""
|
| 227 |
+
try:
|
| 228 |
+
if self.userCfg[configType].has_option(section, option):
|
| 229 |
+
return self.userCfg[configType].Get(section, option,
|
| 230 |
+
type=type, raw=raw)
|
| 231 |
+
except ValueError:
|
| 232 |
+
warning = ('\n Warning: config.py - IdleConf.GetOption -\n'
|
| 233 |
+
' invalid %r value for configuration option %r\n'
|
| 234 |
+
' from section %r: %r' %
|
| 235 |
+
(type, option, section,
|
| 236 |
+
self.userCfg[configType].Get(section, option, raw=raw)))
|
| 237 |
+
_warn(warning, configType, section, option)
|
| 238 |
+
try:
|
| 239 |
+
if self.defaultCfg[configType].has_option(section,option):
|
| 240 |
+
return self.defaultCfg[configType].Get(
|
| 241 |
+
section, option, type=type, raw=raw)
|
| 242 |
+
except ValueError:
|
| 243 |
+
pass
|
| 244 |
+
#returning default, print warning
|
| 245 |
+
if warn_on_default:
|
| 246 |
+
warning = ('\n Warning: config.py - IdleConf.GetOption -\n'
|
| 247 |
+
' problem retrieving configuration option %r\n'
|
| 248 |
+
' from section %r.\n'
|
| 249 |
+
' returning default value: %r' %
|
| 250 |
+
(option, section, default))
|
| 251 |
+
_warn(warning, configType, section, option)
|
| 252 |
+
return default
|
| 253 |
+
|
| 254 |
+
def SetOption(self, configType, section, option, value):
|
| 255 |
+
"""Set section option to value in user config file."""
|
| 256 |
+
self.userCfg[configType].SetOption(section, option, value)
|
| 257 |
+
|
| 258 |
+
def GetSectionList(self, configSet, configType):
|
| 259 |
+
"""Return sections for configSet configType configuration.
|
| 260 |
+
|
| 261 |
+
configSet must be either 'user' or 'default'
|
| 262 |
+
configType must be in self.config_types.
|
| 263 |
+
"""
|
| 264 |
+
if not (configType in self.config_types):
|
| 265 |
+
raise InvalidConfigType('Invalid configType specified')
|
| 266 |
+
if configSet == 'user':
|
| 267 |
+
cfgParser = self.userCfg[configType]
|
| 268 |
+
elif configSet == 'default':
|
| 269 |
+
cfgParser=self.defaultCfg[configType]
|
| 270 |
+
else:
|
| 271 |
+
raise InvalidConfigSet('Invalid configSet specified')
|
| 272 |
+
return cfgParser.sections()
|
| 273 |
+
|
| 274 |
+
def GetHighlight(self, theme, element):
|
| 275 |
+
"""Return dict of theme element highlight colors.
|
| 276 |
+
|
| 277 |
+
The keys are 'foreground' and 'background'. The values are
|
| 278 |
+
tkinter color strings for configuring backgrounds and tags.
|
| 279 |
+
"""
|
| 280 |
+
cfg = ('default' if self.defaultCfg['highlight'].has_section(theme)
|
| 281 |
+
else 'user')
|
| 282 |
+
theme_dict = self.GetThemeDict(cfg, theme)
|
| 283 |
+
fore = theme_dict[element + '-foreground']
|
| 284 |
+
if element == 'cursor':
|
| 285 |
+
element = 'normal'
|
| 286 |
+
back = theme_dict[element + '-background']
|
| 287 |
+
return {"foreground": fore, "background": back}
|
| 288 |
+
|
| 289 |
+
def GetThemeDict(self, type, themeName):
|
| 290 |
+
"""Return {option:value} dict for elements in themeName.
|
| 291 |
+
|
| 292 |
+
type - string, 'default' or 'user' theme type
|
| 293 |
+
themeName - string, theme name
|
| 294 |
+
Values are loaded over ultimate fallback defaults to guarantee
|
| 295 |
+
that all theme elements are present in a newly created theme.
|
| 296 |
+
"""
|
| 297 |
+
if type == 'user':
|
| 298 |
+
cfgParser = self.userCfg['highlight']
|
| 299 |
+
elif type == 'default':
|
| 300 |
+
cfgParser = self.defaultCfg['highlight']
|
| 301 |
+
else:
|
| 302 |
+
raise InvalidTheme('Invalid theme type specified')
|
| 303 |
+
# Provide foreground and background colors for each theme
|
| 304 |
+
# element (other than cursor) even though some values are not
|
| 305 |
+
# yet used by idle, to allow for their use in the future.
|
| 306 |
+
# Default values are generally black and white.
|
| 307 |
+
# TODO copy theme from a class attribute.
|
| 308 |
+
theme ={'normal-foreground':'#000000',
|
| 309 |
+
'normal-background':'#ffffff',
|
| 310 |
+
'keyword-foreground':'#000000',
|
| 311 |
+
'keyword-background':'#ffffff',
|
| 312 |
+
'builtin-foreground':'#000000',
|
| 313 |
+
'builtin-background':'#ffffff',
|
| 314 |
+
'comment-foreground':'#000000',
|
| 315 |
+
'comment-background':'#ffffff',
|
| 316 |
+
'string-foreground':'#000000',
|
| 317 |
+
'string-background':'#ffffff',
|
| 318 |
+
'definition-foreground':'#000000',
|
| 319 |
+
'definition-background':'#ffffff',
|
| 320 |
+
'hilite-foreground':'#000000',
|
| 321 |
+
'hilite-background':'gray',
|
| 322 |
+
'break-foreground':'#ffffff',
|
| 323 |
+
'break-background':'#000000',
|
| 324 |
+
'hit-foreground':'#ffffff',
|
| 325 |
+
'hit-background':'#000000',
|
| 326 |
+
'error-foreground':'#ffffff',
|
| 327 |
+
'error-background':'#000000',
|
| 328 |
+
'context-foreground':'#000000',
|
| 329 |
+
'context-background':'#ffffff',
|
| 330 |
+
'linenumber-foreground':'#000000',
|
| 331 |
+
'linenumber-background':'#ffffff',
|
| 332 |
+
#cursor (only foreground can be set)
|
| 333 |
+
'cursor-foreground':'#000000',
|
| 334 |
+
#shell window
|
| 335 |
+
'stdout-foreground':'#000000',
|
| 336 |
+
'stdout-background':'#ffffff',
|
| 337 |
+
'stderr-foreground':'#000000',
|
| 338 |
+
'stderr-background':'#ffffff',
|
| 339 |
+
'console-foreground':'#000000',
|
| 340 |
+
'console-background':'#ffffff',
|
| 341 |
+
}
|
| 342 |
+
for element in theme:
|
| 343 |
+
if not (cfgParser.has_option(themeName, element) or
|
| 344 |
+
# Skip warning for new elements.
|
| 345 |
+
element.startswith(('context-', 'linenumber-'))):
|
| 346 |
+
# Print warning that will return a default color
|
| 347 |
+
warning = ('\n Warning: config.IdleConf.GetThemeDict'
|
| 348 |
+
' -\n problem retrieving theme element %r'
|
| 349 |
+
'\n from theme %r.\n'
|
| 350 |
+
' returning default color: %r' %
|
| 351 |
+
(element, themeName, theme[element]))
|
| 352 |
+
_warn(warning, 'highlight', themeName, element)
|
| 353 |
+
theme[element] = cfgParser.Get(
|
| 354 |
+
themeName, element, default=theme[element])
|
| 355 |
+
return theme
|
| 356 |
+
|
| 357 |
+
def CurrentTheme(self):
|
| 358 |
+
"Return the name of the currently active text color theme."
|
| 359 |
+
return self.current_colors_and_keys('Theme')
|
| 360 |
+
|
| 361 |
+
def CurrentKeys(self):
|
| 362 |
+
"""Return the name of the currently active key set."""
|
| 363 |
+
return self.current_colors_and_keys('Keys')
|
| 364 |
+
|
| 365 |
+
def current_colors_and_keys(self, section):
|
| 366 |
+
"""Return the currently active name for Theme or Keys section.
|
| 367 |
+
|
| 368 |
+
idlelib.config-main.def ('default') includes these sections
|
| 369 |
+
|
| 370 |
+
[Theme]
|
| 371 |
+
default= 1
|
| 372 |
+
name= IDLE Classic
|
| 373 |
+
name2=
|
| 374 |
+
|
| 375 |
+
[Keys]
|
| 376 |
+
default= 1
|
| 377 |
+
name=
|
| 378 |
+
name2=
|
| 379 |
+
|
| 380 |
+
Item 'name2', is used for built-in ('default') themes and keys
|
| 381 |
+
added after 2015 Oct 1 and 2016 July 1. This kludge is needed
|
| 382 |
+
because setting 'name' to a builtin not defined in older IDLEs
|
| 383 |
+
to display multiple error messages or quit.
|
| 384 |
+
See https://bugs.python.org/issue25313.
|
| 385 |
+
When default = True, 'name2' takes precedence over 'name',
|
| 386 |
+
while older IDLEs will just use name. When default = False,
|
| 387 |
+
'name2' may still be set, but it is ignored.
|
| 388 |
+
"""
|
| 389 |
+
cfgname = 'highlight' if section == 'Theme' else 'keys'
|
| 390 |
+
default = self.GetOption('main', section, 'default',
|
| 391 |
+
type='bool', default=True)
|
| 392 |
+
name = ''
|
| 393 |
+
if default:
|
| 394 |
+
name = self.GetOption('main', section, 'name2', default='')
|
| 395 |
+
if not name:
|
| 396 |
+
name = self.GetOption('main', section, 'name', default='')
|
| 397 |
+
if name:
|
| 398 |
+
source = self.defaultCfg if default else self.userCfg
|
| 399 |
+
if source[cfgname].has_section(name):
|
| 400 |
+
return name
|
| 401 |
+
return "IDLE Classic" if section == 'Theme' else self.default_keys()
|
| 402 |
+
|
| 403 |
+
@staticmethod
|
| 404 |
+
def default_keys():
|
| 405 |
+
if sys.platform[:3] == 'win':
|
| 406 |
+
return 'IDLE Classic Windows'
|
| 407 |
+
elif sys.platform == 'darwin':
|
| 408 |
+
return 'IDLE Classic OSX'
|
| 409 |
+
else:
|
| 410 |
+
return 'IDLE Modern Unix'
|
| 411 |
+
|
| 412 |
+
def GetExtensions(self, active_only=True,
|
| 413 |
+
editor_only=False, shell_only=False):
|
| 414 |
+
"""Return extensions in default and user config-extensions files.
|
| 415 |
+
|
| 416 |
+
If active_only True, only return active (enabled) extensions
|
| 417 |
+
and optionally only editor or shell extensions.
|
| 418 |
+
If active_only False, return all extensions.
|
| 419 |
+
"""
|
| 420 |
+
extns = self.RemoveKeyBindNames(
|
| 421 |
+
self.GetSectionList('default', 'extensions'))
|
| 422 |
+
userExtns = self.RemoveKeyBindNames(
|
| 423 |
+
self.GetSectionList('user', 'extensions'))
|
| 424 |
+
for extn in userExtns:
|
| 425 |
+
if extn not in extns: #user has added own extension
|
| 426 |
+
extns.append(extn)
|
| 427 |
+
for extn in ('AutoComplete','CodeContext',
|
| 428 |
+
'FormatParagraph','ParenMatch'):
|
| 429 |
+
extns.remove(extn)
|
| 430 |
+
# specific exclusions because we are storing config for mainlined old
|
| 431 |
+
# extensions in config-extensions.def for backward compatibility
|
| 432 |
+
if active_only:
|
| 433 |
+
activeExtns = []
|
| 434 |
+
for extn in extns:
|
| 435 |
+
if self.GetOption('extensions', extn, 'enable', default=True,
|
| 436 |
+
type='bool'):
|
| 437 |
+
#the extension is enabled
|
| 438 |
+
if editor_only or shell_only: # TODO both True contradict
|
| 439 |
+
if editor_only:
|
| 440 |
+
option = "enable_editor"
|
| 441 |
+
else:
|
| 442 |
+
option = "enable_shell"
|
| 443 |
+
if self.GetOption('extensions', extn,option,
|
| 444 |
+
default=True, type='bool',
|
| 445 |
+
warn_on_default=False):
|
| 446 |
+
activeExtns.append(extn)
|
| 447 |
+
else:
|
| 448 |
+
activeExtns.append(extn)
|
| 449 |
+
return activeExtns
|
| 450 |
+
else:
|
| 451 |
+
return extns
|
| 452 |
+
|
| 453 |
+
def RemoveKeyBindNames(self, extnNameList):
|
| 454 |
+
"Return extnNameList with keybinding section names removed."
|
| 455 |
+
return [n for n in extnNameList if not n.endswith(('_bindings', '_cfgBindings'))]
|
| 456 |
+
|
| 457 |
+
def GetExtnNameForEvent(self, virtualEvent):
|
| 458 |
+
"""Return the name of the extension binding virtualEvent, or None.
|
| 459 |
+
|
| 460 |
+
virtualEvent - string, name of the virtual event to test for,
|
| 461 |
+
without the enclosing '<< >>'
|
| 462 |
+
"""
|
| 463 |
+
extName = None
|
| 464 |
+
vEvent = '<<' + virtualEvent + '>>'
|
| 465 |
+
for extn in self.GetExtensions(active_only=0):
|
| 466 |
+
for event in self.GetExtensionKeys(extn):
|
| 467 |
+
if event == vEvent:
|
| 468 |
+
extName = extn # TODO return here?
|
| 469 |
+
return extName
|
| 470 |
+
|
| 471 |
+
def GetExtensionKeys(self, extensionName):
|
| 472 |
+
"""Return dict: {configurable extensionName event : active keybinding}.
|
| 473 |
+
|
| 474 |
+
Events come from default config extension_cfgBindings section.
|
| 475 |
+
Keybindings come from GetCurrentKeySet() active key dict,
|
| 476 |
+
where previously used bindings are disabled.
|
| 477 |
+
"""
|
| 478 |
+
keysName = extensionName + '_cfgBindings'
|
| 479 |
+
activeKeys = self.GetCurrentKeySet()
|
| 480 |
+
extKeys = {}
|
| 481 |
+
if self.defaultCfg['extensions'].has_section(keysName):
|
| 482 |
+
eventNames = self.defaultCfg['extensions'].GetOptionList(keysName)
|
| 483 |
+
for eventName in eventNames:
|
| 484 |
+
event = '<<' + eventName + '>>'
|
| 485 |
+
binding = activeKeys[event]
|
| 486 |
+
extKeys[event] = binding
|
| 487 |
+
return extKeys
|
| 488 |
+
|
| 489 |
+
def __GetRawExtensionKeys(self,extensionName):
|
| 490 |
+
"""Return dict {configurable extensionName event : keybinding list}.
|
| 491 |
+
|
| 492 |
+
Events come from default config extension_cfgBindings section.
|
| 493 |
+
Keybindings list come from the splitting of GetOption, which
|
| 494 |
+
tries user config before default config.
|
| 495 |
+
"""
|
| 496 |
+
keysName = extensionName+'_cfgBindings'
|
| 497 |
+
extKeys = {}
|
| 498 |
+
if self.defaultCfg['extensions'].has_section(keysName):
|
| 499 |
+
eventNames = self.defaultCfg['extensions'].GetOptionList(keysName)
|
| 500 |
+
for eventName in eventNames:
|
| 501 |
+
binding = self.GetOption(
|
| 502 |
+
'extensions', keysName, eventName, default='').split()
|
| 503 |
+
event = '<<' + eventName + '>>'
|
| 504 |
+
extKeys[event] = binding
|
| 505 |
+
return extKeys
|
| 506 |
+
|
| 507 |
+
def GetExtensionBindings(self, extensionName):
|
| 508 |
+
"""Return dict {extensionName event : active or defined keybinding}.
|
| 509 |
+
|
| 510 |
+
Augment self.GetExtensionKeys(extensionName) with mapping of non-
|
| 511 |
+
configurable events (from default config) to GetOption splits,
|
| 512 |
+
as in self.__GetRawExtensionKeys.
|
| 513 |
+
"""
|
| 514 |
+
bindsName = extensionName + '_bindings'
|
| 515 |
+
extBinds = self.GetExtensionKeys(extensionName)
|
| 516 |
+
#add the non-configurable bindings
|
| 517 |
+
if self.defaultCfg['extensions'].has_section(bindsName):
|
| 518 |
+
eventNames = self.defaultCfg['extensions'].GetOptionList(bindsName)
|
| 519 |
+
for eventName in eventNames:
|
| 520 |
+
binding = self.GetOption(
|
| 521 |
+
'extensions', bindsName, eventName, default='').split()
|
| 522 |
+
event = '<<' + eventName + '>>'
|
| 523 |
+
extBinds[event] = binding
|
| 524 |
+
|
| 525 |
+
return extBinds
|
| 526 |
+
|
| 527 |
+
def GetKeyBinding(self, keySetName, eventStr):
|
| 528 |
+
"""Return the keybinding list for keySetName eventStr.
|
| 529 |
+
|
| 530 |
+
keySetName - name of key binding set (config-keys section).
|
| 531 |
+
eventStr - virtual event, including brackets, as in '<<event>>'.
|
| 532 |
+
"""
|
| 533 |
+
eventName = eventStr[2:-2] #trim off the angle brackets
|
| 534 |
+
binding = self.GetOption('keys', keySetName, eventName, default='',
|
| 535 |
+
warn_on_default=False).split()
|
| 536 |
+
return binding
|
| 537 |
+
|
| 538 |
+
def GetCurrentKeySet(self):
|
| 539 |
+
"Return CurrentKeys with 'darwin' modifications."
|
| 540 |
+
result = self.GetKeySet(self.CurrentKeys())
|
| 541 |
+
|
| 542 |
+
if sys.platform == "darwin":
|
| 543 |
+
# macOS (OS X) Tk variants do not support the "Alt"
|
| 544 |
+
# keyboard modifier. Replace it with "Option".
|
| 545 |
+
# TODO (Ned?): the "Option" modifier does not work properly
|
| 546 |
+
# for Cocoa Tk and XQuartz Tk so we should not use it
|
| 547 |
+
# in the default 'OSX' keyset.
|
| 548 |
+
for k, v in result.items():
|
| 549 |
+
v2 = [ x.replace('<Alt-', '<Option-') for x in v ]
|
| 550 |
+
if v != v2:
|
| 551 |
+
result[k] = v2
|
| 552 |
+
|
| 553 |
+
return result
|
| 554 |
+
|
| 555 |
+
def GetKeySet(self, keySetName):
|
| 556 |
+
"""Return event-key dict for keySetName core plus active extensions.
|
| 557 |
+
|
| 558 |
+
If a binding defined in an extension is already in use, the
|
| 559 |
+
extension binding is disabled by being set to ''
|
| 560 |
+
"""
|
| 561 |
+
keySet = self.GetCoreKeys(keySetName)
|
| 562 |
+
activeExtns = self.GetExtensions(active_only=1)
|
| 563 |
+
for extn in activeExtns:
|
| 564 |
+
extKeys = self.__GetRawExtensionKeys(extn)
|
| 565 |
+
if extKeys: #the extension defines keybindings
|
| 566 |
+
for event in extKeys:
|
| 567 |
+
if extKeys[event] in keySet.values():
|
| 568 |
+
#the binding is already in use
|
| 569 |
+
extKeys[event] = '' #disable this binding
|
| 570 |
+
keySet[event] = extKeys[event] #add binding
|
| 571 |
+
return keySet
|
| 572 |
+
|
| 573 |
+
def IsCoreBinding(self, virtualEvent):
|
| 574 |
+
"""Return True if the virtual event is one of the core idle key events.
|
| 575 |
+
|
| 576 |
+
virtualEvent - string, name of the virtual event to test for,
|
| 577 |
+
without the enclosing '<< >>'
|
| 578 |
+
"""
|
| 579 |
+
return ('<<'+virtualEvent+'>>') in self.GetCoreKeys()
|
| 580 |
+
|
| 581 |
+
# TODO make keyBindins a file or class attribute used for test above
|
| 582 |
+
# and copied in function below.
|
| 583 |
+
|
| 584 |
+
former_extension_events = { # Those with user-configurable keys.
|
| 585 |
+
'<<force-open-completions>>', '<<expand-word>>',
|
| 586 |
+
'<<force-open-calltip>>', '<<flash-paren>>', '<<format-paragraph>>',
|
| 587 |
+
'<<run-module>>', '<<check-module>>', '<<zoom-height>>',
|
| 588 |
+
'<<run-custom>>',
|
| 589 |
+
}
|
| 590 |
+
|
| 591 |
+
def GetCoreKeys(self, keySetName=None):
|
| 592 |
+
"""Return dict of core virtual-key keybindings for keySetName.
|
| 593 |
+
|
| 594 |
+
The default keySetName None corresponds to the keyBindings base
|
| 595 |
+
dict. If keySetName is not None, bindings from the config
|
| 596 |
+
file(s) are loaded _over_ these defaults, so if there is a
|
| 597 |
+
problem getting any core binding there will be an 'ultimate last
|
| 598 |
+
resort fallback' to the CUA-ish bindings defined here.
|
| 599 |
+
"""
|
| 600 |
+
keyBindings={
|
| 601 |
+
'<<copy>>': ['<Control-c>', '<Control-C>'],
|
| 602 |
+
'<<cut>>': ['<Control-x>', '<Control-X>'],
|
| 603 |
+
'<<paste>>': ['<Control-v>', '<Control-V>'],
|
| 604 |
+
'<<beginning-of-line>>': ['<Control-a>', '<Home>'],
|
| 605 |
+
'<<center-insert>>': ['<Control-l>'],
|
| 606 |
+
'<<close-all-windows>>': ['<Control-q>'],
|
| 607 |
+
'<<close-window>>': ['<Alt-F4>'],
|
| 608 |
+
'<<do-nothing>>': ['<Control-x>'],
|
| 609 |
+
'<<end-of-file>>': ['<Control-d>'],
|
| 610 |
+
'<<python-docs>>': ['<F1>'],
|
| 611 |
+
'<<python-context-help>>': ['<Shift-F1>'],
|
| 612 |
+
'<<history-next>>': ['<Alt-n>'],
|
| 613 |
+
'<<history-previous>>': ['<Alt-p>'],
|
| 614 |
+
'<<interrupt-execution>>': ['<Control-c>'],
|
| 615 |
+
'<<view-restart>>': ['<F6>'],
|
| 616 |
+
'<<restart-shell>>': ['<Control-F6>'],
|
| 617 |
+
'<<open-class-browser>>': ['<Alt-c>'],
|
| 618 |
+
'<<open-module>>': ['<Alt-m>'],
|
| 619 |
+
'<<open-new-window>>': ['<Control-n>'],
|
| 620 |
+
'<<open-window-from-file>>': ['<Control-o>'],
|
| 621 |
+
'<<plain-newline-and-indent>>': ['<Control-j>'],
|
| 622 |
+
'<<print-window>>': ['<Control-p>'],
|
| 623 |
+
'<<redo>>': ['<Control-y>'],
|
| 624 |
+
'<<remove-selection>>': ['<Escape>'],
|
| 625 |
+
'<<save-copy-of-window-as-file>>': ['<Alt-Shift-S>'],
|
| 626 |
+
'<<save-window-as-file>>': ['<Alt-s>'],
|
| 627 |
+
'<<save-window>>': ['<Control-s>'],
|
| 628 |
+
'<<select-all>>': ['<Alt-a>'],
|
| 629 |
+
'<<toggle-auto-coloring>>': ['<Control-slash>'],
|
| 630 |
+
'<<undo>>': ['<Control-z>'],
|
| 631 |
+
'<<find-again>>': ['<Control-g>', '<F3>'],
|
| 632 |
+
'<<find-in-files>>': ['<Alt-F3>'],
|
| 633 |
+
'<<find-selection>>': ['<Control-F3>'],
|
| 634 |
+
'<<find>>': ['<Control-f>'],
|
| 635 |
+
'<<replace>>': ['<Control-h>'],
|
| 636 |
+
'<<goto-line>>': ['<Alt-g>'],
|
| 637 |
+
'<<smart-backspace>>': ['<Key-BackSpace>'],
|
| 638 |
+
'<<newline-and-indent>>': ['<Key-Return>', '<Key-KP_Enter>'],
|
| 639 |
+
'<<smart-indent>>': ['<Key-Tab>'],
|
| 640 |
+
'<<indent-region>>': ['<Control-Key-bracketright>'],
|
| 641 |
+
'<<dedent-region>>': ['<Control-Key-bracketleft>'],
|
| 642 |
+
'<<comment-region>>': ['<Alt-Key-3>'],
|
| 643 |
+
'<<uncomment-region>>': ['<Alt-Key-4>'],
|
| 644 |
+
'<<tabify-region>>': ['<Alt-Key-5>'],
|
| 645 |
+
'<<untabify-region>>': ['<Alt-Key-6>'],
|
| 646 |
+
'<<toggle-tabs>>': ['<Alt-Key-t>'],
|
| 647 |
+
'<<change-indentwidth>>': ['<Alt-Key-u>'],
|
| 648 |
+
'<<del-word-left>>': ['<Control-Key-BackSpace>'],
|
| 649 |
+
'<<del-word-right>>': ['<Control-Key-Delete>'],
|
| 650 |
+
'<<force-open-completions>>': ['<Control-Key-space>'],
|
| 651 |
+
'<<expand-word>>': ['<Alt-Key-slash>'],
|
| 652 |
+
'<<force-open-calltip>>': ['<Control-Key-backslash>'],
|
| 653 |
+
'<<flash-paren>>': ['<Control-Key-0>'],
|
| 654 |
+
'<<format-paragraph>>': ['<Alt-Key-q>'],
|
| 655 |
+
'<<run-module>>': ['<Key-F5>'],
|
| 656 |
+
'<<run-custom>>': ['<Shift-Key-F5>'],
|
| 657 |
+
'<<check-module>>': ['<Alt-Key-x>'],
|
| 658 |
+
'<<zoom-height>>': ['<Alt-Key-2>'],
|
| 659 |
+
}
|
| 660 |
+
|
| 661 |
+
if keySetName:
|
| 662 |
+
if not (self.userCfg['keys'].has_section(keySetName) or
|
| 663 |
+
self.defaultCfg['keys'].has_section(keySetName)):
|
| 664 |
+
warning = (
|
| 665 |
+
'\n Warning: config.py - IdleConf.GetCoreKeys -\n'
|
| 666 |
+
' key set %r is not defined, using default bindings.' %
|
| 667 |
+
(keySetName,)
|
| 668 |
+
)
|
| 669 |
+
_warn(warning, 'keys', keySetName)
|
| 670 |
+
else:
|
| 671 |
+
for event in keyBindings:
|
| 672 |
+
binding = self.GetKeyBinding(keySetName, event)
|
| 673 |
+
if binding:
|
| 674 |
+
keyBindings[event] = binding
|
| 675 |
+
# Otherwise return default in keyBindings.
|
| 676 |
+
elif event not in self.former_extension_events:
|
| 677 |
+
warning = (
|
| 678 |
+
'\n Warning: config.py - IdleConf.GetCoreKeys -\n'
|
| 679 |
+
' problem retrieving key binding for event %r\n'
|
| 680 |
+
' from key set %r.\n'
|
| 681 |
+
' returning default value: %r' %
|
| 682 |
+
(event, keySetName, keyBindings[event])
|
| 683 |
+
)
|
| 684 |
+
_warn(warning, 'keys', keySetName, event)
|
| 685 |
+
return keyBindings
|
| 686 |
+
|
| 687 |
+
def GetExtraHelpSourceList(self, configSet):
|
| 688 |
+
"""Return list of extra help sources from a given configSet.
|
| 689 |
+
|
| 690 |
+
Valid configSets are 'user' or 'default'. Return a list of tuples of
|
| 691 |
+
the form (menu_item , path_to_help_file , option), or return the empty
|
| 692 |
+
list. 'option' is the sequence number of the help resource. 'option'
|
| 693 |
+
values determine the position of the menu items on the Help menu,
|
| 694 |
+
therefore the returned list must be sorted by 'option'.
|
| 695 |
+
|
| 696 |
+
"""
|
| 697 |
+
helpSources = []
|
| 698 |
+
if configSet == 'user':
|
| 699 |
+
cfgParser = self.userCfg['main']
|
| 700 |
+
elif configSet == 'default':
|
| 701 |
+
cfgParser = self.defaultCfg['main']
|
| 702 |
+
else:
|
| 703 |
+
raise InvalidConfigSet('Invalid configSet specified')
|
| 704 |
+
options=cfgParser.GetOptionList('HelpFiles')
|
| 705 |
+
for option in options:
|
| 706 |
+
value=cfgParser.Get('HelpFiles', option, default=';')
|
| 707 |
+
if value.find(';') == -1: #malformed config entry with no ';'
|
| 708 |
+
menuItem = '' #make these empty
|
| 709 |
+
helpPath = '' #so value won't be added to list
|
| 710 |
+
else: #config entry contains ';' as expected
|
| 711 |
+
value=value.split(';')
|
| 712 |
+
menuItem=value[0].strip()
|
| 713 |
+
helpPath=value[1].strip()
|
| 714 |
+
if menuItem and helpPath: #neither are empty strings
|
| 715 |
+
helpSources.append( (menuItem,helpPath,option) )
|
| 716 |
+
helpSources.sort(key=lambda x: x[2])
|
| 717 |
+
return helpSources
|
| 718 |
+
|
| 719 |
+
def GetAllExtraHelpSourcesList(self):
|
| 720 |
+
"""Return a list of the details of all additional help sources.
|
| 721 |
+
|
| 722 |
+
Tuples in the list are those of GetExtraHelpSourceList.
|
| 723 |
+
"""
|
| 724 |
+
allHelpSources = (self.GetExtraHelpSourceList('default') +
|
| 725 |
+
self.GetExtraHelpSourceList('user') )
|
| 726 |
+
return allHelpSources
|
| 727 |
+
|
| 728 |
+
def GetFont(self, root, configType, section):
|
| 729 |
+
"""Retrieve a font from configuration (font, font-size, font-bold)
|
| 730 |
+
Intercept the special value 'TkFixedFont' and substitute
|
| 731 |
+
the actual font, factoring in some tweaks if needed for
|
| 732 |
+
appearance sakes.
|
| 733 |
+
|
| 734 |
+
The 'root' parameter can normally be any valid Tkinter widget.
|
| 735 |
+
|
| 736 |
+
Return a tuple (family, size, weight) suitable for passing
|
| 737 |
+
to tkinter.Font
|
| 738 |
+
"""
|
| 739 |
+
family = self.GetOption(configType, section, 'font', default='courier')
|
| 740 |
+
size = self.GetOption(configType, section, 'font-size', type='int',
|
| 741 |
+
default='10')
|
| 742 |
+
bold = self.GetOption(configType, section, 'font-bold', default=0,
|
| 743 |
+
type='bool')
|
| 744 |
+
if (family == 'TkFixedFont'):
|
| 745 |
+
f = Font(name='TkFixedFont', exists=True, root=root)
|
| 746 |
+
actualFont = Font.actual(f)
|
| 747 |
+
family = actualFont['family']
|
| 748 |
+
size = actualFont['size']
|
| 749 |
+
if size <= 0:
|
| 750 |
+
size = 10 # if font in pixels, ignore actual size
|
| 751 |
+
bold = actualFont['weight'] == 'bold'
|
| 752 |
+
return (family, size, 'bold' if bold else 'normal')
|
| 753 |
+
|
| 754 |
+
def LoadCfgFiles(self):
|
| 755 |
+
"Load all configuration files."
|
| 756 |
+
for key in self.defaultCfg:
|
| 757 |
+
self.defaultCfg[key].Load()
|
| 758 |
+
self.userCfg[key].Load() #same keys
|
| 759 |
+
|
| 760 |
+
def SaveUserCfgFiles(self):
|
| 761 |
+
"Write all loaded user configuration files to disk."
|
| 762 |
+
for key in self.userCfg:
|
| 763 |
+
self.userCfg[key].Save()
|
| 764 |
+
|
| 765 |
+
|
| 766 |
+
idleConf = IdleConf()
|
| 767 |
+
|
| 768 |
+
_warned = set()
|
| 769 |
+
def _warn(msg, *key):
|
| 770 |
+
key = (msg,) + key
|
| 771 |
+
if key not in _warned:
|
| 772 |
+
try:
|
| 773 |
+
print(msg, file=sys.stderr)
|
| 774 |
+
except OSError:
|
| 775 |
+
pass
|
| 776 |
+
_warned.add(key)
|
| 777 |
+
|
| 778 |
+
|
| 779 |
+
class ConfigChanges(dict):
|
| 780 |
+
"""Manage a user's proposed configuration option changes.
|
| 781 |
+
|
| 782 |
+
Names used across multiple methods:
|
| 783 |
+
page -- one of the 4 top-level dicts representing a
|
| 784 |
+
.idlerc/config-x.cfg file.
|
| 785 |
+
config_type -- name of a page.
|
| 786 |
+
section -- a section within a page/file.
|
| 787 |
+
option -- name of an option within a section.
|
| 788 |
+
value -- value for the option.
|
| 789 |
+
|
| 790 |
+
Methods
|
| 791 |
+
add_option: Add option and value to changes.
|
| 792 |
+
save_option: Save option and value to config parser.
|
| 793 |
+
save_all: Save all the changes to the config parser and file.
|
| 794 |
+
delete_section: If section exists,
|
| 795 |
+
delete from changes, userCfg, and file.
|
| 796 |
+
clear: Clear all changes by clearing each page.
|
| 797 |
+
"""
|
| 798 |
+
def __init__(self):
|
| 799 |
+
"Create a page for each configuration file"
|
| 800 |
+
self.pages = [] # List of unhashable dicts.
|
| 801 |
+
for config_type in idleConf.config_types:
|
| 802 |
+
self[config_type] = {}
|
| 803 |
+
self.pages.append(self[config_type])
|
| 804 |
+
|
| 805 |
+
def add_option(self, config_type, section, item, value):
|
| 806 |
+
"Add item/value pair for config_type and section."
|
| 807 |
+
page = self[config_type]
|
| 808 |
+
value = str(value) # Make sure we use a string.
|
| 809 |
+
if section not in page:
|
| 810 |
+
page[section] = {}
|
| 811 |
+
page[section][item] = value
|
| 812 |
+
|
| 813 |
+
@staticmethod
|
| 814 |
+
def save_option(config_type, section, item, value):
|
| 815 |
+
"""Return True if the configuration value was added or changed.
|
| 816 |
+
|
| 817 |
+
Helper for save_all.
|
| 818 |
+
"""
|
| 819 |
+
if idleConf.defaultCfg[config_type].has_option(section, item):
|
| 820 |
+
if idleConf.defaultCfg[config_type].Get(section, item) == value:
|
| 821 |
+
# The setting equals a default setting, remove it from user cfg.
|
| 822 |
+
return idleConf.userCfg[config_type].RemoveOption(section, item)
|
| 823 |
+
# If we got here, set the option.
|
| 824 |
+
return idleConf.userCfg[config_type].SetOption(section, item, value)
|
| 825 |
+
|
| 826 |
+
def save_all(self):
|
| 827 |
+
"""Save configuration changes to the user config file.
|
| 828 |
+
|
| 829 |
+
Clear self in preparation for additional changes.
|
| 830 |
+
Return changed for testing.
|
| 831 |
+
"""
|
| 832 |
+
idleConf.userCfg['main'].Save()
|
| 833 |
+
|
| 834 |
+
changed = False
|
| 835 |
+
for config_type in self:
|
| 836 |
+
cfg_type_changed = False
|
| 837 |
+
page = self[config_type]
|
| 838 |
+
for section in page:
|
| 839 |
+
if section == 'HelpFiles': # Remove it for replacement.
|
| 840 |
+
idleConf.userCfg['main'].remove_section('HelpFiles')
|
| 841 |
+
cfg_type_changed = True
|
| 842 |
+
for item, value in page[section].items():
|
| 843 |
+
if self.save_option(config_type, section, item, value):
|
| 844 |
+
cfg_type_changed = True
|
| 845 |
+
if cfg_type_changed:
|
| 846 |
+
idleConf.userCfg[config_type].Save()
|
| 847 |
+
changed = True
|
| 848 |
+
for config_type in ['keys', 'highlight']:
|
| 849 |
+
# Save these even if unchanged!
|
| 850 |
+
idleConf.userCfg[config_type].Save()
|
| 851 |
+
self.clear()
|
| 852 |
+
# ConfigDialog caller must add the following call
|
| 853 |
+
# self.save_all_changed_extensions() # Uses a different mechanism.
|
| 854 |
+
return changed
|
| 855 |
+
|
| 856 |
+
def delete_section(self, config_type, section):
|
| 857 |
+
"""Delete a section from self, userCfg, and file.
|
| 858 |
+
|
| 859 |
+
Used to delete custom themes and keysets.
|
| 860 |
+
"""
|
| 861 |
+
if section in self[config_type]:
|
| 862 |
+
del self[config_type][section]
|
| 863 |
+
configpage = idleConf.userCfg[config_type]
|
| 864 |
+
configpage.remove_section(section)
|
| 865 |
+
configpage.Save()
|
| 866 |
+
|
| 867 |
+
def clear(self):
|
| 868 |
+
"""Clear all 4 pages.
|
| 869 |
+
|
| 870 |
+
Called in save_all after saving to idleConf.
|
| 871 |
+
XXX Mark window *title* when there are changes; unmark here.
|
| 872 |
+
"""
|
| 873 |
+
for page in self.pages:
|
| 874 |
+
page.clear()
|
| 875 |
+
|
| 876 |
+
|
| 877 |
+
# TODO Revise test output, write expanded unittest
|
| 878 |
+
def _dump(): # htest # (not really, but ignore in coverage)
|
| 879 |
+
from zlib import crc32
|
| 880 |
+
line, crc = 0, 0
|
| 881 |
+
|
| 882 |
+
def sprint(obj):
|
| 883 |
+
global line, crc
|
| 884 |
+
txt = str(obj)
|
| 885 |
+
line += 1
|
| 886 |
+
crc = crc32(txt.encode(encoding='utf-8'), crc)
|
| 887 |
+
print(txt)
|
| 888 |
+
#print('***', line, crc, '***') # Uncomment for diagnosis.
|
| 889 |
+
|
| 890 |
+
def dumpCfg(cfg):
|
| 891 |
+
print('\n', cfg, '\n') # Cfg has variable '0xnnnnnnnn' address.
|
| 892 |
+
for key in sorted(cfg.keys()):
|
| 893 |
+
sections = cfg[key].sections()
|
| 894 |
+
sprint(key)
|
| 895 |
+
sprint(sections)
|
| 896 |
+
for section in sections:
|
| 897 |
+
options = cfg[key].options(section)
|
| 898 |
+
sprint(section)
|
| 899 |
+
sprint(options)
|
| 900 |
+
for option in options:
|
| 901 |
+
sprint(option + ' = ' + cfg[key].Get(section, option))
|
| 902 |
+
|
| 903 |
+
dumpCfg(idleConf.defaultCfg)
|
| 904 |
+
dumpCfg(idleConf.userCfg)
|
| 905 |
+
print('\nlines = ', line, ', crc = ', crc, sep='')
|
| 906 |
+
|
| 907 |
+
if __name__ == '__main__':
|
| 908 |
+
from unittest import main
|
| 909 |
+
main('idlelib.idle_test.test_config', verbosity=2, exit=False)
|
| 910 |
+
|
| 911 |
+
# Run revised _dump() as htest?
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/configdialog.py
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/debugger.py
ADDED
|
@@ -0,0 +1,550 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import bdb
|
| 2 |
+
import os
|
| 3 |
+
|
| 4 |
+
from tkinter import *
|
| 5 |
+
from tkinter.ttk import Frame, Scrollbar
|
| 6 |
+
|
| 7 |
+
from idlelib import macosx
|
| 8 |
+
from idlelib.scrolledlist import ScrolledList
|
| 9 |
+
from idlelib.window import ListedToplevel
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
class Idb(bdb.Bdb):
|
| 13 |
+
|
| 14 |
+
def __init__(self, gui):
|
| 15 |
+
self.gui = gui # An instance of Debugger or proxy of remote.
|
| 16 |
+
bdb.Bdb.__init__(self)
|
| 17 |
+
|
| 18 |
+
def user_line(self, frame):
|
| 19 |
+
if self.in_rpc_code(frame):
|
| 20 |
+
self.set_step()
|
| 21 |
+
return
|
| 22 |
+
message = self.__frame2message(frame)
|
| 23 |
+
try:
|
| 24 |
+
self.gui.interaction(message, frame)
|
| 25 |
+
except TclError: # When closing debugger window with [x] in 3.x
|
| 26 |
+
pass
|
| 27 |
+
|
| 28 |
+
def user_exception(self, frame, info):
|
| 29 |
+
if self.in_rpc_code(frame):
|
| 30 |
+
self.set_step()
|
| 31 |
+
return
|
| 32 |
+
message = self.__frame2message(frame)
|
| 33 |
+
self.gui.interaction(message, frame, info)
|
| 34 |
+
|
| 35 |
+
def in_rpc_code(self, frame):
|
| 36 |
+
if frame.f_code.co_filename.count('rpc.py'):
|
| 37 |
+
return True
|
| 38 |
+
else:
|
| 39 |
+
prev_frame = frame.f_back
|
| 40 |
+
prev_name = prev_frame.f_code.co_filename
|
| 41 |
+
if 'idlelib' in prev_name and 'debugger' in prev_name:
|
| 42 |
+
# catch both idlelib/debugger.py and idlelib/debugger_r.py
|
| 43 |
+
# on both Posix and Windows
|
| 44 |
+
return False
|
| 45 |
+
return self.in_rpc_code(prev_frame)
|
| 46 |
+
|
| 47 |
+
def __frame2message(self, frame):
|
| 48 |
+
code = frame.f_code
|
| 49 |
+
filename = code.co_filename
|
| 50 |
+
lineno = frame.f_lineno
|
| 51 |
+
basename = os.path.basename(filename)
|
| 52 |
+
message = "%s:%s" % (basename, lineno)
|
| 53 |
+
if code.co_name != "?":
|
| 54 |
+
message = "%s: %s()" % (message, code.co_name)
|
| 55 |
+
return message
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
class Debugger:
|
| 59 |
+
|
| 60 |
+
vstack = vsource = vlocals = vglobals = None
|
| 61 |
+
|
| 62 |
+
def __init__(self, pyshell, idb=None):
|
| 63 |
+
if idb is None:
|
| 64 |
+
idb = Idb(self)
|
| 65 |
+
self.pyshell = pyshell
|
| 66 |
+
self.idb = idb # If passed, a proxy of remote instance.
|
| 67 |
+
self.frame = None
|
| 68 |
+
self.make_gui()
|
| 69 |
+
self.interacting = 0
|
| 70 |
+
self.nesting_level = 0
|
| 71 |
+
|
| 72 |
+
def run(self, *args):
|
| 73 |
+
# Deal with the scenario where we've already got a program running
|
| 74 |
+
# in the debugger and we want to start another. If that is the case,
|
| 75 |
+
# our second 'run' was invoked from an event dispatched not from
|
| 76 |
+
# the main event loop, but from the nested event loop in 'interaction'
|
| 77 |
+
# below. So our stack looks something like this:
|
| 78 |
+
# outer main event loop
|
| 79 |
+
# run()
|
| 80 |
+
# <running program with traces>
|
| 81 |
+
# callback to debugger's interaction()
|
| 82 |
+
# nested event loop
|
| 83 |
+
# run() for second command
|
| 84 |
+
#
|
| 85 |
+
# This kind of nesting of event loops causes all kinds of problems
|
| 86 |
+
# (see e.g. issue #24455) especially when dealing with running as a
|
| 87 |
+
# subprocess, where there's all kinds of extra stuff happening in
|
| 88 |
+
# there - insert a traceback.print_stack() to check it out.
|
| 89 |
+
#
|
| 90 |
+
# By this point, we've already called restart_subprocess() in
|
| 91 |
+
# ScriptBinding. However, we also need to unwind the stack back to
|
| 92 |
+
# that outer event loop. To accomplish this, we:
|
| 93 |
+
# - return immediately from the nested run()
|
| 94 |
+
# - abort_loop ensures the nested event loop will terminate
|
| 95 |
+
# - the debugger's interaction routine completes normally
|
| 96 |
+
# - the restart_subprocess() will have taken care of stopping
|
| 97 |
+
# the running program, which will also let the outer run complete
|
| 98 |
+
#
|
| 99 |
+
# That leaves us back at the outer main event loop, at which point our
|
| 100 |
+
# after event can fire, and we'll come back to this routine with a
|
| 101 |
+
# clean stack.
|
| 102 |
+
if self.nesting_level > 0:
|
| 103 |
+
self.abort_loop()
|
| 104 |
+
self.root.after(100, lambda: self.run(*args))
|
| 105 |
+
return
|
| 106 |
+
try:
|
| 107 |
+
self.interacting = 1
|
| 108 |
+
return self.idb.run(*args)
|
| 109 |
+
finally:
|
| 110 |
+
self.interacting = 0
|
| 111 |
+
|
| 112 |
+
def close(self, event=None):
|
| 113 |
+
try:
|
| 114 |
+
self.quit()
|
| 115 |
+
except Exception:
|
| 116 |
+
pass
|
| 117 |
+
if self.interacting:
|
| 118 |
+
self.top.bell()
|
| 119 |
+
return
|
| 120 |
+
if self.stackviewer:
|
| 121 |
+
self.stackviewer.close(); self.stackviewer = None
|
| 122 |
+
# Clean up pyshell if user clicked debugger control close widget.
|
| 123 |
+
# (Causes a harmless extra cycle through close_debugger() if user
|
| 124 |
+
# toggled debugger from pyshell Debug menu)
|
| 125 |
+
self.pyshell.close_debugger()
|
| 126 |
+
# Now close the debugger control window....
|
| 127 |
+
self.top.destroy()
|
| 128 |
+
|
| 129 |
+
def make_gui(self):
|
| 130 |
+
pyshell = self.pyshell
|
| 131 |
+
self.flist = pyshell.flist
|
| 132 |
+
self.root = root = pyshell.root
|
| 133 |
+
self.top = top = ListedToplevel(root)
|
| 134 |
+
self.top.wm_title("Debug Control")
|
| 135 |
+
self.top.wm_iconname("Debug")
|
| 136 |
+
top.wm_protocol("WM_DELETE_WINDOW", self.close)
|
| 137 |
+
self.top.bind("<Escape>", self.close)
|
| 138 |
+
#
|
| 139 |
+
self.bframe = bframe = Frame(top)
|
| 140 |
+
self.bframe.pack(anchor="w")
|
| 141 |
+
self.buttons = bl = []
|
| 142 |
+
#
|
| 143 |
+
self.bcont = b = Button(bframe, text="Go", command=self.cont)
|
| 144 |
+
bl.append(b)
|
| 145 |
+
self.bstep = b = Button(bframe, text="Step", command=self.step)
|
| 146 |
+
bl.append(b)
|
| 147 |
+
self.bnext = b = Button(bframe, text="Over", command=self.next)
|
| 148 |
+
bl.append(b)
|
| 149 |
+
self.bret = b = Button(bframe, text="Out", command=self.ret)
|
| 150 |
+
bl.append(b)
|
| 151 |
+
self.bret = b = Button(bframe, text="Quit", command=self.quit)
|
| 152 |
+
bl.append(b)
|
| 153 |
+
#
|
| 154 |
+
for b in bl:
|
| 155 |
+
b.configure(state="disabled")
|
| 156 |
+
b.pack(side="left")
|
| 157 |
+
#
|
| 158 |
+
self.cframe = cframe = Frame(bframe)
|
| 159 |
+
self.cframe.pack(side="left")
|
| 160 |
+
#
|
| 161 |
+
if not self.vstack:
|
| 162 |
+
self.__class__.vstack = BooleanVar(top)
|
| 163 |
+
self.vstack.set(1)
|
| 164 |
+
self.bstack = Checkbutton(cframe,
|
| 165 |
+
text="Stack", command=self.show_stack, variable=self.vstack)
|
| 166 |
+
self.bstack.grid(row=0, column=0)
|
| 167 |
+
if not self.vsource:
|
| 168 |
+
self.__class__.vsource = BooleanVar(top)
|
| 169 |
+
self.bsource = Checkbutton(cframe,
|
| 170 |
+
text="Source", command=self.show_source, variable=self.vsource)
|
| 171 |
+
self.bsource.grid(row=0, column=1)
|
| 172 |
+
if not self.vlocals:
|
| 173 |
+
self.__class__.vlocals = BooleanVar(top)
|
| 174 |
+
self.vlocals.set(1)
|
| 175 |
+
self.blocals = Checkbutton(cframe,
|
| 176 |
+
text="Locals", command=self.show_locals, variable=self.vlocals)
|
| 177 |
+
self.blocals.grid(row=1, column=0)
|
| 178 |
+
if not self.vglobals:
|
| 179 |
+
self.__class__.vglobals = BooleanVar(top)
|
| 180 |
+
self.bglobals = Checkbutton(cframe,
|
| 181 |
+
text="Globals", command=self.show_globals, variable=self.vglobals)
|
| 182 |
+
self.bglobals.grid(row=1, column=1)
|
| 183 |
+
#
|
| 184 |
+
self.status = Label(top, anchor="w")
|
| 185 |
+
self.status.pack(anchor="w")
|
| 186 |
+
self.error = Label(top, anchor="w")
|
| 187 |
+
self.error.pack(anchor="w", fill="x")
|
| 188 |
+
self.errorbg = self.error.cget("background")
|
| 189 |
+
#
|
| 190 |
+
self.fstack = Frame(top, height=1)
|
| 191 |
+
self.fstack.pack(expand=1, fill="both")
|
| 192 |
+
self.flocals = Frame(top)
|
| 193 |
+
self.flocals.pack(expand=1, fill="both")
|
| 194 |
+
self.fglobals = Frame(top, height=1)
|
| 195 |
+
self.fglobals.pack(expand=1, fill="both")
|
| 196 |
+
#
|
| 197 |
+
if self.vstack.get():
|
| 198 |
+
self.show_stack()
|
| 199 |
+
if self.vlocals.get():
|
| 200 |
+
self.show_locals()
|
| 201 |
+
if self.vglobals.get():
|
| 202 |
+
self.show_globals()
|
| 203 |
+
|
| 204 |
+
def interaction(self, message, frame, info=None):
|
| 205 |
+
self.frame = frame
|
| 206 |
+
self.status.configure(text=message)
|
| 207 |
+
#
|
| 208 |
+
if info:
|
| 209 |
+
type, value, tb = info
|
| 210 |
+
try:
|
| 211 |
+
m1 = type.__name__
|
| 212 |
+
except AttributeError:
|
| 213 |
+
m1 = "%s" % str(type)
|
| 214 |
+
if value is not None:
|
| 215 |
+
try:
|
| 216 |
+
m1 = "%s: %s" % (m1, str(value))
|
| 217 |
+
except:
|
| 218 |
+
pass
|
| 219 |
+
bg = "yellow"
|
| 220 |
+
else:
|
| 221 |
+
m1 = ""
|
| 222 |
+
tb = None
|
| 223 |
+
bg = self.errorbg
|
| 224 |
+
self.error.configure(text=m1, background=bg)
|
| 225 |
+
#
|
| 226 |
+
sv = self.stackviewer
|
| 227 |
+
if sv:
|
| 228 |
+
stack, i = self.idb.get_stack(self.frame, tb)
|
| 229 |
+
sv.load_stack(stack, i)
|
| 230 |
+
#
|
| 231 |
+
self.show_variables(1)
|
| 232 |
+
#
|
| 233 |
+
if self.vsource.get():
|
| 234 |
+
self.sync_source_line()
|
| 235 |
+
#
|
| 236 |
+
for b in self.buttons:
|
| 237 |
+
b.configure(state="normal")
|
| 238 |
+
#
|
| 239 |
+
self.top.wakeup()
|
| 240 |
+
# Nested main loop: Tkinter's main loop is not reentrant, so use
|
| 241 |
+
# Tcl's vwait facility, which reenters the event loop until an
|
| 242 |
+
# event handler sets the variable we're waiting on
|
| 243 |
+
self.nesting_level += 1
|
| 244 |
+
self.root.tk.call('vwait', '::idledebugwait')
|
| 245 |
+
self.nesting_level -= 1
|
| 246 |
+
#
|
| 247 |
+
for b in self.buttons:
|
| 248 |
+
b.configure(state="disabled")
|
| 249 |
+
self.status.configure(text="")
|
| 250 |
+
self.error.configure(text="", background=self.errorbg)
|
| 251 |
+
self.frame = None
|
| 252 |
+
|
| 253 |
+
def sync_source_line(self):
|
| 254 |
+
frame = self.frame
|
| 255 |
+
if not frame:
|
| 256 |
+
return
|
| 257 |
+
filename, lineno = self.__frame2fileline(frame)
|
| 258 |
+
if filename[:1] + filename[-1:] != "<>" and os.path.exists(filename):
|
| 259 |
+
self.flist.gotofileline(filename, lineno)
|
| 260 |
+
|
| 261 |
+
def __frame2fileline(self, frame):
|
| 262 |
+
code = frame.f_code
|
| 263 |
+
filename = code.co_filename
|
| 264 |
+
lineno = frame.f_lineno
|
| 265 |
+
return filename, lineno
|
| 266 |
+
|
| 267 |
+
def cont(self):
|
| 268 |
+
self.idb.set_continue()
|
| 269 |
+
self.abort_loop()
|
| 270 |
+
|
| 271 |
+
def step(self):
|
| 272 |
+
self.idb.set_step()
|
| 273 |
+
self.abort_loop()
|
| 274 |
+
|
| 275 |
+
def next(self):
|
| 276 |
+
self.idb.set_next(self.frame)
|
| 277 |
+
self.abort_loop()
|
| 278 |
+
|
| 279 |
+
def ret(self):
|
| 280 |
+
self.idb.set_return(self.frame)
|
| 281 |
+
self.abort_loop()
|
| 282 |
+
|
| 283 |
+
def quit(self):
|
| 284 |
+
self.idb.set_quit()
|
| 285 |
+
self.abort_loop()
|
| 286 |
+
|
| 287 |
+
def abort_loop(self):
|
| 288 |
+
self.root.tk.call('set', '::idledebugwait', '1')
|
| 289 |
+
|
| 290 |
+
stackviewer = None
|
| 291 |
+
|
| 292 |
+
def show_stack(self):
|
| 293 |
+
if not self.stackviewer and self.vstack.get():
|
| 294 |
+
self.stackviewer = sv = StackViewer(self.fstack, self.flist, self)
|
| 295 |
+
if self.frame:
|
| 296 |
+
stack, i = self.idb.get_stack(self.frame, None)
|
| 297 |
+
sv.load_stack(stack, i)
|
| 298 |
+
else:
|
| 299 |
+
sv = self.stackviewer
|
| 300 |
+
if sv and not self.vstack.get():
|
| 301 |
+
self.stackviewer = None
|
| 302 |
+
sv.close()
|
| 303 |
+
self.fstack['height'] = 1
|
| 304 |
+
|
| 305 |
+
def show_source(self):
|
| 306 |
+
if self.vsource.get():
|
| 307 |
+
self.sync_source_line()
|
| 308 |
+
|
| 309 |
+
def show_frame(self, stackitem):
|
| 310 |
+
self.frame = stackitem[0] # lineno is stackitem[1]
|
| 311 |
+
self.show_variables()
|
| 312 |
+
|
| 313 |
+
localsviewer = None
|
| 314 |
+
globalsviewer = None
|
| 315 |
+
|
| 316 |
+
def show_locals(self):
|
| 317 |
+
lv = self.localsviewer
|
| 318 |
+
if self.vlocals.get():
|
| 319 |
+
if not lv:
|
| 320 |
+
self.localsviewer = NamespaceViewer(self.flocals, "Locals")
|
| 321 |
+
else:
|
| 322 |
+
if lv:
|
| 323 |
+
self.localsviewer = None
|
| 324 |
+
lv.close()
|
| 325 |
+
self.flocals['height'] = 1
|
| 326 |
+
self.show_variables()
|
| 327 |
+
|
| 328 |
+
def show_globals(self):
|
| 329 |
+
gv = self.globalsviewer
|
| 330 |
+
if self.vglobals.get():
|
| 331 |
+
if not gv:
|
| 332 |
+
self.globalsviewer = NamespaceViewer(self.fglobals, "Globals")
|
| 333 |
+
else:
|
| 334 |
+
if gv:
|
| 335 |
+
self.globalsviewer = None
|
| 336 |
+
gv.close()
|
| 337 |
+
self.fglobals['height'] = 1
|
| 338 |
+
self.show_variables()
|
| 339 |
+
|
| 340 |
+
def show_variables(self, force=0):
|
| 341 |
+
lv = self.localsviewer
|
| 342 |
+
gv = self.globalsviewer
|
| 343 |
+
frame = self.frame
|
| 344 |
+
if not frame:
|
| 345 |
+
ldict = gdict = None
|
| 346 |
+
else:
|
| 347 |
+
ldict = frame.f_locals
|
| 348 |
+
gdict = frame.f_globals
|
| 349 |
+
if lv and gv and ldict is gdict:
|
| 350 |
+
ldict = None
|
| 351 |
+
if lv:
|
| 352 |
+
lv.load_dict(ldict, force, self.pyshell.interp.rpcclt)
|
| 353 |
+
if gv:
|
| 354 |
+
gv.load_dict(gdict, force, self.pyshell.interp.rpcclt)
|
| 355 |
+
|
| 356 |
+
def set_breakpoint_here(self, filename, lineno):
|
| 357 |
+
self.idb.set_break(filename, lineno)
|
| 358 |
+
|
| 359 |
+
def clear_breakpoint_here(self, filename, lineno):
|
| 360 |
+
self.idb.clear_break(filename, lineno)
|
| 361 |
+
|
| 362 |
+
def clear_file_breaks(self, filename):
|
| 363 |
+
self.idb.clear_all_file_breaks(filename)
|
| 364 |
+
|
| 365 |
+
def load_breakpoints(self):
|
| 366 |
+
"Load PyShellEditorWindow breakpoints into subprocess debugger"
|
| 367 |
+
for editwin in self.pyshell.flist.inversedict:
|
| 368 |
+
filename = editwin.io.filename
|
| 369 |
+
try:
|
| 370 |
+
for lineno in editwin.breakpoints:
|
| 371 |
+
self.set_breakpoint_here(filename, lineno)
|
| 372 |
+
except AttributeError:
|
| 373 |
+
continue
|
| 374 |
+
|
| 375 |
+
class StackViewer(ScrolledList):
|
| 376 |
+
|
| 377 |
+
def __init__(self, master, flist, gui):
|
| 378 |
+
if macosx.isAquaTk():
|
| 379 |
+
# At least on with the stock AquaTk version on OSX 10.4 you'll
|
| 380 |
+
# get a shaking GUI that eventually kills IDLE if the width
|
| 381 |
+
# argument is specified.
|
| 382 |
+
ScrolledList.__init__(self, master)
|
| 383 |
+
else:
|
| 384 |
+
ScrolledList.__init__(self, master, width=80)
|
| 385 |
+
self.flist = flist
|
| 386 |
+
self.gui = gui
|
| 387 |
+
self.stack = []
|
| 388 |
+
|
| 389 |
+
def load_stack(self, stack, index=None):
|
| 390 |
+
self.stack = stack
|
| 391 |
+
self.clear()
|
| 392 |
+
for i in range(len(stack)):
|
| 393 |
+
frame, lineno = stack[i]
|
| 394 |
+
try:
|
| 395 |
+
modname = frame.f_globals["__name__"]
|
| 396 |
+
except:
|
| 397 |
+
modname = "?"
|
| 398 |
+
code = frame.f_code
|
| 399 |
+
filename = code.co_filename
|
| 400 |
+
funcname = code.co_name
|
| 401 |
+
import linecache
|
| 402 |
+
sourceline = linecache.getline(filename, lineno)
|
| 403 |
+
sourceline = sourceline.strip()
|
| 404 |
+
if funcname in ("?", "", None):
|
| 405 |
+
item = "%s, line %d: %s" % (modname, lineno, sourceline)
|
| 406 |
+
else:
|
| 407 |
+
item = "%s.%s(), line %d: %s" % (modname, funcname,
|
| 408 |
+
lineno, sourceline)
|
| 409 |
+
if i == index:
|
| 410 |
+
item = "> " + item
|
| 411 |
+
self.append(item)
|
| 412 |
+
if index is not None:
|
| 413 |
+
self.select(index)
|
| 414 |
+
|
| 415 |
+
def popup_event(self, event):
|
| 416 |
+
"override base method"
|
| 417 |
+
if self.stack:
|
| 418 |
+
return ScrolledList.popup_event(self, event)
|
| 419 |
+
|
| 420 |
+
def fill_menu(self):
|
| 421 |
+
"override base method"
|
| 422 |
+
menu = self.menu
|
| 423 |
+
menu.add_command(label="Go to source line",
|
| 424 |
+
command=self.goto_source_line)
|
| 425 |
+
menu.add_command(label="Show stack frame",
|
| 426 |
+
command=self.show_stack_frame)
|
| 427 |
+
|
| 428 |
+
def on_select(self, index):
|
| 429 |
+
"override base method"
|
| 430 |
+
if 0 <= index < len(self.stack):
|
| 431 |
+
self.gui.show_frame(self.stack[index])
|
| 432 |
+
|
| 433 |
+
def on_double(self, index):
|
| 434 |
+
"override base method"
|
| 435 |
+
self.show_source(index)
|
| 436 |
+
|
| 437 |
+
def goto_source_line(self):
|
| 438 |
+
index = self.listbox.index("active")
|
| 439 |
+
self.show_source(index)
|
| 440 |
+
|
| 441 |
+
def show_stack_frame(self):
|
| 442 |
+
index = self.listbox.index("active")
|
| 443 |
+
if 0 <= index < len(self.stack):
|
| 444 |
+
self.gui.show_frame(self.stack[index])
|
| 445 |
+
|
| 446 |
+
def show_source(self, index):
|
| 447 |
+
if not (0 <= index < len(self.stack)):
|
| 448 |
+
return
|
| 449 |
+
frame, lineno = self.stack[index]
|
| 450 |
+
code = frame.f_code
|
| 451 |
+
filename = code.co_filename
|
| 452 |
+
if os.path.isfile(filename):
|
| 453 |
+
edit = self.flist.open(filename)
|
| 454 |
+
if edit:
|
| 455 |
+
edit.gotoline(lineno)
|
| 456 |
+
|
| 457 |
+
|
| 458 |
+
class NamespaceViewer:
|
| 459 |
+
|
| 460 |
+
def __init__(self, master, title, dict=None):
|
| 461 |
+
width = 0
|
| 462 |
+
height = 40
|
| 463 |
+
if dict:
|
| 464 |
+
height = 20*len(dict) # XXX 20 == observed height of Entry widget
|
| 465 |
+
self.master = master
|
| 466 |
+
self.title = title
|
| 467 |
+
import reprlib
|
| 468 |
+
self.repr = reprlib.Repr()
|
| 469 |
+
self.repr.maxstring = 60
|
| 470 |
+
self.repr.maxother = 60
|
| 471 |
+
self.frame = frame = Frame(master)
|
| 472 |
+
self.frame.pack(expand=1, fill="both")
|
| 473 |
+
self.label = Label(frame, text=title, borderwidth=2, relief="groove")
|
| 474 |
+
self.label.pack(fill="x")
|
| 475 |
+
self.vbar = vbar = Scrollbar(frame, name="vbar")
|
| 476 |
+
vbar.pack(side="right", fill="y")
|
| 477 |
+
self.canvas = canvas = Canvas(frame,
|
| 478 |
+
height=min(300, max(40, height)),
|
| 479 |
+
scrollregion=(0, 0, width, height))
|
| 480 |
+
canvas.pack(side="left", fill="both", expand=1)
|
| 481 |
+
vbar["command"] = canvas.yview
|
| 482 |
+
canvas["yscrollcommand"] = vbar.set
|
| 483 |
+
self.subframe = subframe = Frame(canvas)
|
| 484 |
+
self.sfid = canvas.create_window(0, 0, window=subframe, anchor="nw")
|
| 485 |
+
self.load_dict(dict)
|
| 486 |
+
|
| 487 |
+
dict = -1
|
| 488 |
+
|
| 489 |
+
def load_dict(self, dict, force=0, rpc_client=None):
|
| 490 |
+
if dict is self.dict and not force:
|
| 491 |
+
return
|
| 492 |
+
subframe = self.subframe
|
| 493 |
+
frame = self.frame
|
| 494 |
+
for c in list(subframe.children.values()):
|
| 495 |
+
c.destroy()
|
| 496 |
+
self.dict = None
|
| 497 |
+
if not dict:
|
| 498 |
+
l = Label(subframe, text="None")
|
| 499 |
+
l.grid(row=0, column=0)
|
| 500 |
+
else:
|
| 501 |
+
#names = sorted(dict)
|
| 502 |
+
###
|
| 503 |
+
# Because of (temporary) limitations on the dict_keys type (not yet
|
| 504 |
+
# public or pickleable), have the subprocess to send a list of
|
| 505 |
+
# keys, not a dict_keys object. sorted() will take a dict_keys
|
| 506 |
+
# (no subprocess) or a list.
|
| 507 |
+
#
|
| 508 |
+
# There is also an obscure bug in sorted(dict) where the
|
| 509 |
+
# interpreter gets into a loop requesting non-existing dict[0],
|
| 510 |
+
# dict[1], dict[2], etc from the debugger_r.DictProxy.
|
| 511 |
+
###
|
| 512 |
+
keys_list = dict.keys()
|
| 513 |
+
names = sorted(keys_list)
|
| 514 |
+
###
|
| 515 |
+
row = 0
|
| 516 |
+
for name in names:
|
| 517 |
+
value = dict[name]
|
| 518 |
+
svalue = self.repr.repr(value) # repr(value)
|
| 519 |
+
# Strip extra quotes caused by calling repr on the (already)
|
| 520 |
+
# repr'd value sent across the RPC interface:
|
| 521 |
+
if rpc_client:
|
| 522 |
+
svalue = svalue[1:-1]
|
| 523 |
+
l = Label(subframe, text=name)
|
| 524 |
+
l.grid(row=row, column=0, sticky="nw")
|
| 525 |
+
l = Entry(subframe, width=0, borderwidth=0)
|
| 526 |
+
l.insert(0, svalue)
|
| 527 |
+
l.grid(row=row, column=1, sticky="nw")
|
| 528 |
+
row = row+1
|
| 529 |
+
self.dict = dict
|
| 530 |
+
# XXX Could we use a <Configure> callback for the following?
|
| 531 |
+
subframe.update_idletasks() # Alas!
|
| 532 |
+
width = subframe.winfo_reqwidth()
|
| 533 |
+
height = subframe.winfo_reqheight()
|
| 534 |
+
canvas = self.canvas
|
| 535 |
+
self.canvas["scrollregion"] = (0, 0, width, height)
|
| 536 |
+
if height > 300:
|
| 537 |
+
canvas["height"] = 300
|
| 538 |
+
frame.pack(expand=1)
|
| 539 |
+
else:
|
| 540 |
+
canvas["height"] = height
|
| 541 |
+
frame.pack(expand=0)
|
| 542 |
+
|
| 543 |
+
def close(self):
|
| 544 |
+
self.frame.destroy()
|
| 545 |
+
|
| 546 |
+
if __name__ == "__main__":
|
| 547 |
+
from unittest import main
|
| 548 |
+
main('idlelib.idle_test.test_debugger', verbosity=2, exit=False)
|
| 549 |
+
|
| 550 |
+
# TODO: htest?
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/debugger_r.py
ADDED
|
@@ -0,0 +1,393 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Support for remote Python debugging.
|
| 2 |
+
|
| 3 |
+
Some ASCII art to describe the structure:
|
| 4 |
+
|
| 5 |
+
IN PYTHON SUBPROCESS # IN IDLE PROCESS
|
| 6 |
+
#
|
| 7 |
+
# oid='gui_adapter'
|
| 8 |
+
+----------+ # +------------+ +-----+
|
| 9 |
+
| GUIProxy |--remote#call-->| GUIAdapter |--calls-->| GUI |
|
| 10 |
+
+-----+--calls-->+----------+ # +------------+ +-----+
|
| 11 |
+
| Idb | # /
|
| 12 |
+
+-----+<-calls--+------------+ # +----------+<--calls-/
|
| 13 |
+
| IdbAdapter |<--remote#call--| IdbProxy |
|
| 14 |
+
+------------+ # +----------+
|
| 15 |
+
oid='idb_adapter' #
|
| 16 |
+
|
| 17 |
+
The purpose of the Proxy and Adapter classes is to translate certain
|
| 18 |
+
arguments and return values that cannot be transported through the RPC
|
| 19 |
+
barrier, in particular frame and traceback objects.
|
| 20 |
+
|
| 21 |
+
"""
|
| 22 |
+
import reprlib
|
| 23 |
+
import types
|
| 24 |
+
from idlelib import debugger
|
| 25 |
+
|
| 26 |
+
debugging = 0
|
| 27 |
+
|
| 28 |
+
idb_adap_oid = "idb_adapter"
|
| 29 |
+
gui_adap_oid = "gui_adapter"
|
| 30 |
+
|
| 31 |
+
#=======================================
|
| 32 |
+
#
|
| 33 |
+
# In the PYTHON subprocess:
|
| 34 |
+
|
| 35 |
+
frametable = {}
|
| 36 |
+
dicttable = {}
|
| 37 |
+
codetable = {}
|
| 38 |
+
tracebacktable = {}
|
| 39 |
+
|
| 40 |
+
def wrap_frame(frame):
|
| 41 |
+
fid = id(frame)
|
| 42 |
+
frametable[fid] = frame
|
| 43 |
+
return fid
|
| 44 |
+
|
| 45 |
+
def wrap_info(info):
|
| 46 |
+
"replace info[2], a traceback instance, by its ID"
|
| 47 |
+
if info is None:
|
| 48 |
+
return None
|
| 49 |
+
else:
|
| 50 |
+
traceback = info[2]
|
| 51 |
+
assert isinstance(traceback, types.TracebackType)
|
| 52 |
+
traceback_id = id(traceback)
|
| 53 |
+
tracebacktable[traceback_id] = traceback
|
| 54 |
+
modified_info = (info[0], info[1], traceback_id)
|
| 55 |
+
return modified_info
|
| 56 |
+
|
| 57 |
+
class GUIProxy:
|
| 58 |
+
|
| 59 |
+
def __init__(self, conn, gui_adap_oid):
|
| 60 |
+
self.conn = conn
|
| 61 |
+
self.oid = gui_adap_oid
|
| 62 |
+
|
| 63 |
+
def interaction(self, message, frame, info=None):
|
| 64 |
+
# calls rpc.SocketIO.remotecall() via run.MyHandler instance
|
| 65 |
+
# pass frame and traceback object IDs instead of the objects themselves
|
| 66 |
+
self.conn.remotecall(self.oid, "interaction",
|
| 67 |
+
(message, wrap_frame(frame), wrap_info(info)),
|
| 68 |
+
{})
|
| 69 |
+
|
| 70 |
+
class IdbAdapter:
|
| 71 |
+
|
| 72 |
+
def __init__(self, idb):
|
| 73 |
+
self.idb = idb
|
| 74 |
+
|
| 75 |
+
#----------called by an IdbProxy----------
|
| 76 |
+
|
| 77 |
+
def set_step(self):
|
| 78 |
+
self.idb.set_step()
|
| 79 |
+
|
| 80 |
+
def set_quit(self):
|
| 81 |
+
self.idb.set_quit()
|
| 82 |
+
|
| 83 |
+
def set_continue(self):
|
| 84 |
+
self.idb.set_continue()
|
| 85 |
+
|
| 86 |
+
def set_next(self, fid):
|
| 87 |
+
frame = frametable[fid]
|
| 88 |
+
self.idb.set_next(frame)
|
| 89 |
+
|
| 90 |
+
def set_return(self, fid):
|
| 91 |
+
frame = frametable[fid]
|
| 92 |
+
self.idb.set_return(frame)
|
| 93 |
+
|
| 94 |
+
def get_stack(self, fid, tbid):
|
| 95 |
+
frame = frametable[fid]
|
| 96 |
+
if tbid is None:
|
| 97 |
+
tb = None
|
| 98 |
+
else:
|
| 99 |
+
tb = tracebacktable[tbid]
|
| 100 |
+
stack, i = self.idb.get_stack(frame, tb)
|
| 101 |
+
stack = [(wrap_frame(frame2), k) for frame2, k in stack]
|
| 102 |
+
return stack, i
|
| 103 |
+
|
| 104 |
+
def run(self, cmd):
|
| 105 |
+
import __main__
|
| 106 |
+
self.idb.run(cmd, __main__.__dict__)
|
| 107 |
+
|
| 108 |
+
def set_break(self, filename, lineno):
|
| 109 |
+
msg = self.idb.set_break(filename, lineno)
|
| 110 |
+
return msg
|
| 111 |
+
|
| 112 |
+
def clear_break(self, filename, lineno):
|
| 113 |
+
msg = self.idb.clear_break(filename, lineno)
|
| 114 |
+
return msg
|
| 115 |
+
|
| 116 |
+
def clear_all_file_breaks(self, filename):
|
| 117 |
+
msg = self.idb.clear_all_file_breaks(filename)
|
| 118 |
+
return msg
|
| 119 |
+
|
| 120 |
+
#----------called by a FrameProxy----------
|
| 121 |
+
|
| 122 |
+
def frame_attr(self, fid, name):
|
| 123 |
+
frame = frametable[fid]
|
| 124 |
+
return getattr(frame, name)
|
| 125 |
+
|
| 126 |
+
def frame_globals(self, fid):
|
| 127 |
+
frame = frametable[fid]
|
| 128 |
+
dict = frame.f_globals
|
| 129 |
+
did = id(dict)
|
| 130 |
+
dicttable[did] = dict
|
| 131 |
+
return did
|
| 132 |
+
|
| 133 |
+
def frame_locals(self, fid):
|
| 134 |
+
frame = frametable[fid]
|
| 135 |
+
dict = frame.f_locals
|
| 136 |
+
did = id(dict)
|
| 137 |
+
dicttable[did] = dict
|
| 138 |
+
return did
|
| 139 |
+
|
| 140 |
+
def frame_code(self, fid):
|
| 141 |
+
frame = frametable[fid]
|
| 142 |
+
code = frame.f_code
|
| 143 |
+
cid = id(code)
|
| 144 |
+
codetable[cid] = code
|
| 145 |
+
return cid
|
| 146 |
+
|
| 147 |
+
#----------called by a CodeProxy----------
|
| 148 |
+
|
| 149 |
+
def code_name(self, cid):
|
| 150 |
+
code = codetable[cid]
|
| 151 |
+
return code.co_name
|
| 152 |
+
|
| 153 |
+
def code_filename(self, cid):
|
| 154 |
+
code = codetable[cid]
|
| 155 |
+
return code.co_filename
|
| 156 |
+
|
| 157 |
+
#----------called by a DictProxy----------
|
| 158 |
+
|
| 159 |
+
def dict_keys(self, did):
|
| 160 |
+
raise NotImplementedError("dict_keys not public or pickleable")
|
| 161 |
+
## dict = dicttable[did]
|
| 162 |
+
## return dict.keys()
|
| 163 |
+
|
| 164 |
+
### Needed until dict_keys is type is finished and pickealable.
|
| 165 |
+
### Will probably need to extend rpc.py:SocketIO._proxify at that time.
|
| 166 |
+
def dict_keys_list(self, did):
|
| 167 |
+
dict = dicttable[did]
|
| 168 |
+
return list(dict.keys())
|
| 169 |
+
|
| 170 |
+
def dict_item(self, did, key):
|
| 171 |
+
dict = dicttable[did]
|
| 172 |
+
value = dict[key]
|
| 173 |
+
value = reprlib.repr(value) ### can't pickle module 'builtins'
|
| 174 |
+
return value
|
| 175 |
+
|
| 176 |
+
#----------end class IdbAdapter----------
|
| 177 |
+
|
| 178 |
+
|
| 179 |
+
def start_debugger(rpchandler, gui_adap_oid):
|
| 180 |
+
"""Start the debugger and its RPC link in the Python subprocess
|
| 181 |
+
|
| 182 |
+
Start the subprocess side of the split debugger and set up that side of the
|
| 183 |
+
RPC link by instantiating the GUIProxy, Idb debugger, and IdbAdapter
|
| 184 |
+
objects and linking them together. Register the IdbAdapter with the
|
| 185 |
+
RPCServer to handle RPC requests from the split debugger GUI via the
|
| 186 |
+
IdbProxy.
|
| 187 |
+
|
| 188 |
+
"""
|
| 189 |
+
gui_proxy = GUIProxy(rpchandler, gui_adap_oid)
|
| 190 |
+
idb = debugger.Idb(gui_proxy)
|
| 191 |
+
idb_adap = IdbAdapter(idb)
|
| 192 |
+
rpchandler.register(idb_adap_oid, idb_adap)
|
| 193 |
+
return idb_adap_oid
|
| 194 |
+
|
| 195 |
+
|
| 196 |
+
#=======================================
|
| 197 |
+
#
|
| 198 |
+
# In the IDLE process:
|
| 199 |
+
|
| 200 |
+
|
| 201 |
+
class FrameProxy:
|
| 202 |
+
|
| 203 |
+
def __init__(self, conn, fid):
|
| 204 |
+
self._conn = conn
|
| 205 |
+
self._fid = fid
|
| 206 |
+
self._oid = "idb_adapter"
|
| 207 |
+
self._dictcache = {}
|
| 208 |
+
|
| 209 |
+
def __getattr__(self, name):
|
| 210 |
+
if name[:1] == "_":
|
| 211 |
+
raise AttributeError(name)
|
| 212 |
+
if name == "f_code":
|
| 213 |
+
return self._get_f_code()
|
| 214 |
+
if name == "f_globals":
|
| 215 |
+
return self._get_f_globals()
|
| 216 |
+
if name == "f_locals":
|
| 217 |
+
return self._get_f_locals()
|
| 218 |
+
return self._conn.remotecall(self._oid, "frame_attr",
|
| 219 |
+
(self._fid, name), {})
|
| 220 |
+
|
| 221 |
+
def _get_f_code(self):
|
| 222 |
+
cid = self._conn.remotecall(self._oid, "frame_code", (self._fid,), {})
|
| 223 |
+
return CodeProxy(self._conn, self._oid, cid)
|
| 224 |
+
|
| 225 |
+
def _get_f_globals(self):
|
| 226 |
+
did = self._conn.remotecall(self._oid, "frame_globals",
|
| 227 |
+
(self._fid,), {})
|
| 228 |
+
return self._get_dict_proxy(did)
|
| 229 |
+
|
| 230 |
+
def _get_f_locals(self):
|
| 231 |
+
did = self._conn.remotecall(self._oid, "frame_locals",
|
| 232 |
+
(self._fid,), {})
|
| 233 |
+
return self._get_dict_proxy(did)
|
| 234 |
+
|
| 235 |
+
def _get_dict_proxy(self, did):
|
| 236 |
+
if did in self._dictcache:
|
| 237 |
+
return self._dictcache[did]
|
| 238 |
+
dp = DictProxy(self._conn, self._oid, did)
|
| 239 |
+
self._dictcache[did] = dp
|
| 240 |
+
return dp
|
| 241 |
+
|
| 242 |
+
|
| 243 |
+
class CodeProxy:
|
| 244 |
+
|
| 245 |
+
def __init__(self, conn, oid, cid):
|
| 246 |
+
self._conn = conn
|
| 247 |
+
self._oid = oid
|
| 248 |
+
self._cid = cid
|
| 249 |
+
|
| 250 |
+
def __getattr__(self, name):
|
| 251 |
+
if name == "co_name":
|
| 252 |
+
return self._conn.remotecall(self._oid, "code_name",
|
| 253 |
+
(self._cid,), {})
|
| 254 |
+
if name == "co_filename":
|
| 255 |
+
return self._conn.remotecall(self._oid, "code_filename",
|
| 256 |
+
(self._cid,), {})
|
| 257 |
+
|
| 258 |
+
|
| 259 |
+
class DictProxy:
|
| 260 |
+
|
| 261 |
+
def __init__(self, conn, oid, did):
|
| 262 |
+
self._conn = conn
|
| 263 |
+
self._oid = oid
|
| 264 |
+
self._did = did
|
| 265 |
+
|
| 266 |
+
## def keys(self):
|
| 267 |
+
## return self._conn.remotecall(self._oid, "dict_keys", (self._did,), {})
|
| 268 |
+
|
| 269 |
+
# 'temporary' until dict_keys is a pickleable built-in type
|
| 270 |
+
def keys(self):
|
| 271 |
+
return self._conn.remotecall(self._oid,
|
| 272 |
+
"dict_keys_list", (self._did,), {})
|
| 273 |
+
|
| 274 |
+
def __getitem__(self, key):
|
| 275 |
+
return self._conn.remotecall(self._oid, "dict_item",
|
| 276 |
+
(self._did, key), {})
|
| 277 |
+
|
| 278 |
+
def __getattr__(self, name):
|
| 279 |
+
##print("*** Failed DictProxy.__getattr__:", name)
|
| 280 |
+
raise AttributeError(name)
|
| 281 |
+
|
| 282 |
+
|
| 283 |
+
class GUIAdapter:
|
| 284 |
+
|
| 285 |
+
def __init__(self, conn, gui):
|
| 286 |
+
self.conn = conn
|
| 287 |
+
self.gui = gui
|
| 288 |
+
|
| 289 |
+
def interaction(self, message, fid, modified_info):
|
| 290 |
+
##print("*** Interaction: (%s, %s, %s)" % (message, fid, modified_info))
|
| 291 |
+
frame = FrameProxy(self.conn, fid)
|
| 292 |
+
self.gui.interaction(message, frame, modified_info)
|
| 293 |
+
|
| 294 |
+
|
| 295 |
+
class IdbProxy:
|
| 296 |
+
|
| 297 |
+
def __init__(self, conn, shell, oid):
|
| 298 |
+
self.oid = oid
|
| 299 |
+
self.conn = conn
|
| 300 |
+
self.shell = shell
|
| 301 |
+
|
| 302 |
+
def call(self, methodname, /, *args, **kwargs):
|
| 303 |
+
##print("*** IdbProxy.call %s %s %s" % (methodname, args, kwargs))
|
| 304 |
+
value = self.conn.remotecall(self.oid, methodname, args, kwargs)
|
| 305 |
+
##print("*** IdbProxy.call %s returns %r" % (methodname, value))
|
| 306 |
+
return value
|
| 307 |
+
|
| 308 |
+
def run(self, cmd, locals):
|
| 309 |
+
# Ignores locals on purpose!
|
| 310 |
+
seq = self.conn.asyncqueue(self.oid, "run", (cmd,), {})
|
| 311 |
+
self.shell.interp.active_seq = seq
|
| 312 |
+
|
| 313 |
+
def get_stack(self, frame, tbid):
|
| 314 |
+
# passing frame and traceback IDs, not the objects themselves
|
| 315 |
+
stack, i = self.call("get_stack", frame._fid, tbid)
|
| 316 |
+
stack = [(FrameProxy(self.conn, fid), k) for fid, k in stack]
|
| 317 |
+
return stack, i
|
| 318 |
+
|
| 319 |
+
def set_continue(self):
|
| 320 |
+
self.call("set_continue")
|
| 321 |
+
|
| 322 |
+
def set_step(self):
|
| 323 |
+
self.call("set_step")
|
| 324 |
+
|
| 325 |
+
def set_next(self, frame):
|
| 326 |
+
self.call("set_next", frame._fid)
|
| 327 |
+
|
| 328 |
+
def set_return(self, frame):
|
| 329 |
+
self.call("set_return", frame._fid)
|
| 330 |
+
|
| 331 |
+
def set_quit(self):
|
| 332 |
+
self.call("set_quit")
|
| 333 |
+
|
| 334 |
+
def set_break(self, filename, lineno):
|
| 335 |
+
msg = self.call("set_break", filename, lineno)
|
| 336 |
+
return msg
|
| 337 |
+
|
| 338 |
+
def clear_break(self, filename, lineno):
|
| 339 |
+
msg = self.call("clear_break", filename, lineno)
|
| 340 |
+
return msg
|
| 341 |
+
|
| 342 |
+
def clear_all_file_breaks(self, filename):
|
| 343 |
+
msg = self.call("clear_all_file_breaks", filename)
|
| 344 |
+
return msg
|
| 345 |
+
|
| 346 |
+
def start_remote_debugger(rpcclt, pyshell):
|
| 347 |
+
"""Start the subprocess debugger, initialize the debugger GUI and RPC link
|
| 348 |
+
|
| 349 |
+
Request the RPCServer start the Python subprocess debugger and link. Set
|
| 350 |
+
up the Idle side of the split debugger by instantiating the IdbProxy,
|
| 351 |
+
debugger GUI, and debugger GUIAdapter objects and linking them together.
|
| 352 |
+
|
| 353 |
+
Register the GUIAdapter with the RPCClient to handle debugger GUI
|
| 354 |
+
interaction requests coming from the subprocess debugger via the GUIProxy.
|
| 355 |
+
|
| 356 |
+
The IdbAdapter will pass execution and environment requests coming from the
|
| 357 |
+
Idle debugger GUI to the subprocess debugger via the IdbProxy.
|
| 358 |
+
|
| 359 |
+
"""
|
| 360 |
+
global idb_adap_oid
|
| 361 |
+
|
| 362 |
+
idb_adap_oid = rpcclt.remotecall("exec", "start_the_debugger",\
|
| 363 |
+
(gui_adap_oid,), {})
|
| 364 |
+
idb_proxy = IdbProxy(rpcclt, pyshell, idb_adap_oid)
|
| 365 |
+
gui = debugger.Debugger(pyshell, idb_proxy)
|
| 366 |
+
gui_adap = GUIAdapter(rpcclt, gui)
|
| 367 |
+
rpcclt.register(gui_adap_oid, gui_adap)
|
| 368 |
+
return gui
|
| 369 |
+
|
| 370 |
+
def close_remote_debugger(rpcclt):
|
| 371 |
+
"""Shut down subprocess debugger and Idle side of debugger RPC link
|
| 372 |
+
|
| 373 |
+
Request that the RPCServer shut down the subprocess debugger and link.
|
| 374 |
+
Unregister the GUIAdapter, which will cause a GC on the Idle process
|
| 375 |
+
debugger and RPC link objects. (The second reference to the debugger GUI
|
| 376 |
+
is deleted in pyshell.close_remote_debugger().)
|
| 377 |
+
|
| 378 |
+
"""
|
| 379 |
+
close_subprocess_debugger(rpcclt)
|
| 380 |
+
rpcclt.unregister(gui_adap_oid)
|
| 381 |
+
|
| 382 |
+
def close_subprocess_debugger(rpcclt):
|
| 383 |
+
rpcclt.remotecall("exec", "stop_the_debugger", (idb_adap_oid,), {})
|
| 384 |
+
|
| 385 |
+
def restart_subprocess_debugger(rpcclt):
|
| 386 |
+
idb_adap_oid_ret = rpcclt.remotecall("exec", "start_the_debugger",\
|
| 387 |
+
(gui_adap_oid,), {})
|
| 388 |
+
assert idb_adap_oid_ret == idb_adap_oid, 'Idb restarted with different oid'
|
| 389 |
+
|
| 390 |
+
|
| 391 |
+
if __name__ == "__main__":
|
| 392 |
+
from unittest import main
|
| 393 |
+
main('idlelib.idle_test.test_debugger_r', verbosity=2, exit=False)
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/debugobj.py
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# XXX TO DO:
|
| 2 |
+
# - popup menu
|
| 3 |
+
# - support partial or total redisplay
|
| 4 |
+
# - more doc strings
|
| 5 |
+
# - tooltips
|
| 6 |
+
|
| 7 |
+
# object browser
|
| 8 |
+
|
| 9 |
+
# XXX TO DO:
|
| 10 |
+
# - for classes/modules, add "open source" to object browser
|
| 11 |
+
from reprlib import Repr
|
| 12 |
+
|
| 13 |
+
from idlelib.tree import TreeItem, TreeNode, ScrolledCanvas
|
| 14 |
+
|
| 15 |
+
myrepr = Repr()
|
| 16 |
+
myrepr.maxstring = 100
|
| 17 |
+
myrepr.maxother = 100
|
| 18 |
+
|
| 19 |
+
class ObjectTreeItem(TreeItem):
|
| 20 |
+
def __init__(self, labeltext, object, setfunction=None):
|
| 21 |
+
self.labeltext = labeltext
|
| 22 |
+
self.object = object
|
| 23 |
+
self.setfunction = setfunction
|
| 24 |
+
def GetLabelText(self):
|
| 25 |
+
return self.labeltext
|
| 26 |
+
def GetText(self):
|
| 27 |
+
return myrepr.repr(self.object)
|
| 28 |
+
def GetIconName(self):
|
| 29 |
+
if not self.IsExpandable():
|
| 30 |
+
return "python"
|
| 31 |
+
def IsEditable(self):
|
| 32 |
+
return self.setfunction is not None
|
| 33 |
+
def SetText(self, text):
|
| 34 |
+
try:
|
| 35 |
+
value = eval(text)
|
| 36 |
+
self.setfunction(value)
|
| 37 |
+
except:
|
| 38 |
+
pass
|
| 39 |
+
else:
|
| 40 |
+
self.object = value
|
| 41 |
+
def IsExpandable(self):
|
| 42 |
+
return not not dir(self.object)
|
| 43 |
+
def GetSubList(self):
|
| 44 |
+
keys = dir(self.object)
|
| 45 |
+
sublist = []
|
| 46 |
+
for key in keys:
|
| 47 |
+
try:
|
| 48 |
+
value = getattr(self.object, key)
|
| 49 |
+
except AttributeError:
|
| 50 |
+
continue
|
| 51 |
+
item = make_objecttreeitem(
|
| 52 |
+
str(key) + " =",
|
| 53 |
+
value,
|
| 54 |
+
lambda value, key=key, object=self.object:
|
| 55 |
+
setattr(object, key, value))
|
| 56 |
+
sublist.append(item)
|
| 57 |
+
return sublist
|
| 58 |
+
|
| 59 |
+
class ClassTreeItem(ObjectTreeItem):
|
| 60 |
+
def IsExpandable(self):
|
| 61 |
+
return True
|
| 62 |
+
def GetSubList(self):
|
| 63 |
+
sublist = ObjectTreeItem.GetSubList(self)
|
| 64 |
+
if len(self.object.__bases__) == 1:
|
| 65 |
+
item = make_objecttreeitem("__bases__[0] =",
|
| 66 |
+
self.object.__bases__[0])
|
| 67 |
+
else:
|
| 68 |
+
item = make_objecttreeitem("__bases__ =", self.object.__bases__)
|
| 69 |
+
sublist.insert(0, item)
|
| 70 |
+
return sublist
|
| 71 |
+
|
| 72 |
+
class AtomicObjectTreeItem(ObjectTreeItem):
|
| 73 |
+
def IsExpandable(self):
|
| 74 |
+
return False
|
| 75 |
+
|
| 76 |
+
class SequenceTreeItem(ObjectTreeItem):
|
| 77 |
+
def IsExpandable(self):
|
| 78 |
+
return len(self.object) > 0
|
| 79 |
+
def keys(self):
|
| 80 |
+
return range(len(self.object))
|
| 81 |
+
def GetSubList(self):
|
| 82 |
+
sublist = []
|
| 83 |
+
for key in self.keys():
|
| 84 |
+
try:
|
| 85 |
+
value = self.object[key]
|
| 86 |
+
except KeyError:
|
| 87 |
+
continue
|
| 88 |
+
def setfunction(value, key=key, object=self.object):
|
| 89 |
+
object[key] = value
|
| 90 |
+
item = make_objecttreeitem("%r:" % (key,), value, setfunction)
|
| 91 |
+
sublist.append(item)
|
| 92 |
+
return sublist
|
| 93 |
+
|
| 94 |
+
class DictTreeItem(SequenceTreeItem):
|
| 95 |
+
def keys(self):
|
| 96 |
+
keys = list(self.object.keys())
|
| 97 |
+
try:
|
| 98 |
+
keys.sort()
|
| 99 |
+
except:
|
| 100 |
+
pass
|
| 101 |
+
return keys
|
| 102 |
+
|
| 103 |
+
dispatch = {
|
| 104 |
+
int: AtomicObjectTreeItem,
|
| 105 |
+
float: AtomicObjectTreeItem,
|
| 106 |
+
str: AtomicObjectTreeItem,
|
| 107 |
+
tuple: SequenceTreeItem,
|
| 108 |
+
list: SequenceTreeItem,
|
| 109 |
+
dict: DictTreeItem,
|
| 110 |
+
type: ClassTreeItem,
|
| 111 |
+
}
|
| 112 |
+
|
| 113 |
+
def make_objecttreeitem(labeltext, object, setfunction=None):
|
| 114 |
+
t = type(object)
|
| 115 |
+
if t in dispatch:
|
| 116 |
+
c = dispatch[t]
|
| 117 |
+
else:
|
| 118 |
+
c = ObjectTreeItem
|
| 119 |
+
return c(labeltext, object, setfunction)
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
def _object_browser(parent): # htest #
|
| 123 |
+
import sys
|
| 124 |
+
from tkinter import Toplevel
|
| 125 |
+
top = Toplevel(parent)
|
| 126 |
+
top.title("Test debug object browser")
|
| 127 |
+
x, y = map(int, parent.geometry().split('+')[1:])
|
| 128 |
+
top.geometry("+%d+%d" % (x + 100, y + 175))
|
| 129 |
+
top.configure(bd=0, bg="yellow")
|
| 130 |
+
top.focus_set()
|
| 131 |
+
sc = ScrolledCanvas(top, bg="white", highlightthickness=0, takefocus=1)
|
| 132 |
+
sc.frame.pack(expand=1, fill="both")
|
| 133 |
+
item = make_objecttreeitem("sys", sys)
|
| 134 |
+
node = TreeNode(sc.canvas, None, item)
|
| 135 |
+
node.update()
|
| 136 |
+
|
| 137 |
+
if __name__ == '__main__':
|
| 138 |
+
from unittest import main
|
| 139 |
+
main('idlelib.idle_test.test_debugobj', verbosity=2, exit=False)
|
| 140 |
+
|
| 141 |
+
from idlelib.idle_test.htest import run
|
| 142 |
+
run(_object_browser)
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/debugobj_r.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from idlelib import rpc
|
| 2 |
+
|
| 3 |
+
def remote_object_tree_item(item):
|
| 4 |
+
wrapper = WrappedObjectTreeItem(item)
|
| 5 |
+
oid = id(wrapper)
|
| 6 |
+
rpc.objecttable[oid] = wrapper
|
| 7 |
+
return oid
|
| 8 |
+
|
| 9 |
+
class WrappedObjectTreeItem:
|
| 10 |
+
# Lives in PYTHON subprocess
|
| 11 |
+
|
| 12 |
+
def __init__(self, item):
|
| 13 |
+
self.__item = item
|
| 14 |
+
|
| 15 |
+
def __getattr__(self, name):
|
| 16 |
+
value = getattr(self.__item, name)
|
| 17 |
+
return value
|
| 18 |
+
|
| 19 |
+
def _GetSubList(self):
|
| 20 |
+
sub_list = self.__item._GetSubList()
|
| 21 |
+
return list(map(remote_object_tree_item, sub_list))
|
| 22 |
+
|
| 23 |
+
class StubObjectTreeItem:
|
| 24 |
+
# Lives in IDLE process
|
| 25 |
+
|
| 26 |
+
def __init__(self, sockio, oid):
|
| 27 |
+
self.sockio = sockio
|
| 28 |
+
self.oid = oid
|
| 29 |
+
|
| 30 |
+
def __getattr__(self, name):
|
| 31 |
+
value = rpc.MethodProxy(self.sockio, self.oid, name)
|
| 32 |
+
return value
|
| 33 |
+
|
| 34 |
+
def _GetSubList(self):
|
| 35 |
+
sub_list = self.sockio.remotecall(self.oid, "_GetSubList", (), {})
|
| 36 |
+
return [StubObjectTreeItem(self.sockio, oid) for oid in sub_list]
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
if __name__ == '__main__':
|
| 40 |
+
from unittest import main
|
| 41 |
+
main('idlelib.idle_test.test_debugobj_r', verbosity=2)
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/delegator.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
class Delegator:
|
| 2 |
+
|
| 3 |
+
def __init__(self, delegate=None):
|
| 4 |
+
self.delegate = delegate
|
| 5 |
+
self.__cache = set()
|
| 6 |
+
# Cache is used to only remove added attributes
|
| 7 |
+
# when changing the delegate.
|
| 8 |
+
|
| 9 |
+
def __getattr__(self, name):
|
| 10 |
+
attr = getattr(self.delegate, name) # May raise AttributeError
|
| 11 |
+
setattr(self, name, attr)
|
| 12 |
+
self.__cache.add(name)
|
| 13 |
+
return attr
|
| 14 |
+
|
| 15 |
+
def resetcache(self):
|
| 16 |
+
"Removes added attributes while leaving original attributes."
|
| 17 |
+
# Function is really about resetting delegator dict
|
| 18 |
+
# to original state. Cache is just a means
|
| 19 |
+
for key in self.__cache:
|
| 20 |
+
try:
|
| 21 |
+
delattr(self, key)
|
| 22 |
+
except AttributeError:
|
| 23 |
+
pass
|
| 24 |
+
self.__cache.clear()
|
| 25 |
+
|
| 26 |
+
def setdelegate(self, delegate):
|
| 27 |
+
"Reset attributes and change delegate."
|
| 28 |
+
self.resetcache()
|
| 29 |
+
self.delegate = delegate
|
| 30 |
+
|
| 31 |
+
if __name__ == '__main__':
|
| 32 |
+
from unittest import main
|
| 33 |
+
main('idlelib.idle_test.test_delegator', verbosity=2)
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/editor.py
ADDED
|
@@ -0,0 +1,1672 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import importlib.abc
|
| 2 |
+
import importlib.util
|
| 3 |
+
import os
|
| 4 |
+
import platform
|
| 5 |
+
import re
|
| 6 |
+
import string
|
| 7 |
+
import sys
|
| 8 |
+
import tokenize
|
| 9 |
+
import traceback
|
| 10 |
+
import webbrowser
|
| 11 |
+
|
| 12 |
+
from tkinter import *
|
| 13 |
+
from tkinter.font import Font
|
| 14 |
+
from tkinter.ttk import Scrollbar
|
| 15 |
+
from tkinter import simpledialog
|
| 16 |
+
from tkinter import messagebox
|
| 17 |
+
|
| 18 |
+
from idlelib.config import idleConf
|
| 19 |
+
from idlelib import configdialog
|
| 20 |
+
from idlelib import grep
|
| 21 |
+
from idlelib import help
|
| 22 |
+
from idlelib import help_about
|
| 23 |
+
from idlelib import macosx
|
| 24 |
+
from idlelib.multicall import MultiCallCreator
|
| 25 |
+
from idlelib import pyparse
|
| 26 |
+
from idlelib import query
|
| 27 |
+
from idlelib import replace
|
| 28 |
+
from idlelib import search
|
| 29 |
+
from idlelib.tree import wheel_event
|
| 30 |
+
from idlelib import window
|
| 31 |
+
|
| 32 |
+
# The default tab setting for a Text widget, in average-width characters.
|
| 33 |
+
TK_TABWIDTH_DEFAULT = 8
|
| 34 |
+
_py_version = ' (%s)' % platform.python_version()
|
| 35 |
+
darwin = sys.platform == 'darwin'
|
| 36 |
+
|
| 37 |
+
def _sphinx_version():
|
| 38 |
+
"Format sys.version_info to produce the Sphinx version string used to install the chm docs"
|
| 39 |
+
major, minor, micro, level, serial = sys.version_info
|
| 40 |
+
release = '%s%s' % (major, minor)
|
| 41 |
+
release += '%s' % (micro,)
|
| 42 |
+
if level == 'candidate':
|
| 43 |
+
release += 'rc%s' % (serial,)
|
| 44 |
+
elif level != 'final':
|
| 45 |
+
release += '%s%s' % (level[0], serial)
|
| 46 |
+
return release
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
class EditorWindow:
|
| 50 |
+
from idlelib.percolator import Percolator
|
| 51 |
+
from idlelib.colorizer import ColorDelegator, color_config
|
| 52 |
+
from idlelib.undo import UndoDelegator
|
| 53 |
+
from idlelib.iomenu import IOBinding, encoding
|
| 54 |
+
from idlelib import mainmenu
|
| 55 |
+
from idlelib.statusbar import MultiStatusBar
|
| 56 |
+
from idlelib.autocomplete import AutoComplete
|
| 57 |
+
from idlelib.autoexpand import AutoExpand
|
| 58 |
+
from idlelib.calltip import Calltip
|
| 59 |
+
from idlelib.codecontext import CodeContext
|
| 60 |
+
from idlelib.sidebar import LineNumbers
|
| 61 |
+
from idlelib.format import FormatParagraph, FormatRegion, Indents, Rstrip
|
| 62 |
+
from idlelib.parenmatch import ParenMatch
|
| 63 |
+
from idlelib.squeezer import Squeezer
|
| 64 |
+
from idlelib.zoomheight import ZoomHeight
|
| 65 |
+
|
| 66 |
+
filesystemencoding = sys.getfilesystemencoding() # for file names
|
| 67 |
+
help_url = None
|
| 68 |
+
|
| 69 |
+
allow_code_context = True
|
| 70 |
+
allow_line_numbers = True
|
| 71 |
+
|
| 72 |
+
def __init__(self, flist=None, filename=None, key=None, root=None):
|
| 73 |
+
# Delay import: runscript imports pyshell imports EditorWindow.
|
| 74 |
+
from idlelib.runscript import ScriptBinding
|
| 75 |
+
|
| 76 |
+
if EditorWindow.help_url is None:
|
| 77 |
+
dochome = os.path.join(sys.base_prefix, 'Doc', 'index.html')
|
| 78 |
+
if sys.platform.count('linux'):
|
| 79 |
+
# look for html docs in a couple of standard places
|
| 80 |
+
pyver = 'python-docs-' + '%s.%s.%s' % sys.version_info[:3]
|
| 81 |
+
if os.path.isdir('/var/www/html/python/'): # "python2" rpm
|
| 82 |
+
dochome = '/var/www/html/python/index.html'
|
| 83 |
+
else:
|
| 84 |
+
basepath = '/usr/share/doc/' # standard location
|
| 85 |
+
dochome = os.path.join(basepath, pyver,
|
| 86 |
+
'Doc', 'index.html')
|
| 87 |
+
elif sys.platform[:3] == 'win':
|
| 88 |
+
chmfile = os.path.join(sys.base_prefix, 'Doc',
|
| 89 |
+
'Python%s.chm' % _sphinx_version())
|
| 90 |
+
if os.path.isfile(chmfile):
|
| 91 |
+
dochome = chmfile
|
| 92 |
+
elif sys.platform == 'darwin':
|
| 93 |
+
# documentation may be stored inside a python framework
|
| 94 |
+
dochome = os.path.join(sys.base_prefix,
|
| 95 |
+
'Resources/English.lproj/Documentation/index.html')
|
| 96 |
+
dochome = os.path.normpath(dochome)
|
| 97 |
+
if os.path.isfile(dochome):
|
| 98 |
+
EditorWindow.help_url = dochome
|
| 99 |
+
if sys.platform == 'darwin':
|
| 100 |
+
# Safari requires real file:-URLs
|
| 101 |
+
EditorWindow.help_url = 'file://' + EditorWindow.help_url
|
| 102 |
+
else:
|
| 103 |
+
EditorWindow.help_url = ("https://docs.python.org/%d.%d/"
|
| 104 |
+
% sys.version_info[:2])
|
| 105 |
+
self.flist = flist
|
| 106 |
+
root = root or flist.root
|
| 107 |
+
self.root = root
|
| 108 |
+
self.menubar = Menu(root)
|
| 109 |
+
self.top = top = window.ListedToplevel(root, menu=self.menubar)
|
| 110 |
+
if flist:
|
| 111 |
+
self.tkinter_vars = flist.vars
|
| 112 |
+
#self.top.instance_dict makes flist.inversedict available to
|
| 113 |
+
#configdialog.py so it can access all EditorWindow instances
|
| 114 |
+
self.top.instance_dict = flist.inversedict
|
| 115 |
+
else:
|
| 116 |
+
self.tkinter_vars = {} # keys: Tkinter event names
|
| 117 |
+
# values: Tkinter variable instances
|
| 118 |
+
self.top.instance_dict = {}
|
| 119 |
+
self.recent_files_path = idleConf.userdir and os.path.join(
|
| 120 |
+
idleConf.userdir, 'recent-files.lst')
|
| 121 |
+
|
| 122 |
+
self.prompt_last_line = '' # Override in PyShell
|
| 123 |
+
self.text_frame = text_frame = Frame(top)
|
| 124 |
+
self.vbar = vbar = Scrollbar(text_frame, name='vbar')
|
| 125 |
+
width = idleConf.GetOption('main', 'EditorWindow', 'width', type='int')
|
| 126 |
+
text_options = {
|
| 127 |
+
'name': 'text',
|
| 128 |
+
'padx': 5,
|
| 129 |
+
'wrap': 'none',
|
| 130 |
+
'highlightthickness': 0,
|
| 131 |
+
'width': width,
|
| 132 |
+
'tabstyle': 'wordprocessor', # new in 8.5
|
| 133 |
+
'height': idleConf.GetOption(
|
| 134 |
+
'main', 'EditorWindow', 'height', type='int'),
|
| 135 |
+
}
|
| 136 |
+
self.text = text = MultiCallCreator(Text)(text_frame, **text_options)
|
| 137 |
+
self.top.focused_widget = self.text
|
| 138 |
+
|
| 139 |
+
self.createmenubar()
|
| 140 |
+
self.apply_bindings()
|
| 141 |
+
|
| 142 |
+
self.top.protocol("WM_DELETE_WINDOW", self.close)
|
| 143 |
+
self.top.bind("<<close-window>>", self.close_event)
|
| 144 |
+
if macosx.isAquaTk():
|
| 145 |
+
# Command-W on editor windows doesn't work without this.
|
| 146 |
+
text.bind('<<close-window>>', self.close_event)
|
| 147 |
+
# Some OS X systems have only one mouse button, so use
|
| 148 |
+
# control-click for popup context menus there. For two
|
| 149 |
+
# buttons, AquaTk defines <2> as the right button, not <3>.
|
| 150 |
+
text.bind("<Control-Button-1>",self.right_menu_event)
|
| 151 |
+
text.bind("<2>", self.right_menu_event)
|
| 152 |
+
else:
|
| 153 |
+
# Elsewhere, use right-click for popup menus.
|
| 154 |
+
text.bind("<3>",self.right_menu_event)
|
| 155 |
+
|
| 156 |
+
text.bind('<MouseWheel>', wheel_event)
|
| 157 |
+
text.bind('<Button-4>', wheel_event)
|
| 158 |
+
text.bind('<Button-5>', wheel_event)
|
| 159 |
+
text.bind('<Configure>', self.handle_winconfig)
|
| 160 |
+
text.bind("<<cut>>", self.cut)
|
| 161 |
+
text.bind("<<copy>>", self.copy)
|
| 162 |
+
text.bind("<<paste>>", self.paste)
|
| 163 |
+
text.bind("<<center-insert>>", self.center_insert_event)
|
| 164 |
+
text.bind("<<help>>", self.help_dialog)
|
| 165 |
+
text.bind("<<python-docs>>", self.python_docs)
|
| 166 |
+
text.bind("<<about-idle>>", self.about_dialog)
|
| 167 |
+
text.bind("<<open-config-dialog>>", self.config_dialog)
|
| 168 |
+
text.bind("<<open-module>>", self.open_module_event)
|
| 169 |
+
text.bind("<<do-nothing>>", lambda event: "break")
|
| 170 |
+
text.bind("<<select-all>>", self.select_all)
|
| 171 |
+
text.bind("<<remove-selection>>", self.remove_selection)
|
| 172 |
+
text.bind("<<find>>", self.find_event)
|
| 173 |
+
text.bind("<<find-again>>", self.find_again_event)
|
| 174 |
+
text.bind("<<find-in-files>>", self.find_in_files_event)
|
| 175 |
+
text.bind("<<find-selection>>", self.find_selection_event)
|
| 176 |
+
text.bind("<<replace>>", self.replace_event)
|
| 177 |
+
text.bind("<<goto-line>>", self.goto_line_event)
|
| 178 |
+
text.bind("<<smart-backspace>>",self.smart_backspace_event)
|
| 179 |
+
text.bind("<<newline-and-indent>>",self.newline_and_indent_event)
|
| 180 |
+
text.bind("<<smart-indent>>",self.smart_indent_event)
|
| 181 |
+
self.fregion = fregion = self.FormatRegion(self)
|
| 182 |
+
# self.fregion used in smart_indent_event to access indent_region.
|
| 183 |
+
text.bind("<<indent-region>>", fregion.indent_region_event)
|
| 184 |
+
text.bind("<<dedent-region>>", fregion.dedent_region_event)
|
| 185 |
+
text.bind("<<comment-region>>", fregion.comment_region_event)
|
| 186 |
+
text.bind("<<uncomment-region>>", fregion.uncomment_region_event)
|
| 187 |
+
text.bind("<<tabify-region>>", fregion.tabify_region_event)
|
| 188 |
+
text.bind("<<untabify-region>>", fregion.untabify_region_event)
|
| 189 |
+
indents = self.Indents(self)
|
| 190 |
+
text.bind("<<toggle-tabs>>", indents.toggle_tabs_event)
|
| 191 |
+
text.bind("<<change-indentwidth>>", indents.change_indentwidth_event)
|
| 192 |
+
text.bind("<Left>", self.move_at_edge_if_selection(0))
|
| 193 |
+
text.bind("<Right>", self.move_at_edge_if_selection(1))
|
| 194 |
+
text.bind("<<del-word-left>>", self.del_word_left)
|
| 195 |
+
text.bind("<<del-word-right>>", self.del_word_right)
|
| 196 |
+
text.bind("<<beginning-of-line>>", self.home_callback)
|
| 197 |
+
|
| 198 |
+
if flist:
|
| 199 |
+
flist.inversedict[self] = key
|
| 200 |
+
if key:
|
| 201 |
+
flist.dict[key] = self
|
| 202 |
+
text.bind("<<open-new-window>>", self.new_callback)
|
| 203 |
+
text.bind("<<close-all-windows>>", self.flist.close_all_callback)
|
| 204 |
+
text.bind("<<open-class-browser>>", self.open_module_browser)
|
| 205 |
+
text.bind("<<open-path-browser>>", self.open_path_browser)
|
| 206 |
+
text.bind("<<open-turtle-demo>>", self.open_turtle_demo)
|
| 207 |
+
|
| 208 |
+
self.set_status_bar()
|
| 209 |
+
text_frame.pack(side=LEFT, fill=BOTH, expand=1)
|
| 210 |
+
text_frame.rowconfigure(1, weight=1)
|
| 211 |
+
text_frame.columnconfigure(1, weight=1)
|
| 212 |
+
vbar['command'] = self.handle_yview
|
| 213 |
+
vbar.grid(row=1, column=2, sticky=NSEW)
|
| 214 |
+
text['yscrollcommand'] = vbar.set
|
| 215 |
+
text['font'] = idleConf.GetFont(self.root, 'main', 'EditorWindow')
|
| 216 |
+
text.grid(row=1, column=1, sticky=NSEW)
|
| 217 |
+
text.focus_set()
|
| 218 |
+
self.set_width()
|
| 219 |
+
|
| 220 |
+
# usetabs true -> literal tab characters are used by indent and
|
| 221 |
+
# dedent cmds, possibly mixed with spaces if
|
| 222 |
+
# indentwidth is not a multiple of tabwidth,
|
| 223 |
+
# which will cause Tabnanny to nag!
|
| 224 |
+
# false -> tab characters are converted to spaces by indent
|
| 225 |
+
# and dedent cmds, and ditto TAB keystrokes
|
| 226 |
+
# Although use-spaces=0 can be configured manually in config-main.def,
|
| 227 |
+
# configuration of tabs v. spaces is not supported in the configuration
|
| 228 |
+
# dialog. IDLE promotes the preferred Python indentation: use spaces!
|
| 229 |
+
usespaces = idleConf.GetOption('main', 'Indent',
|
| 230 |
+
'use-spaces', type='bool')
|
| 231 |
+
self.usetabs = not usespaces
|
| 232 |
+
|
| 233 |
+
# tabwidth is the display width of a literal tab character.
|
| 234 |
+
# CAUTION: telling Tk to use anything other than its default
|
| 235 |
+
# tab setting causes it to use an entirely different tabbing algorithm,
|
| 236 |
+
# treating tab stops as fixed distances from the left margin.
|
| 237 |
+
# Nobody expects this, so for now tabwidth should never be changed.
|
| 238 |
+
self.tabwidth = 8 # must remain 8 until Tk is fixed.
|
| 239 |
+
|
| 240 |
+
# indentwidth is the number of screen characters per indent level.
|
| 241 |
+
# The recommended Python indentation is four spaces.
|
| 242 |
+
self.indentwidth = self.tabwidth
|
| 243 |
+
self.set_notabs_indentwidth()
|
| 244 |
+
|
| 245 |
+
# Store the current value of the insertofftime now so we can restore
|
| 246 |
+
# it if needed.
|
| 247 |
+
if not hasattr(idleConf, 'blink_off_time'):
|
| 248 |
+
idleConf.blink_off_time = self.text['insertofftime']
|
| 249 |
+
self.update_cursor_blink()
|
| 250 |
+
|
| 251 |
+
# When searching backwards for a reliable place to begin parsing,
|
| 252 |
+
# first start num_context_lines[0] lines back, then
|
| 253 |
+
# num_context_lines[1] lines back if that didn't work, and so on.
|
| 254 |
+
# The last value should be huge (larger than the # of lines in a
|
| 255 |
+
# conceivable file).
|
| 256 |
+
# Making the initial values larger slows things down more often.
|
| 257 |
+
self.num_context_lines = 50, 500, 5000000
|
| 258 |
+
self.per = per = self.Percolator(text)
|
| 259 |
+
self.undo = undo = self.UndoDelegator()
|
| 260 |
+
per.insertfilter(undo)
|
| 261 |
+
text.undo_block_start = undo.undo_block_start
|
| 262 |
+
text.undo_block_stop = undo.undo_block_stop
|
| 263 |
+
undo.set_saved_change_hook(self.saved_change_hook)
|
| 264 |
+
# IOBinding implements file I/O and printing functionality
|
| 265 |
+
self.io = io = self.IOBinding(self)
|
| 266 |
+
io.set_filename_change_hook(self.filename_change_hook)
|
| 267 |
+
self.good_load = False
|
| 268 |
+
self.set_indentation_params(False)
|
| 269 |
+
self.color = None # initialized below in self.ResetColorizer
|
| 270 |
+
self.code_context = None # optionally initialized later below
|
| 271 |
+
self.line_numbers = None # optionally initialized later below
|
| 272 |
+
if filename:
|
| 273 |
+
if os.path.exists(filename) and not os.path.isdir(filename):
|
| 274 |
+
if io.loadfile(filename):
|
| 275 |
+
self.good_load = True
|
| 276 |
+
is_py_src = self.ispythonsource(filename)
|
| 277 |
+
self.set_indentation_params(is_py_src)
|
| 278 |
+
else:
|
| 279 |
+
io.set_filename(filename)
|
| 280 |
+
self.good_load = True
|
| 281 |
+
|
| 282 |
+
self.ResetColorizer()
|
| 283 |
+
self.saved_change_hook()
|
| 284 |
+
self.update_recent_files_list()
|
| 285 |
+
self.load_extensions()
|
| 286 |
+
menu = self.menudict.get('window')
|
| 287 |
+
if menu:
|
| 288 |
+
end = menu.index("end")
|
| 289 |
+
if end is None:
|
| 290 |
+
end = -1
|
| 291 |
+
if end >= 0:
|
| 292 |
+
menu.add_separator()
|
| 293 |
+
end = end + 1
|
| 294 |
+
self.wmenu_end = end
|
| 295 |
+
window.register_callback(self.postwindowsmenu)
|
| 296 |
+
|
| 297 |
+
# Some abstractions so IDLE extensions are cross-IDE
|
| 298 |
+
self.askinteger = simpledialog.askinteger
|
| 299 |
+
self.askyesno = messagebox.askyesno
|
| 300 |
+
self.showerror = messagebox.showerror
|
| 301 |
+
|
| 302 |
+
# Add pseudoevents for former extension fixed keys.
|
| 303 |
+
# (This probably needs to be done once in the process.)
|
| 304 |
+
text.event_add('<<autocomplete>>', '<Key-Tab>')
|
| 305 |
+
text.event_add('<<try-open-completions>>', '<KeyRelease-period>',
|
| 306 |
+
'<KeyRelease-slash>', '<KeyRelease-backslash>')
|
| 307 |
+
text.event_add('<<try-open-calltip>>', '<KeyRelease-parenleft>')
|
| 308 |
+
text.event_add('<<refresh-calltip>>', '<KeyRelease-parenright>')
|
| 309 |
+
text.event_add('<<paren-closed>>', '<KeyRelease-parenright>',
|
| 310 |
+
'<KeyRelease-bracketright>', '<KeyRelease-braceright>')
|
| 311 |
+
|
| 312 |
+
# Former extension bindings depends on frame.text being packed
|
| 313 |
+
# (called from self.ResetColorizer()).
|
| 314 |
+
autocomplete = self.AutoComplete(self)
|
| 315 |
+
text.bind("<<autocomplete>>", autocomplete.autocomplete_event)
|
| 316 |
+
text.bind("<<try-open-completions>>",
|
| 317 |
+
autocomplete.try_open_completions_event)
|
| 318 |
+
text.bind("<<force-open-completions>>",
|
| 319 |
+
autocomplete.force_open_completions_event)
|
| 320 |
+
text.bind("<<expand-word>>", self.AutoExpand(self).expand_word_event)
|
| 321 |
+
text.bind("<<format-paragraph>>",
|
| 322 |
+
self.FormatParagraph(self).format_paragraph_event)
|
| 323 |
+
parenmatch = self.ParenMatch(self)
|
| 324 |
+
text.bind("<<flash-paren>>", parenmatch.flash_paren_event)
|
| 325 |
+
text.bind("<<paren-closed>>", parenmatch.paren_closed_event)
|
| 326 |
+
scriptbinding = ScriptBinding(self)
|
| 327 |
+
text.bind("<<check-module>>", scriptbinding.check_module_event)
|
| 328 |
+
text.bind("<<run-module>>", scriptbinding.run_module_event)
|
| 329 |
+
text.bind("<<run-custom>>", scriptbinding.run_custom_event)
|
| 330 |
+
text.bind("<<do-rstrip>>", self.Rstrip(self).do_rstrip)
|
| 331 |
+
self.ctip = ctip = self.Calltip(self)
|
| 332 |
+
text.bind("<<try-open-calltip>>", ctip.try_open_calltip_event)
|
| 333 |
+
#refresh-calltip must come after paren-closed to work right
|
| 334 |
+
text.bind("<<refresh-calltip>>", ctip.refresh_calltip_event)
|
| 335 |
+
text.bind("<<force-open-calltip>>", ctip.force_open_calltip_event)
|
| 336 |
+
text.bind("<<zoom-height>>", self.ZoomHeight(self).zoom_height_event)
|
| 337 |
+
if self.allow_code_context:
|
| 338 |
+
self.code_context = self.CodeContext(self)
|
| 339 |
+
text.bind("<<toggle-code-context>>",
|
| 340 |
+
self.code_context.toggle_code_context_event)
|
| 341 |
+
else:
|
| 342 |
+
self.update_menu_state('options', '*ode*ontext', 'disabled')
|
| 343 |
+
if self.allow_line_numbers:
|
| 344 |
+
self.line_numbers = self.LineNumbers(self)
|
| 345 |
+
if idleConf.GetOption('main', 'EditorWindow',
|
| 346 |
+
'line-numbers-default', type='bool'):
|
| 347 |
+
self.toggle_line_numbers_event()
|
| 348 |
+
text.bind("<<toggle-line-numbers>>", self.toggle_line_numbers_event)
|
| 349 |
+
else:
|
| 350 |
+
self.update_menu_state('options', '*ine*umbers', 'disabled')
|
| 351 |
+
|
| 352 |
+
def handle_winconfig(self, event=None):
|
| 353 |
+
self.set_width()
|
| 354 |
+
|
| 355 |
+
def set_width(self):
|
| 356 |
+
text = self.text
|
| 357 |
+
inner_padding = sum(map(text.tk.getint, [text.cget('border'),
|
| 358 |
+
text.cget('padx')]))
|
| 359 |
+
pixel_width = text.winfo_width() - 2 * inner_padding
|
| 360 |
+
|
| 361 |
+
# Divide the width of the Text widget by the font width,
|
| 362 |
+
# which is taken to be the width of '0' (zero).
|
| 363 |
+
# http://www.tcl.tk/man/tcl8.6/TkCmd/text.htm#M21
|
| 364 |
+
zero_char_width = \
|
| 365 |
+
Font(text, font=text.cget('font')).measure('0')
|
| 366 |
+
self.width = pixel_width // zero_char_width
|
| 367 |
+
|
| 368 |
+
def new_callback(self, event):
|
| 369 |
+
dirname, basename = self.io.defaultfilename()
|
| 370 |
+
self.flist.new(dirname)
|
| 371 |
+
return "break"
|
| 372 |
+
|
| 373 |
+
def home_callback(self, event):
|
| 374 |
+
if (event.state & 4) != 0 and event.keysym == "Home":
|
| 375 |
+
# state&4==Control. If <Control-Home>, use the Tk binding.
|
| 376 |
+
return None
|
| 377 |
+
if self.text.index("iomark") and \
|
| 378 |
+
self.text.compare("iomark", "<=", "insert lineend") and \
|
| 379 |
+
self.text.compare("insert linestart", "<=", "iomark"):
|
| 380 |
+
# In Shell on input line, go to just after prompt
|
| 381 |
+
insertpt = int(self.text.index("iomark").split(".")[1])
|
| 382 |
+
else:
|
| 383 |
+
line = self.text.get("insert linestart", "insert lineend")
|
| 384 |
+
for insertpt in range(len(line)):
|
| 385 |
+
if line[insertpt] not in (' ','\t'):
|
| 386 |
+
break
|
| 387 |
+
else:
|
| 388 |
+
insertpt=len(line)
|
| 389 |
+
lineat = int(self.text.index("insert").split('.')[1])
|
| 390 |
+
if insertpt == lineat:
|
| 391 |
+
insertpt = 0
|
| 392 |
+
dest = "insert linestart+"+str(insertpt)+"c"
|
| 393 |
+
if (event.state&1) == 0:
|
| 394 |
+
# shift was not pressed
|
| 395 |
+
self.text.tag_remove("sel", "1.0", "end")
|
| 396 |
+
else:
|
| 397 |
+
if not self.text.index("sel.first"):
|
| 398 |
+
# there was no previous selection
|
| 399 |
+
self.text.mark_set("my_anchor", "insert")
|
| 400 |
+
else:
|
| 401 |
+
if self.text.compare(self.text.index("sel.first"), "<",
|
| 402 |
+
self.text.index("insert")):
|
| 403 |
+
self.text.mark_set("my_anchor", "sel.first") # extend back
|
| 404 |
+
else:
|
| 405 |
+
self.text.mark_set("my_anchor", "sel.last") # extend forward
|
| 406 |
+
first = self.text.index(dest)
|
| 407 |
+
last = self.text.index("my_anchor")
|
| 408 |
+
if self.text.compare(first,">",last):
|
| 409 |
+
first,last = last,first
|
| 410 |
+
self.text.tag_remove("sel", "1.0", "end")
|
| 411 |
+
self.text.tag_add("sel", first, last)
|
| 412 |
+
self.text.mark_set("insert", dest)
|
| 413 |
+
self.text.see("insert")
|
| 414 |
+
return "break"
|
| 415 |
+
|
| 416 |
+
def set_status_bar(self):
|
| 417 |
+
self.status_bar = self.MultiStatusBar(self.top)
|
| 418 |
+
sep = Frame(self.top, height=1, borderwidth=1, background='grey75')
|
| 419 |
+
if sys.platform == "darwin":
|
| 420 |
+
# Insert some padding to avoid obscuring some of the statusbar
|
| 421 |
+
# by the resize widget.
|
| 422 |
+
self.status_bar.set_label('_padding1', ' ', side=RIGHT)
|
| 423 |
+
self.status_bar.set_label('column', 'Col: ?', side=RIGHT)
|
| 424 |
+
self.status_bar.set_label('line', 'Ln: ?', side=RIGHT)
|
| 425 |
+
self.status_bar.pack(side=BOTTOM, fill=X)
|
| 426 |
+
sep.pack(side=BOTTOM, fill=X)
|
| 427 |
+
self.text.bind("<<set-line-and-column>>", self.set_line_and_column)
|
| 428 |
+
self.text.event_add("<<set-line-and-column>>",
|
| 429 |
+
"<KeyRelease>", "<ButtonRelease>")
|
| 430 |
+
self.text.after_idle(self.set_line_and_column)
|
| 431 |
+
|
| 432 |
+
def set_line_and_column(self, event=None):
|
| 433 |
+
line, column = self.text.index(INSERT).split('.')
|
| 434 |
+
self.status_bar.set_label('column', 'Col: %s' % column)
|
| 435 |
+
self.status_bar.set_label('line', 'Ln: %s' % line)
|
| 436 |
+
|
| 437 |
+
menu_specs = [
|
| 438 |
+
("file", "_File"),
|
| 439 |
+
("edit", "_Edit"),
|
| 440 |
+
("format", "F_ormat"),
|
| 441 |
+
("run", "_Run"),
|
| 442 |
+
("options", "_Options"),
|
| 443 |
+
("window", "_Window"),
|
| 444 |
+
("help", "_Help"),
|
| 445 |
+
]
|
| 446 |
+
|
| 447 |
+
|
| 448 |
+
def createmenubar(self):
|
| 449 |
+
mbar = self.menubar
|
| 450 |
+
self.menudict = menudict = {}
|
| 451 |
+
for name, label in self.menu_specs:
|
| 452 |
+
underline, label = prepstr(label)
|
| 453 |
+
postcommand = getattr(self, f'{name}_menu_postcommand', None)
|
| 454 |
+
menudict[name] = menu = Menu(mbar, name=name, tearoff=0,
|
| 455 |
+
postcommand=postcommand)
|
| 456 |
+
mbar.add_cascade(label=label, menu=menu, underline=underline)
|
| 457 |
+
if macosx.isCarbonTk():
|
| 458 |
+
# Insert the application menu
|
| 459 |
+
menudict['application'] = menu = Menu(mbar, name='apple',
|
| 460 |
+
tearoff=0)
|
| 461 |
+
mbar.add_cascade(label='IDLE', menu=menu)
|
| 462 |
+
self.fill_menus()
|
| 463 |
+
self.recent_files_menu = Menu(self.menubar, tearoff=0)
|
| 464 |
+
self.menudict['file'].insert_cascade(3, label='Recent Files',
|
| 465 |
+
underline=0,
|
| 466 |
+
menu=self.recent_files_menu)
|
| 467 |
+
self.base_helpmenu_length = self.menudict['help'].index(END)
|
| 468 |
+
self.reset_help_menu_entries()
|
| 469 |
+
|
| 470 |
+
def postwindowsmenu(self):
|
| 471 |
+
# Only called when Window menu exists
|
| 472 |
+
menu = self.menudict['window']
|
| 473 |
+
end = menu.index("end")
|
| 474 |
+
if end is None:
|
| 475 |
+
end = -1
|
| 476 |
+
if end > self.wmenu_end:
|
| 477 |
+
menu.delete(self.wmenu_end+1, end)
|
| 478 |
+
window.add_windows_to_menu(menu)
|
| 479 |
+
|
| 480 |
+
def update_menu_label(self, menu, index, label):
|
| 481 |
+
"Update label for menu item at index."
|
| 482 |
+
menuitem = self.menudict[menu]
|
| 483 |
+
menuitem.entryconfig(index, label=label)
|
| 484 |
+
|
| 485 |
+
def update_menu_state(self, menu, index, state):
|
| 486 |
+
"Update state for menu item at index."
|
| 487 |
+
menuitem = self.menudict[menu]
|
| 488 |
+
menuitem.entryconfig(index, state=state)
|
| 489 |
+
|
| 490 |
+
def handle_yview(self, event, *args):
|
| 491 |
+
"Handle scrollbar."
|
| 492 |
+
if event == 'moveto':
|
| 493 |
+
fraction = float(args[0])
|
| 494 |
+
lines = (round(self.getlineno('end') * fraction) -
|
| 495 |
+
self.getlineno('@0,0'))
|
| 496 |
+
event = 'scroll'
|
| 497 |
+
args = (lines, 'units')
|
| 498 |
+
self.text.yview(event, *args)
|
| 499 |
+
return 'break'
|
| 500 |
+
|
| 501 |
+
rmenu = None
|
| 502 |
+
|
| 503 |
+
def right_menu_event(self, event):
|
| 504 |
+
text = self.text
|
| 505 |
+
newdex = text.index(f'@{event.x},{event.y}')
|
| 506 |
+
try:
|
| 507 |
+
in_selection = (text.compare('sel.first', '<=', newdex) and
|
| 508 |
+
text.compare(newdex, '<=', 'sel.last'))
|
| 509 |
+
except TclError:
|
| 510 |
+
in_selection = False
|
| 511 |
+
if not in_selection:
|
| 512 |
+
text.tag_remove("sel", "1.0", "end")
|
| 513 |
+
text.mark_set("insert", newdex)
|
| 514 |
+
if not self.rmenu:
|
| 515 |
+
self.make_rmenu()
|
| 516 |
+
rmenu = self.rmenu
|
| 517 |
+
self.event = event
|
| 518 |
+
iswin = sys.platform[:3] == 'win'
|
| 519 |
+
if iswin:
|
| 520 |
+
text.config(cursor="arrow")
|
| 521 |
+
|
| 522 |
+
for item in self.rmenu_specs:
|
| 523 |
+
try:
|
| 524 |
+
label, eventname, verify_state = item
|
| 525 |
+
except ValueError: # see issue1207589
|
| 526 |
+
continue
|
| 527 |
+
|
| 528 |
+
if verify_state is None:
|
| 529 |
+
continue
|
| 530 |
+
state = getattr(self, verify_state)()
|
| 531 |
+
rmenu.entryconfigure(label, state=state)
|
| 532 |
+
|
| 533 |
+
rmenu.tk_popup(event.x_root, event.y_root)
|
| 534 |
+
if iswin:
|
| 535 |
+
self.text.config(cursor="ibeam")
|
| 536 |
+
return "break"
|
| 537 |
+
|
| 538 |
+
rmenu_specs = [
|
| 539 |
+
# ("Label", "<<virtual-event>>", "statefuncname"), ...
|
| 540 |
+
("Close", "<<close-window>>", None), # Example
|
| 541 |
+
]
|
| 542 |
+
|
| 543 |
+
def make_rmenu(self):
|
| 544 |
+
rmenu = Menu(self.text, tearoff=0)
|
| 545 |
+
for item in self.rmenu_specs:
|
| 546 |
+
label, eventname = item[0], item[1]
|
| 547 |
+
if label is not None:
|
| 548 |
+
def command(text=self.text, eventname=eventname):
|
| 549 |
+
text.event_generate(eventname)
|
| 550 |
+
rmenu.add_command(label=label, command=command)
|
| 551 |
+
else:
|
| 552 |
+
rmenu.add_separator()
|
| 553 |
+
self.rmenu = rmenu
|
| 554 |
+
|
| 555 |
+
def rmenu_check_cut(self):
|
| 556 |
+
return self.rmenu_check_copy()
|
| 557 |
+
|
| 558 |
+
def rmenu_check_copy(self):
|
| 559 |
+
try:
|
| 560 |
+
indx = self.text.index('sel.first')
|
| 561 |
+
except TclError:
|
| 562 |
+
return 'disabled'
|
| 563 |
+
else:
|
| 564 |
+
return 'normal' if indx else 'disabled'
|
| 565 |
+
|
| 566 |
+
def rmenu_check_paste(self):
|
| 567 |
+
try:
|
| 568 |
+
self.text.tk.call('tk::GetSelection', self.text, 'CLIPBOARD')
|
| 569 |
+
except TclError:
|
| 570 |
+
return 'disabled'
|
| 571 |
+
else:
|
| 572 |
+
return 'normal'
|
| 573 |
+
|
| 574 |
+
def about_dialog(self, event=None):
|
| 575 |
+
"Handle Help 'About IDLE' event."
|
| 576 |
+
# Synchronize with macosx.overrideRootMenu.about_dialog.
|
| 577 |
+
help_about.AboutDialog(self.top)
|
| 578 |
+
return "break"
|
| 579 |
+
|
| 580 |
+
def config_dialog(self, event=None):
|
| 581 |
+
"Handle Options 'Configure IDLE' event."
|
| 582 |
+
# Synchronize with macosx.overrideRootMenu.config_dialog.
|
| 583 |
+
configdialog.ConfigDialog(self.top,'Settings')
|
| 584 |
+
return "break"
|
| 585 |
+
|
| 586 |
+
def help_dialog(self, event=None):
|
| 587 |
+
"Handle Help 'IDLE Help' event."
|
| 588 |
+
# Synchronize with macosx.overrideRootMenu.help_dialog.
|
| 589 |
+
if self.root:
|
| 590 |
+
parent = self.root
|
| 591 |
+
else:
|
| 592 |
+
parent = self.top
|
| 593 |
+
help.show_idlehelp(parent)
|
| 594 |
+
return "break"
|
| 595 |
+
|
| 596 |
+
def python_docs(self, event=None):
|
| 597 |
+
if sys.platform[:3] == 'win':
|
| 598 |
+
try:
|
| 599 |
+
os.startfile(self.help_url)
|
| 600 |
+
except OSError as why:
|
| 601 |
+
messagebox.showerror(title='Document Start Failure',
|
| 602 |
+
message=str(why), parent=self.text)
|
| 603 |
+
else:
|
| 604 |
+
webbrowser.open(self.help_url)
|
| 605 |
+
return "break"
|
| 606 |
+
|
| 607 |
+
def cut(self,event):
|
| 608 |
+
self.text.event_generate("<<Cut>>")
|
| 609 |
+
return "break"
|
| 610 |
+
|
| 611 |
+
def copy(self,event):
|
| 612 |
+
if not self.text.tag_ranges("sel"):
|
| 613 |
+
# There is no selection, so do nothing and maybe interrupt.
|
| 614 |
+
return None
|
| 615 |
+
self.text.event_generate("<<Copy>>")
|
| 616 |
+
return "break"
|
| 617 |
+
|
| 618 |
+
def paste(self,event):
|
| 619 |
+
self.text.event_generate("<<Paste>>")
|
| 620 |
+
self.text.see("insert")
|
| 621 |
+
return "break"
|
| 622 |
+
|
| 623 |
+
def select_all(self, event=None):
|
| 624 |
+
self.text.tag_add("sel", "1.0", "end-1c")
|
| 625 |
+
self.text.mark_set("insert", "1.0")
|
| 626 |
+
self.text.see("insert")
|
| 627 |
+
return "break"
|
| 628 |
+
|
| 629 |
+
def remove_selection(self, event=None):
|
| 630 |
+
self.text.tag_remove("sel", "1.0", "end")
|
| 631 |
+
self.text.see("insert")
|
| 632 |
+
return "break"
|
| 633 |
+
|
| 634 |
+
def move_at_edge_if_selection(self, edge_index):
|
| 635 |
+
"""Cursor move begins at start or end of selection
|
| 636 |
+
|
| 637 |
+
When a left/right cursor key is pressed create and return to Tkinter a
|
| 638 |
+
function which causes a cursor move from the associated edge of the
|
| 639 |
+
selection.
|
| 640 |
+
|
| 641 |
+
"""
|
| 642 |
+
self_text_index = self.text.index
|
| 643 |
+
self_text_mark_set = self.text.mark_set
|
| 644 |
+
edges_table = ("sel.first+1c", "sel.last-1c")
|
| 645 |
+
def move_at_edge(event):
|
| 646 |
+
if (event.state & 5) == 0: # no shift(==1) or control(==4) pressed
|
| 647 |
+
try:
|
| 648 |
+
self_text_index("sel.first")
|
| 649 |
+
self_text_mark_set("insert", edges_table[edge_index])
|
| 650 |
+
except TclError:
|
| 651 |
+
pass
|
| 652 |
+
return move_at_edge
|
| 653 |
+
|
| 654 |
+
def del_word_left(self, event):
|
| 655 |
+
self.text.event_generate('<Meta-Delete>')
|
| 656 |
+
return "break"
|
| 657 |
+
|
| 658 |
+
def del_word_right(self, event):
|
| 659 |
+
self.text.event_generate('<Meta-d>')
|
| 660 |
+
return "break"
|
| 661 |
+
|
| 662 |
+
def find_event(self, event):
|
| 663 |
+
search.find(self.text)
|
| 664 |
+
return "break"
|
| 665 |
+
|
| 666 |
+
def find_again_event(self, event):
|
| 667 |
+
search.find_again(self.text)
|
| 668 |
+
return "break"
|
| 669 |
+
|
| 670 |
+
def find_selection_event(self, event):
|
| 671 |
+
search.find_selection(self.text)
|
| 672 |
+
return "break"
|
| 673 |
+
|
| 674 |
+
def find_in_files_event(self, event):
|
| 675 |
+
grep.grep(self.text, self.io, self.flist)
|
| 676 |
+
return "break"
|
| 677 |
+
|
| 678 |
+
def replace_event(self, event):
|
| 679 |
+
replace.replace(self.text)
|
| 680 |
+
return "break"
|
| 681 |
+
|
| 682 |
+
def goto_line_event(self, event):
|
| 683 |
+
text = self.text
|
| 684 |
+
lineno = query.Goto(
|
| 685 |
+
text, "Go To Line",
|
| 686 |
+
"Enter a positive integer\n"
|
| 687 |
+
"('big' = end of file):"
|
| 688 |
+
).result
|
| 689 |
+
if lineno is not None:
|
| 690 |
+
text.tag_remove("sel", "1.0", "end")
|
| 691 |
+
text.mark_set("insert", f'{lineno}.0')
|
| 692 |
+
text.see("insert")
|
| 693 |
+
self.set_line_and_column()
|
| 694 |
+
return "break"
|
| 695 |
+
|
| 696 |
+
def open_module(self):
|
| 697 |
+
"""Get module name from user and open it.
|
| 698 |
+
|
| 699 |
+
Return module path or None for calls by open_module_browser
|
| 700 |
+
when latter is not invoked in named editor window.
|
| 701 |
+
"""
|
| 702 |
+
# XXX This, open_module_browser, and open_path_browser
|
| 703 |
+
# would fit better in iomenu.IOBinding.
|
| 704 |
+
try:
|
| 705 |
+
name = self.text.get("sel.first", "sel.last").strip()
|
| 706 |
+
except TclError:
|
| 707 |
+
name = ''
|
| 708 |
+
file_path = query.ModuleName(
|
| 709 |
+
self.text, "Open Module",
|
| 710 |
+
"Enter the name of a Python module\n"
|
| 711 |
+
"to search on sys.path and open:",
|
| 712 |
+
name).result
|
| 713 |
+
if file_path is not None:
|
| 714 |
+
if self.flist:
|
| 715 |
+
self.flist.open(file_path)
|
| 716 |
+
else:
|
| 717 |
+
self.io.loadfile(file_path)
|
| 718 |
+
return file_path
|
| 719 |
+
|
| 720 |
+
def open_module_event(self, event):
|
| 721 |
+
self.open_module()
|
| 722 |
+
return "break"
|
| 723 |
+
|
| 724 |
+
def open_module_browser(self, event=None):
|
| 725 |
+
filename = self.io.filename
|
| 726 |
+
if not (self.__class__.__name__ == 'PyShellEditorWindow'
|
| 727 |
+
and filename):
|
| 728 |
+
filename = self.open_module()
|
| 729 |
+
if filename is None:
|
| 730 |
+
return "break"
|
| 731 |
+
from idlelib import browser
|
| 732 |
+
browser.ModuleBrowser(self.root, filename)
|
| 733 |
+
return "break"
|
| 734 |
+
|
| 735 |
+
def open_path_browser(self, event=None):
|
| 736 |
+
from idlelib import pathbrowser
|
| 737 |
+
pathbrowser.PathBrowser(self.root)
|
| 738 |
+
return "break"
|
| 739 |
+
|
| 740 |
+
def open_turtle_demo(self, event = None):
|
| 741 |
+
import subprocess
|
| 742 |
+
|
| 743 |
+
cmd = [sys.executable,
|
| 744 |
+
'-c',
|
| 745 |
+
'from turtledemo.__main__ import main; main()']
|
| 746 |
+
subprocess.Popen(cmd, shell=False)
|
| 747 |
+
return "break"
|
| 748 |
+
|
| 749 |
+
def gotoline(self, lineno):
|
| 750 |
+
if lineno is not None and lineno > 0:
|
| 751 |
+
self.text.mark_set("insert", "%d.0" % lineno)
|
| 752 |
+
self.text.tag_remove("sel", "1.0", "end")
|
| 753 |
+
self.text.tag_add("sel", "insert", "insert +1l")
|
| 754 |
+
self.center()
|
| 755 |
+
|
| 756 |
+
def ispythonsource(self, filename):
|
| 757 |
+
if not filename or os.path.isdir(filename):
|
| 758 |
+
return True
|
| 759 |
+
base, ext = os.path.splitext(os.path.basename(filename))
|
| 760 |
+
if os.path.normcase(ext) in (".py", ".pyw"):
|
| 761 |
+
return True
|
| 762 |
+
line = self.text.get('1.0', '1.0 lineend')
|
| 763 |
+
return line.startswith('#!') and 'python' in line
|
| 764 |
+
|
| 765 |
+
def close_hook(self):
|
| 766 |
+
if self.flist:
|
| 767 |
+
self.flist.unregister_maybe_terminate(self)
|
| 768 |
+
self.flist = None
|
| 769 |
+
|
| 770 |
+
def set_close_hook(self, close_hook):
|
| 771 |
+
self.close_hook = close_hook
|
| 772 |
+
|
| 773 |
+
def filename_change_hook(self):
|
| 774 |
+
if self.flist:
|
| 775 |
+
self.flist.filename_changed_edit(self)
|
| 776 |
+
self.saved_change_hook()
|
| 777 |
+
self.top.update_windowlist_registry(self)
|
| 778 |
+
self.ResetColorizer()
|
| 779 |
+
|
| 780 |
+
def _addcolorizer(self):
|
| 781 |
+
if self.color:
|
| 782 |
+
return
|
| 783 |
+
if self.ispythonsource(self.io.filename):
|
| 784 |
+
self.color = self.ColorDelegator()
|
| 785 |
+
# can add more colorizers here...
|
| 786 |
+
if self.color:
|
| 787 |
+
self.per.removefilter(self.undo)
|
| 788 |
+
self.per.insertfilter(self.color)
|
| 789 |
+
self.per.insertfilter(self.undo)
|
| 790 |
+
|
| 791 |
+
def _rmcolorizer(self):
|
| 792 |
+
if not self.color:
|
| 793 |
+
return
|
| 794 |
+
self.color.removecolors()
|
| 795 |
+
self.per.removefilter(self.color)
|
| 796 |
+
self.color = None
|
| 797 |
+
|
| 798 |
+
def ResetColorizer(self):
|
| 799 |
+
"Update the color theme"
|
| 800 |
+
# Called from self.filename_change_hook and from configdialog.py
|
| 801 |
+
self._rmcolorizer()
|
| 802 |
+
self._addcolorizer()
|
| 803 |
+
EditorWindow.color_config(self.text)
|
| 804 |
+
|
| 805 |
+
if self.code_context is not None:
|
| 806 |
+
self.code_context.update_highlight_colors()
|
| 807 |
+
|
| 808 |
+
if self.line_numbers is not None:
|
| 809 |
+
self.line_numbers.update_colors()
|
| 810 |
+
|
| 811 |
+
IDENTCHARS = string.ascii_letters + string.digits + "_"
|
| 812 |
+
|
| 813 |
+
def colorize_syntax_error(self, text, pos):
|
| 814 |
+
text.tag_add("ERROR", pos)
|
| 815 |
+
char = text.get(pos)
|
| 816 |
+
if char and char in self.IDENTCHARS:
|
| 817 |
+
text.tag_add("ERROR", pos + " wordstart", pos)
|
| 818 |
+
if '\n' == text.get(pos): # error at line end
|
| 819 |
+
text.mark_set("insert", pos)
|
| 820 |
+
else:
|
| 821 |
+
text.mark_set("insert", pos + "+1c")
|
| 822 |
+
text.see(pos)
|
| 823 |
+
|
| 824 |
+
def update_cursor_blink(self):
|
| 825 |
+
"Update the cursor blink configuration."
|
| 826 |
+
cursorblink = idleConf.GetOption(
|
| 827 |
+
'main', 'EditorWindow', 'cursor-blink', type='bool')
|
| 828 |
+
if not cursorblink:
|
| 829 |
+
self.text['insertofftime'] = 0
|
| 830 |
+
else:
|
| 831 |
+
# Restore the original value
|
| 832 |
+
self.text['insertofftime'] = idleConf.blink_off_time
|
| 833 |
+
|
| 834 |
+
def ResetFont(self):
|
| 835 |
+
"Update the text widgets' font if it is changed"
|
| 836 |
+
# Called from configdialog.py
|
| 837 |
+
|
| 838 |
+
# Update the code context widget first, since its height affects
|
| 839 |
+
# the height of the text widget. This avoids double re-rendering.
|
| 840 |
+
if self.code_context is not None:
|
| 841 |
+
self.code_context.update_font()
|
| 842 |
+
# Next, update the line numbers widget, since its width affects
|
| 843 |
+
# the width of the text widget.
|
| 844 |
+
if self.line_numbers is not None:
|
| 845 |
+
self.line_numbers.update_font()
|
| 846 |
+
# Finally, update the main text widget.
|
| 847 |
+
new_font = idleConf.GetFont(self.root, 'main', 'EditorWindow')
|
| 848 |
+
self.text['font'] = new_font
|
| 849 |
+
self.set_width()
|
| 850 |
+
|
| 851 |
+
def RemoveKeybindings(self):
|
| 852 |
+
"Remove the keybindings before they are changed."
|
| 853 |
+
# Called from configdialog.py
|
| 854 |
+
self.mainmenu.default_keydefs = keydefs = idleConf.GetCurrentKeySet()
|
| 855 |
+
for event, keylist in keydefs.items():
|
| 856 |
+
self.text.event_delete(event, *keylist)
|
| 857 |
+
for extensionName in self.get_standard_extension_names():
|
| 858 |
+
xkeydefs = idleConf.GetExtensionBindings(extensionName)
|
| 859 |
+
if xkeydefs:
|
| 860 |
+
for event, keylist in xkeydefs.items():
|
| 861 |
+
self.text.event_delete(event, *keylist)
|
| 862 |
+
|
| 863 |
+
def ApplyKeybindings(self):
|
| 864 |
+
"Update the keybindings after they are changed"
|
| 865 |
+
# Called from configdialog.py
|
| 866 |
+
self.mainmenu.default_keydefs = keydefs = idleConf.GetCurrentKeySet()
|
| 867 |
+
self.apply_bindings()
|
| 868 |
+
for extensionName in self.get_standard_extension_names():
|
| 869 |
+
xkeydefs = idleConf.GetExtensionBindings(extensionName)
|
| 870 |
+
if xkeydefs:
|
| 871 |
+
self.apply_bindings(xkeydefs)
|
| 872 |
+
#update menu accelerators
|
| 873 |
+
menuEventDict = {}
|
| 874 |
+
for menu in self.mainmenu.menudefs:
|
| 875 |
+
menuEventDict[menu[0]] = {}
|
| 876 |
+
for item in menu[1]:
|
| 877 |
+
if item:
|
| 878 |
+
menuEventDict[menu[0]][prepstr(item[0])[1]] = item[1]
|
| 879 |
+
for menubarItem in self.menudict:
|
| 880 |
+
menu = self.menudict[menubarItem]
|
| 881 |
+
end = menu.index(END)
|
| 882 |
+
if end is None:
|
| 883 |
+
# Skip empty menus
|
| 884 |
+
continue
|
| 885 |
+
end += 1
|
| 886 |
+
for index in range(0, end):
|
| 887 |
+
if menu.type(index) == 'command':
|
| 888 |
+
accel = menu.entrycget(index, 'accelerator')
|
| 889 |
+
if accel:
|
| 890 |
+
itemName = menu.entrycget(index, 'label')
|
| 891 |
+
event = ''
|
| 892 |
+
if menubarItem in menuEventDict:
|
| 893 |
+
if itemName in menuEventDict[menubarItem]:
|
| 894 |
+
event = menuEventDict[menubarItem][itemName]
|
| 895 |
+
if event:
|
| 896 |
+
accel = get_accelerator(keydefs, event)
|
| 897 |
+
menu.entryconfig(index, accelerator=accel)
|
| 898 |
+
|
| 899 |
+
def set_notabs_indentwidth(self):
|
| 900 |
+
"Update the indentwidth if changed and not using tabs in this window"
|
| 901 |
+
# Called from configdialog.py
|
| 902 |
+
if not self.usetabs:
|
| 903 |
+
self.indentwidth = idleConf.GetOption('main', 'Indent','num-spaces',
|
| 904 |
+
type='int')
|
| 905 |
+
|
| 906 |
+
def reset_help_menu_entries(self):
|
| 907 |
+
"Update the additional help entries on the Help menu"
|
| 908 |
+
help_list = idleConf.GetAllExtraHelpSourcesList()
|
| 909 |
+
helpmenu = self.menudict['help']
|
| 910 |
+
# first delete the extra help entries, if any
|
| 911 |
+
helpmenu_length = helpmenu.index(END)
|
| 912 |
+
if helpmenu_length > self.base_helpmenu_length:
|
| 913 |
+
helpmenu.delete((self.base_helpmenu_length + 1), helpmenu_length)
|
| 914 |
+
# then rebuild them
|
| 915 |
+
if help_list:
|
| 916 |
+
helpmenu.add_separator()
|
| 917 |
+
for entry in help_list:
|
| 918 |
+
cmd = self.__extra_help_callback(entry[1])
|
| 919 |
+
helpmenu.add_command(label=entry[0], command=cmd)
|
| 920 |
+
# and update the menu dictionary
|
| 921 |
+
self.menudict['help'] = helpmenu
|
| 922 |
+
|
| 923 |
+
def __extra_help_callback(self, helpfile):
|
| 924 |
+
"Create a callback with the helpfile value frozen at definition time"
|
| 925 |
+
def display_extra_help(helpfile=helpfile):
|
| 926 |
+
if not helpfile.startswith(('www', 'http')):
|
| 927 |
+
helpfile = os.path.normpath(helpfile)
|
| 928 |
+
if sys.platform[:3] == 'win':
|
| 929 |
+
try:
|
| 930 |
+
os.startfile(helpfile)
|
| 931 |
+
except OSError as why:
|
| 932 |
+
messagebox.showerror(title='Document Start Failure',
|
| 933 |
+
message=str(why), parent=self.text)
|
| 934 |
+
else:
|
| 935 |
+
webbrowser.open(helpfile)
|
| 936 |
+
return display_extra_help
|
| 937 |
+
|
| 938 |
+
def update_recent_files_list(self, new_file=None):
|
| 939 |
+
"Load and update the recent files list and menus"
|
| 940 |
+
# TODO: move to iomenu.
|
| 941 |
+
rf_list = []
|
| 942 |
+
file_path = self.recent_files_path
|
| 943 |
+
if file_path and os.path.exists(file_path):
|
| 944 |
+
with open(file_path, 'r',
|
| 945 |
+
encoding='utf_8', errors='replace') as rf_list_file:
|
| 946 |
+
rf_list = rf_list_file.readlines()
|
| 947 |
+
if new_file:
|
| 948 |
+
new_file = os.path.abspath(new_file) + '\n'
|
| 949 |
+
if new_file in rf_list:
|
| 950 |
+
rf_list.remove(new_file) # move to top
|
| 951 |
+
rf_list.insert(0, new_file)
|
| 952 |
+
# clean and save the recent files list
|
| 953 |
+
bad_paths = []
|
| 954 |
+
for path in rf_list:
|
| 955 |
+
if '\0' in path or not os.path.exists(path[0:-1]):
|
| 956 |
+
bad_paths.append(path)
|
| 957 |
+
rf_list = [path for path in rf_list if path not in bad_paths]
|
| 958 |
+
ulchars = "1234567890ABCDEFGHIJK"
|
| 959 |
+
rf_list = rf_list[0:len(ulchars)]
|
| 960 |
+
if file_path:
|
| 961 |
+
try:
|
| 962 |
+
with open(file_path, 'w',
|
| 963 |
+
encoding='utf_8', errors='replace') as rf_file:
|
| 964 |
+
rf_file.writelines(rf_list)
|
| 965 |
+
except OSError as err:
|
| 966 |
+
if not getattr(self.root, "recentfiles_message", False):
|
| 967 |
+
self.root.recentfiles_message = True
|
| 968 |
+
messagebox.showwarning(title='IDLE Warning',
|
| 969 |
+
message="Cannot save Recent Files list to disk.\n"
|
| 970 |
+
f" {err}\n"
|
| 971 |
+
"Select OK to continue.",
|
| 972 |
+
parent=self.text)
|
| 973 |
+
# for each edit window instance, construct the recent files menu
|
| 974 |
+
for instance in self.top.instance_dict:
|
| 975 |
+
menu = instance.recent_files_menu
|
| 976 |
+
menu.delete(0, END) # clear, and rebuild:
|
| 977 |
+
for i, file_name in enumerate(rf_list):
|
| 978 |
+
file_name = file_name.rstrip() # zap \n
|
| 979 |
+
callback = instance.__recent_file_callback(file_name)
|
| 980 |
+
menu.add_command(label=ulchars[i] + " " + file_name,
|
| 981 |
+
command=callback,
|
| 982 |
+
underline=0)
|
| 983 |
+
|
| 984 |
+
def __recent_file_callback(self, file_name):
|
| 985 |
+
def open_recent_file(fn_closure=file_name):
|
| 986 |
+
self.io.open(editFile=fn_closure)
|
| 987 |
+
return open_recent_file
|
| 988 |
+
|
| 989 |
+
def saved_change_hook(self):
|
| 990 |
+
short = self.short_title()
|
| 991 |
+
long = self.long_title()
|
| 992 |
+
if short and long:
|
| 993 |
+
title = short + " - " + long + _py_version
|
| 994 |
+
elif short:
|
| 995 |
+
title = short
|
| 996 |
+
elif long:
|
| 997 |
+
title = long
|
| 998 |
+
else:
|
| 999 |
+
title = "untitled"
|
| 1000 |
+
icon = short or long or title
|
| 1001 |
+
if not self.get_saved():
|
| 1002 |
+
title = "*%s*" % title
|
| 1003 |
+
icon = "*%s" % icon
|
| 1004 |
+
self.top.wm_title(title)
|
| 1005 |
+
self.top.wm_iconname(icon)
|
| 1006 |
+
|
| 1007 |
+
def get_saved(self):
|
| 1008 |
+
return self.undo.get_saved()
|
| 1009 |
+
|
| 1010 |
+
def set_saved(self, flag):
|
| 1011 |
+
self.undo.set_saved(flag)
|
| 1012 |
+
|
| 1013 |
+
def reset_undo(self):
|
| 1014 |
+
self.undo.reset_undo()
|
| 1015 |
+
|
| 1016 |
+
def short_title(self):
|
| 1017 |
+
filename = self.io.filename
|
| 1018 |
+
return os.path.basename(filename) if filename else "untitled"
|
| 1019 |
+
|
| 1020 |
+
def long_title(self):
|
| 1021 |
+
return self.io.filename or ""
|
| 1022 |
+
|
| 1023 |
+
def center_insert_event(self, event):
|
| 1024 |
+
self.center()
|
| 1025 |
+
return "break"
|
| 1026 |
+
|
| 1027 |
+
def center(self, mark="insert"):
|
| 1028 |
+
text = self.text
|
| 1029 |
+
top, bot = self.getwindowlines()
|
| 1030 |
+
lineno = self.getlineno(mark)
|
| 1031 |
+
height = bot - top
|
| 1032 |
+
newtop = max(1, lineno - height//2)
|
| 1033 |
+
text.yview(float(newtop))
|
| 1034 |
+
|
| 1035 |
+
def getwindowlines(self):
|
| 1036 |
+
text = self.text
|
| 1037 |
+
top = self.getlineno("@0,0")
|
| 1038 |
+
bot = self.getlineno("@0,65535")
|
| 1039 |
+
if top == bot and text.winfo_height() == 1:
|
| 1040 |
+
# Geometry manager hasn't run yet
|
| 1041 |
+
height = int(text['height'])
|
| 1042 |
+
bot = top + height - 1
|
| 1043 |
+
return top, bot
|
| 1044 |
+
|
| 1045 |
+
def getlineno(self, mark="insert"):
|
| 1046 |
+
text = self.text
|
| 1047 |
+
return int(float(text.index(mark)))
|
| 1048 |
+
|
| 1049 |
+
def get_geometry(self):
|
| 1050 |
+
"Return (width, height, x, y)"
|
| 1051 |
+
geom = self.top.wm_geometry()
|
| 1052 |
+
m = re.match(r"(\d+)x(\d+)\+(-?\d+)\+(-?\d+)", geom)
|
| 1053 |
+
return list(map(int, m.groups()))
|
| 1054 |
+
|
| 1055 |
+
def close_event(self, event):
|
| 1056 |
+
self.close()
|
| 1057 |
+
return "break"
|
| 1058 |
+
|
| 1059 |
+
def maybesave(self):
|
| 1060 |
+
if self.io:
|
| 1061 |
+
if not self.get_saved():
|
| 1062 |
+
if self.top.state()!='normal':
|
| 1063 |
+
self.top.deiconify()
|
| 1064 |
+
self.top.lower()
|
| 1065 |
+
self.top.lift()
|
| 1066 |
+
return self.io.maybesave()
|
| 1067 |
+
|
| 1068 |
+
def close(self):
|
| 1069 |
+
try:
|
| 1070 |
+
reply = self.maybesave()
|
| 1071 |
+
if str(reply) != "cancel":
|
| 1072 |
+
self._close()
|
| 1073 |
+
return reply
|
| 1074 |
+
except AttributeError: # bpo-35379: close called twice
|
| 1075 |
+
pass
|
| 1076 |
+
|
| 1077 |
+
def _close(self):
|
| 1078 |
+
if self.io.filename:
|
| 1079 |
+
self.update_recent_files_list(new_file=self.io.filename)
|
| 1080 |
+
window.unregister_callback(self.postwindowsmenu)
|
| 1081 |
+
self.unload_extensions()
|
| 1082 |
+
self.io.close()
|
| 1083 |
+
self.io = None
|
| 1084 |
+
self.undo = None
|
| 1085 |
+
if self.color:
|
| 1086 |
+
self.color.close()
|
| 1087 |
+
self.color = None
|
| 1088 |
+
self.text = None
|
| 1089 |
+
self.tkinter_vars = None
|
| 1090 |
+
self.per.close()
|
| 1091 |
+
self.per = None
|
| 1092 |
+
self.top.destroy()
|
| 1093 |
+
if self.close_hook:
|
| 1094 |
+
# unless override: unregister from flist, terminate if last window
|
| 1095 |
+
self.close_hook()
|
| 1096 |
+
|
| 1097 |
+
def load_extensions(self):
|
| 1098 |
+
self.extensions = {}
|
| 1099 |
+
self.load_standard_extensions()
|
| 1100 |
+
|
| 1101 |
+
def unload_extensions(self):
|
| 1102 |
+
for ins in list(self.extensions.values()):
|
| 1103 |
+
if hasattr(ins, "close"):
|
| 1104 |
+
ins.close()
|
| 1105 |
+
self.extensions = {}
|
| 1106 |
+
|
| 1107 |
+
def load_standard_extensions(self):
|
| 1108 |
+
for name in self.get_standard_extension_names():
|
| 1109 |
+
try:
|
| 1110 |
+
self.load_extension(name)
|
| 1111 |
+
except:
|
| 1112 |
+
print("Failed to load extension", repr(name))
|
| 1113 |
+
traceback.print_exc()
|
| 1114 |
+
|
| 1115 |
+
def get_standard_extension_names(self):
|
| 1116 |
+
return idleConf.GetExtensions(editor_only=True)
|
| 1117 |
+
|
| 1118 |
+
extfiles = { # Map built-in config-extension section names to file names.
|
| 1119 |
+
'ZzDummy': 'zzdummy',
|
| 1120 |
+
}
|
| 1121 |
+
|
| 1122 |
+
def load_extension(self, name):
|
| 1123 |
+
fname = self.extfiles.get(name, name)
|
| 1124 |
+
try:
|
| 1125 |
+
try:
|
| 1126 |
+
mod = importlib.import_module('.' + fname, package=__package__)
|
| 1127 |
+
except (ImportError, TypeError):
|
| 1128 |
+
mod = importlib.import_module(fname)
|
| 1129 |
+
except ImportError:
|
| 1130 |
+
print("\nFailed to import extension: ", name)
|
| 1131 |
+
raise
|
| 1132 |
+
cls = getattr(mod, name)
|
| 1133 |
+
keydefs = idleConf.GetExtensionBindings(name)
|
| 1134 |
+
if hasattr(cls, "menudefs"):
|
| 1135 |
+
self.fill_menus(cls.menudefs, keydefs)
|
| 1136 |
+
ins = cls(self)
|
| 1137 |
+
self.extensions[name] = ins
|
| 1138 |
+
if keydefs:
|
| 1139 |
+
self.apply_bindings(keydefs)
|
| 1140 |
+
for vevent in keydefs:
|
| 1141 |
+
methodname = vevent.replace("-", "_")
|
| 1142 |
+
while methodname[:1] == '<':
|
| 1143 |
+
methodname = methodname[1:]
|
| 1144 |
+
while methodname[-1:] == '>':
|
| 1145 |
+
methodname = methodname[:-1]
|
| 1146 |
+
methodname = methodname + "_event"
|
| 1147 |
+
if hasattr(ins, methodname):
|
| 1148 |
+
self.text.bind(vevent, getattr(ins, methodname))
|
| 1149 |
+
|
| 1150 |
+
def apply_bindings(self, keydefs=None):
|
| 1151 |
+
if keydefs is None:
|
| 1152 |
+
keydefs = self.mainmenu.default_keydefs
|
| 1153 |
+
text = self.text
|
| 1154 |
+
text.keydefs = keydefs
|
| 1155 |
+
for event, keylist in keydefs.items():
|
| 1156 |
+
if keylist:
|
| 1157 |
+
text.event_add(event, *keylist)
|
| 1158 |
+
|
| 1159 |
+
def fill_menus(self, menudefs=None, keydefs=None):
|
| 1160 |
+
"""Add appropriate entries to the menus and submenus
|
| 1161 |
+
|
| 1162 |
+
Menus that are absent or None in self.menudict are ignored.
|
| 1163 |
+
"""
|
| 1164 |
+
if menudefs is None:
|
| 1165 |
+
menudefs = self.mainmenu.menudefs
|
| 1166 |
+
if keydefs is None:
|
| 1167 |
+
keydefs = self.mainmenu.default_keydefs
|
| 1168 |
+
menudict = self.menudict
|
| 1169 |
+
text = self.text
|
| 1170 |
+
for mname, entrylist in menudefs:
|
| 1171 |
+
menu = menudict.get(mname)
|
| 1172 |
+
if not menu:
|
| 1173 |
+
continue
|
| 1174 |
+
for entry in entrylist:
|
| 1175 |
+
if not entry:
|
| 1176 |
+
menu.add_separator()
|
| 1177 |
+
else:
|
| 1178 |
+
label, eventname = entry
|
| 1179 |
+
checkbutton = (label[:1] == '!')
|
| 1180 |
+
if checkbutton:
|
| 1181 |
+
label = label[1:]
|
| 1182 |
+
underline, label = prepstr(label)
|
| 1183 |
+
accelerator = get_accelerator(keydefs, eventname)
|
| 1184 |
+
def command(text=text, eventname=eventname):
|
| 1185 |
+
text.event_generate(eventname)
|
| 1186 |
+
if checkbutton:
|
| 1187 |
+
var = self.get_var_obj(eventname, BooleanVar)
|
| 1188 |
+
menu.add_checkbutton(label=label, underline=underline,
|
| 1189 |
+
command=command, accelerator=accelerator,
|
| 1190 |
+
variable=var)
|
| 1191 |
+
else:
|
| 1192 |
+
menu.add_command(label=label, underline=underline,
|
| 1193 |
+
command=command,
|
| 1194 |
+
accelerator=accelerator)
|
| 1195 |
+
|
| 1196 |
+
def getvar(self, name):
|
| 1197 |
+
var = self.get_var_obj(name)
|
| 1198 |
+
if var:
|
| 1199 |
+
value = var.get()
|
| 1200 |
+
return value
|
| 1201 |
+
else:
|
| 1202 |
+
raise NameError(name)
|
| 1203 |
+
|
| 1204 |
+
def setvar(self, name, value, vartype=None):
|
| 1205 |
+
var = self.get_var_obj(name, vartype)
|
| 1206 |
+
if var:
|
| 1207 |
+
var.set(value)
|
| 1208 |
+
else:
|
| 1209 |
+
raise NameError(name)
|
| 1210 |
+
|
| 1211 |
+
def get_var_obj(self, name, vartype=None):
|
| 1212 |
+
var = self.tkinter_vars.get(name)
|
| 1213 |
+
if not var and vartype:
|
| 1214 |
+
# create a Tkinter variable object with self.text as master:
|
| 1215 |
+
self.tkinter_vars[name] = var = vartype(self.text)
|
| 1216 |
+
return var
|
| 1217 |
+
|
| 1218 |
+
# Tk implementations of "virtual text methods" -- each platform
|
| 1219 |
+
# reusing IDLE's support code needs to define these for its GUI's
|
| 1220 |
+
# flavor of widget.
|
| 1221 |
+
|
| 1222 |
+
# Is character at text_index in a Python string? Return 0 for
|
| 1223 |
+
# "guaranteed no", true for anything else. This info is expensive
|
| 1224 |
+
# to compute ab initio, but is probably already known by the
|
| 1225 |
+
# platform's colorizer.
|
| 1226 |
+
|
| 1227 |
+
def is_char_in_string(self, text_index):
|
| 1228 |
+
if self.color:
|
| 1229 |
+
# Return true iff colorizer hasn't (re)gotten this far
|
| 1230 |
+
# yet, or the character is tagged as being in a string
|
| 1231 |
+
return self.text.tag_prevrange("TODO", text_index) or \
|
| 1232 |
+
"STRING" in self.text.tag_names(text_index)
|
| 1233 |
+
else:
|
| 1234 |
+
# The colorizer is missing: assume the worst
|
| 1235 |
+
return 1
|
| 1236 |
+
|
| 1237 |
+
# If a selection is defined in the text widget, return (start,
|
| 1238 |
+
# end) as Tkinter text indices, otherwise return (None, None)
|
| 1239 |
+
def get_selection_indices(self):
|
| 1240 |
+
try:
|
| 1241 |
+
first = self.text.index("sel.first")
|
| 1242 |
+
last = self.text.index("sel.last")
|
| 1243 |
+
return first, last
|
| 1244 |
+
except TclError:
|
| 1245 |
+
return None, None
|
| 1246 |
+
|
| 1247 |
+
# Return the text widget's current view of what a tab stop means
|
| 1248 |
+
# (equivalent width in spaces).
|
| 1249 |
+
|
| 1250 |
+
def get_tk_tabwidth(self):
|
| 1251 |
+
current = self.text['tabs'] or TK_TABWIDTH_DEFAULT
|
| 1252 |
+
return int(current)
|
| 1253 |
+
|
| 1254 |
+
# Set the text widget's current view of what a tab stop means.
|
| 1255 |
+
|
| 1256 |
+
def set_tk_tabwidth(self, newtabwidth):
|
| 1257 |
+
text = self.text
|
| 1258 |
+
if self.get_tk_tabwidth() != newtabwidth:
|
| 1259 |
+
# Set text widget tab width
|
| 1260 |
+
pixels = text.tk.call("font", "measure", text["font"],
|
| 1261 |
+
"-displayof", text.master,
|
| 1262 |
+
"n" * newtabwidth)
|
| 1263 |
+
text.configure(tabs=pixels)
|
| 1264 |
+
|
| 1265 |
+
### begin autoindent code ### (configuration was moved to beginning of class)
|
| 1266 |
+
|
| 1267 |
+
def set_indentation_params(self, is_py_src, guess=True):
|
| 1268 |
+
if is_py_src and guess:
|
| 1269 |
+
i = self.guess_indent()
|
| 1270 |
+
if 2 <= i <= 8:
|
| 1271 |
+
self.indentwidth = i
|
| 1272 |
+
if self.indentwidth != self.tabwidth:
|
| 1273 |
+
self.usetabs = False
|
| 1274 |
+
self.set_tk_tabwidth(self.tabwidth)
|
| 1275 |
+
|
| 1276 |
+
def smart_backspace_event(self, event):
|
| 1277 |
+
text = self.text
|
| 1278 |
+
first, last = self.get_selection_indices()
|
| 1279 |
+
if first and last:
|
| 1280 |
+
text.delete(first, last)
|
| 1281 |
+
text.mark_set("insert", first)
|
| 1282 |
+
return "break"
|
| 1283 |
+
# Delete whitespace left, until hitting a real char or closest
|
| 1284 |
+
# preceding virtual tab stop.
|
| 1285 |
+
chars = text.get("insert linestart", "insert")
|
| 1286 |
+
if chars == '':
|
| 1287 |
+
if text.compare("insert", ">", "1.0"):
|
| 1288 |
+
# easy: delete preceding newline
|
| 1289 |
+
text.delete("insert-1c")
|
| 1290 |
+
else:
|
| 1291 |
+
text.bell() # at start of buffer
|
| 1292 |
+
return "break"
|
| 1293 |
+
if chars[-1] not in " \t":
|
| 1294 |
+
# easy: delete preceding real char
|
| 1295 |
+
text.delete("insert-1c")
|
| 1296 |
+
return "break"
|
| 1297 |
+
# Ick. It may require *inserting* spaces if we back up over a
|
| 1298 |
+
# tab character! This is written to be clear, not fast.
|
| 1299 |
+
tabwidth = self.tabwidth
|
| 1300 |
+
have = len(chars.expandtabs(tabwidth))
|
| 1301 |
+
assert have > 0
|
| 1302 |
+
want = ((have - 1) // self.indentwidth) * self.indentwidth
|
| 1303 |
+
# Debug prompt is multilined....
|
| 1304 |
+
ncharsdeleted = 0
|
| 1305 |
+
while 1:
|
| 1306 |
+
if chars == self.prompt_last_line: # '' unless PyShell
|
| 1307 |
+
break
|
| 1308 |
+
chars = chars[:-1]
|
| 1309 |
+
ncharsdeleted = ncharsdeleted + 1
|
| 1310 |
+
have = len(chars.expandtabs(tabwidth))
|
| 1311 |
+
if have <= want or chars[-1] not in " \t":
|
| 1312 |
+
break
|
| 1313 |
+
text.undo_block_start()
|
| 1314 |
+
text.delete("insert-%dc" % ncharsdeleted, "insert")
|
| 1315 |
+
if have < want:
|
| 1316 |
+
text.insert("insert", ' ' * (want - have))
|
| 1317 |
+
text.undo_block_stop()
|
| 1318 |
+
return "break"
|
| 1319 |
+
|
| 1320 |
+
def smart_indent_event(self, event):
|
| 1321 |
+
# if intraline selection:
|
| 1322 |
+
# delete it
|
| 1323 |
+
# elif multiline selection:
|
| 1324 |
+
# do indent-region
|
| 1325 |
+
# else:
|
| 1326 |
+
# indent one level
|
| 1327 |
+
text = self.text
|
| 1328 |
+
first, last = self.get_selection_indices()
|
| 1329 |
+
text.undo_block_start()
|
| 1330 |
+
try:
|
| 1331 |
+
if first and last:
|
| 1332 |
+
if index2line(first) != index2line(last):
|
| 1333 |
+
return self.fregion.indent_region_event(event)
|
| 1334 |
+
text.delete(first, last)
|
| 1335 |
+
text.mark_set("insert", first)
|
| 1336 |
+
prefix = text.get("insert linestart", "insert")
|
| 1337 |
+
raw, effective = get_line_indent(prefix, self.tabwidth)
|
| 1338 |
+
if raw == len(prefix):
|
| 1339 |
+
# only whitespace to the left
|
| 1340 |
+
self.reindent_to(effective + self.indentwidth)
|
| 1341 |
+
else:
|
| 1342 |
+
# tab to the next 'stop' within or to right of line's text:
|
| 1343 |
+
if self.usetabs:
|
| 1344 |
+
pad = '\t'
|
| 1345 |
+
else:
|
| 1346 |
+
effective = len(prefix.expandtabs(self.tabwidth))
|
| 1347 |
+
n = self.indentwidth
|
| 1348 |
+
pad = ' ' * (n - effective % n)
|
| 1349 |
+
text.insert("insert", pad)
|
| 1350 |
+
text.see("insert")
|
| 1351 |
+
return "break"
|
| 1352 |
+
finally:
|
| 1353 |
+
text.undo_block_stop()
|
| 1354 |
+
|
| 1355 |
+
def newline_and_indent_event(self, event):
|
| 1356 |
+
"""Insert a newline and indentation after Enter keypress event.
|
| 1357 |
+
|
| 1358 |
+
Properly position the cursor on the new line based on information
|
| 1359 |
+
from the current line. This takes into account if the current line
|
| 1360 |
+
is a shell prompt, is empty, has selected text, contains a block
|
| 1361 |
+
opener, contains a block closer, is a continuation line, or
|
| 1362 |
+
is inside a string.
|
| 1363 |
+
"""
|
| 1364 |
+
text = self.text
|
| 1365 |
+
first, last = self.get_selection_indices()
|
| 1366 |
+
text.undo_block_start()
|
| 1367 |
+
try: # Close undo block and expose new line in finally clause.
|
| 1368 |
+
if first and last:
|
| 1369 |
+
text.delete(first, last)
|
| 1370 |
+
text.mark_set("insert", first)
|
| 1371 |
+
line = text.get("insert linestart", "insert")
|
| 1372 |
+
|
| 1373 |
+
# Count leading whitespace for indent size.
|
| 1374 |
+
i, n = 0, len(line)
|
| 1375 |
+
while i < n and line[i] in " \t":
|
| 1376 |
+
i += 1
|
| 1377 |
+
if i == n:
|
| 1378 |
+
# The cursor is in or at leading indentation in a continuation
|
| 1379 |
+
# line; just inject an empty line at the start.
|
| 1380 |
+
text.insert("insert linestart", '\n')
|
| 1381 |
+
return "break"
|
| 1382 |
+
indent = line[:i]
|
| 1383 |
+
|
| 1384 |
+
# Strip whitespace before insert point unless it's in the prompt.
|
| 1385 |
+
i = 0
|
| 1386 |
+
while line and line[-1] in " \t" and line != self.prompt_last_line:
|
| 1387 |
+
line = line[:-1]
|
| 1388 |
+
i += 1
|
| 1389 |
+
if i:
|
| 1390 |
+
text.delete("insert - %d chars" % i, "insert")
|
| 1391 |
+
|
| 1392 |
+
# Strip whitespace after insert point.
|
| 1393 |
+
while text.get("insert") in " \t":
|
| 1394 |
+
text.delete("insert")
|
| 1395 |
+
|
| 1396 |
+
# Insert new line.
|
| 1397 |
+
text.insert("insert", '\n')
|
| 1398 |
+
|
| 1399 |
+
# Adjust indentation for continuations and block open/close.
|
| 1400 |
+
# First need to find the last statement.
|
| 1401 |
+
lno = index2line(text.index('insert'))
|
| 1402 |
+
y = pyparse.Parser(self.indentwidth, self.tabwidth)
|
| 1403 |
+
if not self.prompt_last_line:
|
| 1404 |
+
for context in self.num_context_lines:
|
| 1405 |
+
startat = max(lno - context, 1)
|
| 1406 |
+
startatindex = repr(startat) + ".0"
|
| 1407 |
+
rawtext = text.get(startatindex, "insert")
|
| 1408 |
+
y.set_code(rawtext)
|
| 1409 |
+
bod = y.find_good_parse_start(
|
| 1410 |
+
self._build_char_in_string_func(startatindex))
|
| 1411 |
+
if bod is not None or startat == 1:
|
| 1412 |
+
break
|
| 1413 |
+
y.set_lo(bod or 0)
|
| 1414 |
+
else:
|
| 1415 |
+
r = text.tag_prevrange("console", "insert")
|
| 1416 |
+
if r:
|
| 1417 |
+
startatindex = r[1]
|
| 1418 |
+
else:
|
| 1419 |
+
startatindex = "1.0"
|
| 1420 |
+
rawtext = text.get(startatindex, "insert")
|
| 1421 |
+
y.set_code(rawtext)
|
| 1422 |
+
y.set_lo(0)
|
| 1423 |
+
|
| 1424 |
+
c = y.get_continuation_type()
|
| 1425 |
+
if c != pyparse.C_NONE:
|
| 1426 |
+
# The current statement hasn't ended yet.
|
| 1427 |
+
if c == pyparse.C_STRING_FIRST_LINE:
|
| 1428 |
+
# After the first line of a string do not indent at all.
|
| 1429 |
+
pass
|
| 1430 |
+
elif c == pyparse.C_STRING_NEXT_LINES:
|
| 1431 |
+
# Inside a string which started before this line;
|
| 1432 |
+
# just mimic the current indent.
|
| 1433 |
+
text.insert("insert", indent)
|
| 1434 |
+
elif c == pyparse.C_BRACKET:
|
| 1435 |
+
# Line up with the first (if any) element of the
|
| 1436 |
+
# last open bracket structure; else indent one
|
| 1437 |
+
# level beyond the indent of the line with the
|
| 1438 |
+
# last open bracket.
|
| 1439 |
+
self.reindent_to(y.compute_bracket_indent())
|
| 1440 |
+
elif c == pyparse.C_BACKSLASH:
|
| 1441 |
+
# If more than one line in this statement already, just
|
| 1442 |
+
# mimic the current indent; else if initial line
|
| 1443 |
+
# has a start on an assignment stmt, indent to
|
| 1444 |
+
# beyond leftmost =; else to beyond first chunk of
|
| 1445 |
+
# non-whitespace on initial line.
|
| 1446 |
+
if y.get_num_lines_in_stmt() > 1:
|
| 1447 |
+
text.insert("insert", indent)
|
| 1448 |
+
else:
|
| 1449 |
+
self.reindent_to(y.compute_backslash_indent())
|
| 1450 |
+
else:
|
| 1451 |
+
assert 0, "bogus continuation type %r" % (c,)
|
| 1452 |
+
return "break"
|
| 1453 |
+
|
| 1454 |
+
# This line starts a brand new statement; indent relative to
|
| 1455 |
+
# indentation of initial line of closest preceding
|
| 1456 |
+
# interesting statement.
|
| 1457 |
+
indent = y.get_base_indent_string()
|
| 1458 |
+
text.insert("insert", indent)
|
| 1459 |
+
if y.is_block_opener():
|
| 1460 |
+
self.smart_indent_event(event)
|
| 1461 |
+
elif indent and y.is_block_closer():
|
| 1462 |
+
self.smart_backspace_event(event)
|
| 1463 |
+
return "break"
|
| 1464 |
+
finally:
|
| 1465 |
+
text.see("insert")
|
| 1466 |
+
text.undo_block_stop()
|
| 1467 |
+
|
| 1468 |
+
# Our editwin provides an is_char_in_string function that works
|
| 1469 |
+
# with a Tk text index, but PyParse only knows about offsets into
|
| 1470 |
+
# a string. This builds a function for PyParse that accepts an
|
| 1471 |
+
# offset.
|
| 1472 |
+
|
| 1473 |
+
def _build_char_in_string_func(self, startindex):
|
| 1474 |
+
def inner(offset, _startindex=startindex,
|
| 1475 |
+
_icis=self.is_char_in_string):
|
| 1476 |
+
return _icis(_startindex + "+%dc" % offset)
|
| 1477 |
+
return inner
|
| 1478 |
+
|
| 1479 |
+
# XXX this isn't bound to anything -- see tabwidth comments
|
| 1480 |
+
## def change_tabwidth_event(self, event):
|
| 1481 |
+
## new = self._asktabwidth()
|
| 1482 |
+
## if new != self.tabwidth:
|
| 1483 |
+
## self.tabwidth = new
|
| 1484 |
+
## self.set_indentation_params(0, guess=0)
|
| 1485 |
+
## return "break"
|
| 1486 |
+
|
| 1487 |
+
# Make string that displays as n leading blanks.
|
| 1488 |
+
|
| 1489 |
+
def _make_blanks(self, n):
|
| 1490 |
+
if self.usetabs:
|
| 1491 |
+
ntabs, nspaces = divmod(n, self.tabwidth)
|
| 1492 |
+
return '\t' * ntabs + ' ' * nspaces
|
| 1493 |
+
else:
|
| 1494 |
+
return ' ' * n
|
| 1495 |
+
|
| 1496 |
+
# Delete from beginning of line to insert point, then reinsert
|
| 1497 |
+
# column logical (meaning use tabs if appropriate) spaces.
|
| 1498 |
+
|
| 1499 |
+
def reindent_to(self, column):
|
| 1500 |
+
text = self.text
|
| 1501 |
+
text.undo_block_start()
|
| 1502 |
+
if text.compare("insert linestart", "!=", "insert"):
|
| 1503 |
+
text.delete("insert linestart", "insert")
|
| 1504 |
+
if column:
|
| 1505 |
+
text.insert("insert", self._make_blanks(column))
|
| 1506 |
+
text.undo_block_stop()
|
| 1507 |
+
|
| 1508 |
+
# Guess indentwidth from text content.
|
| 1509 |
+
# Return guessed indentwidth. This should not be believed unless
|
| 1510 |
+
# it's in a reasonable range (e.g., it will be 0 if no indented
|
| 1511 |
+
# blocks are found).
|
| 1512 |
+
|
| 1513 |
+
def guess_indent(self):
|
| 1514 |
+
opener, indented = IndentSearcher(self.text, self.tabwidth).run()
|
| 1515 |
+
if opener and indented:
|
| 1516 |
+
raw, indentsmall = get_line_indent(opener, self.tabwidth)
|
| 1517 |
+
raw, indentlarge = get_line_indent(indented, self.tabwidth)
|
| 1518 |
+
else:
|
| 1519 |
+
indentsmall = indentlarge = 0
|
| 1520 |
+
return indentlarge - indentsmall
|
| 1521 |
+
|
| 1522 |
+
def toggle_line_numbers_event(self, event=None):
|
| 1523 |
+
if self.line_numbers is None:
|
| 1524 |
+
return
|
| 1525 |
+
|
| 1526 |
+
if self.line_numbers.is_shown:
|
| 1527 |
+
self.line_numbers.hide_sidebar()
|
| 1528 |
+
menu_label = "Show"
|
| 1529 |
+
else:
|
| 1530 |
+
self.line_numbers.show_sidebar()
|
| 1531 |
+
menu_label = "Hide"
|
| 1532 |
+
self.update_menu_label(menu='options', index='*ine*umbers',
|
| 1533 |
+
label=f'{menu_label} Line Numbers')
|
| 1534 |
+
|
| 1535 |
+
# "line.col" -> line, as an int
|
| 1536 |
+
def index2line(index):
|
| 1537 |
+
return int(float(index))
|
| 1538 |
+
|
| 1539 |
+
|
| 1540 |
+
_line_indent_re = re.compile(r'[ \t]*')
|
| 1541 |
+
def get_line_indent(line, tabwidth):
|
| 1542 |
+
"""Return a line's indentation as (# chars, effective # of spaces).
|
| 1543 |
+
|
| 1544 |
+
The effective # of spaces is the length after properly "expanding"
|
| 1545 |
+
the tabs into spaces, as done by str.expandtabs(tabwidth).
|
| 1546 |
+
"""
|
| 1547 |
+
m = _line_indent_re.match(line)
|
| 1548 |
+
return m.end(), len(m.group().expandtabs(tabwidth))
|
| 1549 |
+
|
| 1550 |
+
|
| 1551 |
+
class IndentSearcher:
|
| 1552 |
+
|
| 1553 |
+
# .run() chews over the Text widget, looking for a block opener
|
| 1554 |
+
# and the stmt following it. Returns a pair,
|
| 1555 |
+
# (line containing block opener, line containing stmt)
|
| 1556 |
+
# Either or both may be None.
|
| 1557 |
+
|
| 1558 |
+
def __init__(self, text, tabwidth):
|
| 1559 |
+
self.text = text
|
| 1560 |
+
self.tabwidth = tabwidth
|
| 1561 |
+
self.i = self.finished = 0
|
| 1562 |
+
self.blkopenline = self.indentedline = None
|
| 1563 |
+
|
| 1564 |
+
def readline(self):
|
| 1565 |
+
if self.finished:
|
| 1566 |
+
return ""
|
| 1567 |
+
i = self.i = self.i + 1
|
| 1568 |
+
mark = repr(i) + ".0"
|
| 1569 |
+
if self.text.compare(mark, ">=", "end"):
|
| 1570 |
+
return ""
|
| 1571 |
+
return self.text.get(mark, mark + " lineend+1c")
|
| 1572 |
+
|
| 1573 |
+
def tokeneater(self, type, token, start, end, line,
|
| 1574 |
+
INDENT=tokenize.INDENT,
|
| 1575 |
+
NAME=tokenize.NAME,
|
| 1576 |
+
OPENERS=('class', 'def', 'for', 'if', 'try', 'while')):
|
| 1577 |
+
if self.finished:
|
| 1578 |
+
pass
|
| 1579 |
+
elif type == NAME and token in OPENERS:
|
| 1580 |
+
self.blkopenline = line
|
| 1581 |
+
elif type == INDENT and self.blkopenline:
|
| 1582 |
+
self.indentedline = line
|
| 1583 |
+
self.finished = 1
|
| 1584 |
+
|
| 1585 |
+
def run(self):
|
| 1586 |
+
save_tabsize = tokenize.tabsize
|
| 1587 |
+
tokenize.tabsize = self.tabwidth
|
| 1588 |
+
try:
|
| 1589 |
+
try:
|
| 1590 |
+
tokens = tokenize.generate_tokens(self.readline)
|
| 1591 |
+
for token in tokens:
|
| 1592 |
+
self.tokeneater(*token)
|
| 1593 |
+
except (tokenize.TokenError, SyntaxError):
|
| 1594 |
+
# since we cut off the tokenizer early, we can trigger
|
| 1595 |
+
# spurious errors
|
| 1596 |
+
pass
|
| 1597 |
+
finally:
|
| 1598 |
+
tokenize.tabsize = save_tabsize
|
| 1599 |
+
return self.blkopenline, self.indentedline
|
| 1600 |
+
|
| 1601 |
+
### end autoindent code ###
|
| 1602 |
+
|
| 1603 |
+
def prepstr(s):
|
| 1604 |
+
# Helper to extract the underscore from a string, e.g.
|
| 1605 |
+
# prepstr("Co_py") returns (2, "Copy").
|
| 1606 |
+
i = s.find('_')
|
| 1607 |
+
if i >= 0:
|
| 1608 |
+
s = s[:i] + s[i+1:]
|
| 1609 |
+
return i, s
|
| 1610 |
+
|
| 1611 |
+
|
| 1612 |
+
keynames = {
|
| 1613 |
+
'bracketleft': '[',
|
| 1614 |
+
'bracketright': ']',
|
| 1615 |
+
'slash': '/',
|
| 1616 |
+
}
|
| 1617 |
+
|
| 1618 |
+
def get_accelerator(keydefs, eventname):
|
| 1619 |
+
keylist = keydefs.get(eventname)
|
| 1620 |
+
# issue10940: temporary workaround to prevent hang with OS X Cocoa Tk 8.5
|
| 1621 |
+
# if not keylist:
|
| 1622 |
+
if (not keylist) or (macosx.isCocoaTk() and eventname in {
|
| 1623 |
+
"<<open-module>>",
|
| 1624 |
+
"<<goto-line>>",
|
| 1625 |
+
"<<change-indentwidth>>"}):
|
| 1626 |
+
return ""
|
| 1627 |
+
s = keylist[0]
|
| 1628 |
+
s = re.sub(r"-[a-z]\b", lambda m: m.group().upper(), s)
|
| 1629 |
+
s = re.sub(r"\b\w+\b", lambda m: keynames.get(m.group(), m.group()), s)
|
| 1630 |
+
s = re.sub("Key-", "", s)
|
| 1631 |
+
s = re.sub("Cancel","Ctrl-Break",s) # dscherer@cmu.edu
|
| 1632 |
+
s = re.sub("Control-", "Ctrl-", s)
|
| 1633 |
+
s = re.sub("-", "+", s)
|
| 1634 |
+
s = re.sub("><", " ", s)
|
| 1635 |
+
s = re.sub("<", "", s)
|
| 1636 |
+
s = re.sub(">", "", s)
|
| 1637 |
+
return s
|
| 1638 |
+
|
| 1639 |
+
|
| 1640 |
+
def fixwordbreaks(root):
|
| 1641 |
+
# On Windows, tcl/tk breaks 'words' only on spaces, as in Command Prompt.
|
| 1642 |
+
# We want Motif style everywhere. See #21474, msg218992 and followup.
|
| 1643 |
+
tk = root.tk
|
| 1644 |
+
tk.call('tcl_wordBreakAfter', 'a b', 0) # make sure word.tcl is loaded
|
| 1645 |
+
tk.call('set', 'tcl_wordchars', r'\w')
|
| 1646 |
+
tk.call('set', 'tcl_nonwordchars', r'\W')
|
| 1647 |
+
|
| 1648 |
+
|
| 1649 |
+
def _editor_window(parent): # htest #
|
| 1650 |
+
# error if close master window first - timer event, after script
|
| 1651 |
+
root = parent
|
| 1652 |
+
fixwordbreaks(root)
|
| 1653 |
+
if sys.argv[1:]:
|
| 1654 |
+
filename = sys.argv[1]
|
| 1655 |
+
else:
|
| 1656 |
+
filename = None
|
| 1657 |
+
macosx.setupApp(root, None)
|
| 1658 |
+
edit = EditorWindow(root=root, filename=filename)
|
| 1659 |
+
text = edit.text
|
| 1660 |
+
text['height'] = 10
|
| 1661 |
+
for i in range(20):
|
| 1662 |
+
text.insert('insert', ' '*i + str(i) + '\n')
|
| 1663 |
+
# text.bind("<<close-all-windows>>", edit.close_event)
|
| 1664 |
+
# Does not stop error, neither does following
|
| 1665 |
+
# edit.text.bind("<<close-window>>", edit.close_event)
|
| 1666 |
+
|
| 1667 |
+
if __name__ == '__main__':
|
| 1668 |
+
from unittest import main
|
| 1669 |
+
main('idlelib.idle_test.test_editor', verbosity=2, exit=False)
|
| 1670 |
+
|
| 1671 |
+
from idlelib.idle_test.htest import run
|
| 1672 |
+
run(_editor_window)
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/extend.txt
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Writing an IDLE extension
|
| 2 |
+
=========================
|
| 3 |
+
|
| 4 |
+
An IDLE extension can define new key bindings and menu entries for IDLE
|
| 5 |
+
edit windows. There is a simple mechanism to load extensions when IDLE
|
| 6 |
+
starts up and to attach them to each edit window. (It is also possible
|
| 7 |
+
to make other changes to IDLE, but this must be done by editing the IDLE
|
| 8 |
+
source code.)
|
| 9 |
+
|
| 10 |
+
The list of extensions loaded at startup time is configured by editing
|
| 11 |
+
the file config-extensions.def. See below for details.
|
| 12 |
+
|
| 13 |
+
An IDLE extension is defined by a class. Methods of the class define
|
| 14 |
+
actions that are invoked by event bindings or menu entries. Class (or
|
| 15 |
+
instance) variables define the bindings and menu additions; these are
|
| 16 |
+
automatically applied by IDLE when the extension is linked to an edit
|
| 17 |
+
window.
|
| 18 |
+
|
| 19 |
+
An IDLE extension class is instantiated with a single argument,
|
| 20 |
+
`editwin', an EditorWindow instance. The extension cannot assume much
|
| 21 |
+
about this argument, but it is guaranteed to have the following instance
|
| 22 |
+
variables:
|
| 23 |
+
|
| 24 |
+
text a Text instance (a widget)
|
| 25 |
+
io an IOBinding instance (more about this later)
|
| 26 |
+
flist the FileList instance (shared by all edit windows)
|
| 27 |
+
|
| 28 |
+
(There are a few more, but they are rarely useful.)
|
| 29 |
+
|
| 30 |
+
The extension class must not directly bind Window Manager (e.g. X) events.
|
| 31 |
+
Rather, it must define one or more virtual events, e.g. <<z-in>>, and
|
| 32 |
+
corresponding methods, e.g. z_in_event(). The virtual events will be
|
| 33 |
+
bound to the corresponding methods, and Window Manager events can then be bound
|
| 34 |
+
to the virtual events. (This indirection is done so that the key bindings can
|
| 35 |
+
easily be changed, and so that other sources of virtual events can exist, such
|
| 36 |
+
as menu entries.)
|
| 37 |
+
|
| 38 |
+
An extension can define menu entries. This is done with a class or instance
|
| 39 |
+
variable named menudefs; it should be a list of pairs, where each pair is a
|
| 40 |
+
menu name (lowercase) and a list of menu entries. Each menu entry is either
|
| 41 |
+
None (to insert a separator entry) or a pair of strings (menu_label,
|
| 42 |
+
virtual_event). Here, menu_label is the label of the menu entry, and
|
| 43 |
+
virtual_event is the virtual event to be generated when the entry is selected.
|
| 44 |
+
An underscore in the menu label is removed; the character following the
|
| 45 |
+
underscore is displayed underlined, to indicate the shortcut character (for
|
| 46 |
+
Windows).
|
| 47 |
+
|
| 48 |
+
At the moment, extensions cannot define whole new menus; they must define
|
| 49 |
+
entries in existing menus. Some menus are not present on some windows; such
|
| 50 |
+
entry definitions are then ignored, but key bindings are still applied. (This
|
| 51 |
+
should probably be refined in the future.)
|
| 52 |
+
|
| 53 |
+
Extensions are not required to define menu entries for all the events they
|
| 54 |
+
implement. (They are also not required to create keybindings, but in that
|
| 55 |
+
case there must be empty bindings in cofig-extensions.def)
|
| 56 |
+
|
| 57 |
+
Here is a partial example from zzdummy.py:
|
| 58 |
+
|
| 59 |
+
class ZzDummy:
|
| 60 |
+
|
| 61 |
+
menudefs = [
|
| 62 |
+
('format', [
|
| 63 |
+
('Z in', '<<z-in>>'),
|
| 64 |
+
('Z out', '<<z-out>>'),
|
| 65 |
+
] )
|
| 66 |
+
]
|
| 67 |
+
|
| 68 |
+
def __init__(self, editwin):
|
| 69 |
+
self.editwin = editwin
|
| 70 |
+
|
| 71 |
+
def z_in_event(self, event=None):
|
| 72 |
+
"...Do what you want here..."
|
| 73 |
+
|
| 74 |
+
The final piece of the puzzle is the file "config-extensions.def", which is
|
| 75 |
+
used to configure the loading of extensions and to establish key (or, more
|
| 76 |
+
generally, event) bindings to the virtual events defined in the extensions.
|
| 77 |
+
|
| 78 |
+
See the comments at the top of config-extensions.def for information. It's
|
| 79 |
+
currently necessary to manually modify that file to change IDLE's extension
|
| 80 |
+
loading or extension key bindings.
|
| 81 |
+
|
| 82 |
+
For further information on binding refer to the Tkinter Resources web page at
|
| 83 |
+
python.org and to the Tk Command "bind" man page.
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/filelist.py
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"idlelib.filelist"
|
| 2 |
+
|
| 3 |
+
import os
|
| 4 |
+
from tkinter import messagebox
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
class FileList:
|
| 8 |
+
|
| 9 |
+
# N.B. this import overridden in PyShellFileList.
|
| 10 |
+
from idlelib.editor import EditorWindow
|
| 11 |
+
|
| 12 |
+
def __init__(self, root):
|
| 13 |
+
self.root = root
|
| 14 |
+
self.dict = {}
|
| 15 |
+
self.inversedict = {}
|
| 16 |
+
self.vars = {} # For EditorWindow.getrawvar (shared Tcl variables)
|
| 17 |
+
|
| 18 |
+
def open(self, filename, action=None):
|
| 19 |
+
assert filename
|
| 20 |
+
filename = self.canonize(filename)
|
| 21 |
+
if os.path.isdir(filename):
|
| 22 |
+
# This can happen when bad filename is passed on command line:
|
| 23 |
+
messagebox.showerror(
|
| 24 |
+
"File Error",
|
| 25 |
+
"%r is a directory." % (filename,),
|
| 26 |
+
master=self.root)
|
| 27 |
+
return None
|
| 28 |
+
key = os.path.normcase(filename)
|
| 29 |
+
if key in self.dict:
|
| 30 |
+
edit = self.dict[key]
|
| 31 |
+
edit.top.wakeup()
|
| 32 |
+
return edit
|
| 33 |
+
if action:
|
| 34 |
+
# Don't create window, perform 'action', e.g. open in same window
|
| 35 |
+
return action(filename)
|
| 36 |
+
else:
|
| 37 |
+
edit = self.EditorWindow(self, filename, key)
|
| 38 |
+
if edit.good_load:
|
| 39 |
+
return edit
|
| 40 |
+
else:
|
| 41 |
+
edit._close()
|
| 42 |
+
return None
|
| 43 |
+
|
| 44 |
+
def gotofileline(self, filename, lineno=None):
|
| 45 |
+
edit = self.open(filename)
|
| 46 |
+
if edit is not None and lineno is not None:
|
| 47 |
+
edit.gotoline(lineno)
|
| 48 |
+
|
| 49 |
+
def new(self, filename=None):
|
| 50 |
+
return self.EditorWindow(self, filename)
|
| 51 |
+
|
| 52 |
+
def close_all_callback(self, *args, **kwds):
|
| 53 |
+
for edit in list(self.inversedict):
|
| 54 |
+
reply = edit.close()
|
| 55 |
+
if reply == "cancel":
|
| 56 |
+
break
|
| 57 |
+
return "break"
|
| 58 |
+
|
| 59 |
+
def unregister_maybe_terminate(self, edit):
|
| 60 |
+
try:
|
| 61 |
+
key = self.inversedict[edit]
|
| 62 |
+
except KeyError:
|
| 63 |
+
print("Don't know this EditorWindow object. (close)")
|
| 64 |
+
return
|
| 65 |
+
if key:
|
| 66 |
+
del self.dict[key]
|
| 67 |
+
del self.inversedict[edit]
|
| 68 |
+
if not self.inversedict:
|
| 69 |
+
self.root.quit()
|
| 70 |
+
|
| 71 |
+
def filename_changed_edit(self, edit):
|
| 72 |
+
edit.saved_change_hook()
|
| 73 |
+
try:
|
| 74 |
+
key = self.inversedict[edit]
|
| 75 |
+
except KeyError:
|
| 76 |
+
print("Don't know this EditorWindow object. (rename)")
|
| 77 |
+
return
|
| 78 |
+
filename = edit.io.filename
|
| 79 |
+
if not filename:
|
| 80 |
+
if key:
|
| 81 |
+
del self.dict[key]
|
| 82 |
+
self.inversedict[edit] = None
|
| 83 |
+
return
|
| 84 |
+
filename = self.canonize(filename)
|
| 85 |
+
newkey = os.path.normcase(filename)
|
| 86 |
+
if newkey == key:
|
| 87 |
+
return
|
| 88 |
+
if newkey in self.dict:
|
| 89 |
+
conflict = self.dict[newkey]
|
| 90 |
+
self.inversedict[conflict] = None
|
| 91 |
+
messagebox.showerror(
|
| 92 |
+
"Name Conflict",
|
| 93 |
+
"You now have multiple edit windows open for %r" % (filename,),
|
| 94 |
+
master=self.root)
|
| 95 |
+
self.dict[newkey] = edit
|
| 96 |
+
self.inversedict[edit] = newkey
|
| 97 |
+
if key:
|
| 98 |
+
try:
|
| 99 |
+
del self.dict[key]
|
| 100 |
+
except KeyError:
|
| 101 |
+
pass
|
| 102 |
+
|
| 103 |
+
def canonize(self, filename):
|
| 104 |
+
if not os.path.isabs(filename):
|
| 105 |
+
try:
|
| 106 |
+
pwd = os.getcwd()
|
| 107 |
+
except OSError:
|
| 108 |
+
pass
|
| 109 |
+
else:
|
| 110 |
+
filename = os.path.join(pwd, filename)
|
| 111 |
+
return os.path.normpath(filename)
|
| 112 |
+
|
| 113 |
+
|
| 114 |
+
def _test(): # TODO check and convert to htest
|
| 115 |
+
from tkinter import Tk
|
| 116 |
+
from idlelib.editor import fixwordbreaks
|
| 117 |
+
from idlelib.run import fix_scaling
|
| 118 |
+
root = Tk()
|
| 119 |
+
fix_scaling(root)
|
| 120 |
+
fixwordbreaks(root)
|
| 121 |
+
root.withdraw()
|
| 122 |
+
flist = FileList(root)
|
| 123 |
+
flist.new()
|
| 124 |
+
if flist.inversedict:
|
| 125 |
+
root.mainloop()
|
| 126 |
+
|
| 127 |
+
if __name__ == '__main__':
|
| 128 |
+
from unittest import main
|
| 129 |
+
main('idlelib.idle_test.test_filelist', verbosity=2)
|
| 130 |
+
|
| 131 |
+
# _test()
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/format.py
ADDED
|
@@ -0,0 +1,426 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Format all or a selected region (line slice) of text.
|
| 2 |
+
|
| 3 |
+
Region formatting options: paragraph, comment block, indent, deindent,
|
| 4 |
+
comment, uncomment, tabify, and untabify.
|
| 5 |
+
|
| 6 |
+
File renamed from paragraph.py with functions added from editor.py.
|
| 7 |
+
"""
|
| 8 |
+
import re
|
| 9 |
+
from tkinter.messagebox import askyesno
|
| 10 |
+
from tkinter.simpledialog import askinteger
|
| 11 |
+
from idlelib.config import idleConf
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
class FormatParagraph:
|
| 15 |
+
"""Format a paragraph, comment block, or selection to a max width.
|
| 16 |
+
|
| 17 |
+
Does basic, standard text formatting, and also understands Python
|
| 18 |
+
comment blocks. Thus, for editing Python source code, this
|
| 19 |
+
extension is really only suitable for reformatting these comment
|
| 20 |
+
blocks or triple-quoted strings.
|
| 21 |
+
|
| 22 |
+
Known problems with comment reformatting:
|
| 23 |
+
* If there is a selection marked, and the first line of the
|
| 24 |
+
selection is not complete, the block will probably not be detected
|
| 25 |
+
as comments, and will have the normal "text formatting" rules
|
| 26 |
+
applied.
|
| 27 |
+
* If a comment block has leading whitespace that mixes tabs and
|
| 28 |
+
spaces, they will not be considered part of the same block.
|
| 29 |
+
* Fancy comments, like this bulleted list, aren't handled :-)
|
| 30 |
+
"""
|
| 31 |
+
def __init__(self, editwin):
|
| 32 |
+
self.editwin = editwin
|
| 33 |
+
|
| 34 |
+
@classmethod
|
| 35 |
+
def reload(cls):
|
| 36 |
+
cls.max_width = idleConf.GetOption('extensions', 'FormatParagraph',
|
| 37 |
+
'max-width', type='int', default=72)
|
| 38 |
+
|
| 39 |
+
def close(self):
|
| 40 |
+
self.editwin = None
|
| 41 |
+
|
| 42 |
+
def format_paragraph_event(self, event, limit=None):
|
| 43 |
+
"""Formats paragraph to a max width specified in idleConf.
|
| 44 |
+
|
| 45 |
+
If text is selected, format_paragraph_event will start breaking lines
|
| 46 |
+
at the max width, starting from the beginning selection.
|
| 47 |
+
|
| 48 |
+
If no text is selected, format_paragraph_event uses the current
|
| 49 |
+
cursor location to determine the paragraph (lines of text surrounded
|
| 50 |
+
by blank lines) and formats it.
|
| 51 |
+
|
| 52 |
+
The length limit parameter is for testing with a known value.
|
| 53 |
+
"""
|
| 54 |
+
limit = self.max_width if limit is None else limit
|
| 55 |
+
text = self.editwin.text
|
| 56 |
+
first, last = self.editwin.get_selection_indices()
|
| 57 |
+
if first and last:
|
| 58 |
+
data = text.get(first, last)
|
| 59 |
+
comment_header = get_comment_header(data)
|
| 60 |
+
else:
|
| 61 |
+
first, last, comment_header, data = \
|
| 62 |
+
find_paragraph(text, text.index("insert"))
|
| 63 |
+
if comment_header:
|
| 64 |
+
newdata = reformat_comment(data, limit, comment_header)
|
| 65 |
+
else:
|
| 66 |
+
newdata = reformat_paragraph(data, limit)
|
| 67 |
+
text.tag_remove("sel", "1.0", "end")
|
| 68 |
+
|
| 69 |
+
if newdata != data:
|
| 70 |
+
text.mark_set("insert", first)
|
| 71 |
+
text.undo_block_start()
|
| 72 |
+
text.delete(first, last)
|
| 73 |
+
text.insert(first, newdata)
|
| 74 |
+
text.undo_block_stop()
|
| 75 |
+
else:
|
| 76 |
+
text.mark_set("insert", last)
|
| 77 |
+
text.see("insert")
|
| 78 |
+
return "break"
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
FormatParagraph.reload()
|
| 82 |
+
|
| 83 |
+
def find_paragraph(text, mark):
|
| 84 |
+
"""Returns the start/stop indices enclosing the paragraph that mark is in.
|
| 85 |
+
|
| 86 |
+
Also returns the comment format string, if any, and paragraph of text
|
| 87 |
+
between the start/stop indices.
|
| 88 |
+
"""
|
| 89 |
+
lineno, col = map(int, mark.split("."))
|
| 90 |
+
line = text.get("%d.0" % lineno, "%d.end" % lineno)
|
| 91 |
+
|
| 92 |
+
# Look for start of next paragraph if the index passed in is a blank line
|
| 93 |
+
while text.compare("%d.0" % lineno, "<", "end") and is_all_white(line):
|
| 94 |
+
lineno = lineno + 1
|
| 95 |
+
line = text.get("%d.0" % lineno, "%d.end" % lineno)
|
| 96 |
+
first_lineno = lineno
|
| 97 |
+
comment_header = get_comment_header(line)
|
| 98 |
+
comment_header_len = len(comment_header)
|
| 99 |
+
|
| 100 |
+
# Once start line found, search for end of paragraph (a blank line)
|
| 101 |
+
while get_comment_header(line)==comment_header and \
|
| 102 |
+
not is_all_white(line[comment_header_len:]):
|
| 103 |
+
lineno = lineno + 1
|
| 104 |
+
line = text.get("%d.0" % lineno, "%d.end" % lineno)
|
| 105 |
+
last = "%d.0" % lineno
|
| 106 |
+
|
| 107 |
+
# Search back to beginning of paragraph (first blank line before)
|
| 108 |
+
lineno = first_lineno - 1
|
| 109 |
+
line = text.get("%d.0" % lineno, "%d.end" % lineno)
|
| 110 |
+
while lineno > 0 and \
|
| 111 |
+
get_comment_header(line)==comment_header and \
|
| 112 |
+
not is_all_white(line[comment_header_len:]):
|
| 113 |
+
lineno = lineno - 1
|
| 114 |
+
line = text.get("%d.0" % lineno, "%d.end" % lineno)
|
| 115 |
+
first = "%d.0" % (lineno+1)
|
| 116 |
+
|
| 117 |
+
return first, last, comment_header, text.get(first, last)
|
| 118 |
+
|
| 119 |
+
# This should perhaps be replaced with textwrap.wrap
|
| 120 |
+
def reformat_paragraph(data, limit):
|
| 121 |
+
"""Return data reformatted to specified width (limit)."""
|
| 122 |
+
lines = data.split("\n")
|
| 123 |
+
i = 0
|
| 124 |
+
n = len(lines)
|
| 125 |
+
while i < n and is_all_white(lines[i]):
|
| 126 |
+
i = i+1
|
| 127 |
+
if i >= n:
|
| 128 |
+
return data
|
| 129 |
+
indent1 = get_indent(lines[i])
|
| 130 |
+
if i+1 < n and not is_all_white(lines[i+1]):
|
| 131 |
+
indent2 = get_indent(lines[i+1])
|
| 132 |
+
else:
|
| 133 |
+
indent2 = indent1
|
| 134 |
+
new = lines[:i]
|
| 135 |
+
partial = indent1
|
| 136 |
+
while i < n and not is_all_white(lines[i]):
|
| 137 |
+
# XXX Should take double space after period (etc.) into account
|
| 138 |
+
words = re.split(r"(\s+)", lines[i])
|
| 139 |
+
for j in range(0, len(words), 2):
|
| 140 |
+
word = words[j]
|
| 141 |
+
if not word:
|
| 142 |
+
continue # Can happen when line ends in whitespace
|
| 143 |
+
if len((partial + word).expandtabs()) > limit and \
|
| 144 |
+
partial != indent1:
|
| 145 |
+
new.append(partial.rstrip())
|
| 146 |
+
partial = indent2
|
| 147 |
+
partial = partial + word + " "
|
| 148 |
+
if j+1 < len(words) and words[j+1] != " ":
|
| 149 |
+
partial = partial + " "
|
| 150 |
+
i = i+1
|
| 151 |
+
new.append(partial.rstrip())
|
| 152 |
+
# XXX Should reformat remaining paragraphs as well
|
| 153 |
+
new.extend(lines[i:])
|
| 154 |
+
return "\n".join(new)
|
| 155 |
+
|
| 156 |
+
def reformat_comment(data, limit, comment_header):
|
| 157 |
+
"""Return data reformatted to specified width with comment header."""
|
| 158 |
+
|
| 159 |
+
# Remove header from the comment lines
|
| 160 |
+
lc = len(comment_header)
|
| 161 |
+
data = "\n".join(line[lc:] for line in data.split("\n"))
|
| 162 |
+
# Reformat to maxformatwidth chars or a 20 char width,
|
| 163 |
+
# whichever is greater.
|
| 164 |
+
format_width = max(limit - len(comment_header), 20)
|
| 165 |
+
newdata = reformat_paragraph(data, format_width)
|
| 166 |
+
# re-split and re-insert the comment header.
|
| 167 |
+
newdata = newdata.split("\n")
|
| 168 |
+
# If the block ends in a \n, we don't want the comment prefix
|
| 169 |
+
# inserted after it. (Im not sure it makes sense to reformat a
|
| 170 |
+
# comment block that is not made of complete lines, but whatever!)
|
| 171 |
+
# Can't think of a clean solution, so we hack away
|
| 172 |
+
block_suffix = ""
|
| 173 |
+
if not newdata[-1]:
|
| 174 |
+
block_suffix = "\n"
|
| 175 |
+
newdata = newdata[:-1]
|
| 176 |
+
return '\n'.join(comment_header+line for line in newdata) + block_suffix
|
| 177 |
+
|
| 178 |
+
def is_all_white(line):
|
| 179 |
+
"""Return True if line is empty or all whitespace."""
|
| 180 |
+
|
| 181 |
+
return re.match(r"^\s*$", line) is not None
|
| 182 |
+
|
| 183 |
+
def get_indent(line):
|
| 184 |
+
"""Return the initial space or tab indent of line."""
|
| 185 |
+
return re.match(r"^([ \t]*)", line).group()
|
| 186 |
+
|
| 187 |
+
def get_comment_header(line):
|
| 188 |
+
"""Return string with leading whitespace and '#' from line or ''.
|
| 189 |
+
|
| 190 |
+
A null return indicates that the line is not a comment line. A non-
|
| 191 |
+
null return, such as ' #', will be used to find the other lines of
|
| 192 |
+
a comment block with the same indent.
|
| 193 |
+
"""
|
| 194 |
+
m = re.match(r"^([ \t]*#*)", line)
|
| 195 |
+
if m is None: return ""
|
| 196 |
+
return m.group(1)
|
| 197 |
+
|
| 198 |
+
|
| 199 |
+
# Copied from editor.py; importing it would cause an import cycle.
|
| 200 |
+
_line_indent_re = re.compile(r'[ \t]*')
|
| 201 |
+
|
| 202 |
+
def get_line_indent(line, tabwidth):
|
| 203 |
+
"""Return a line's indentation as (# chars, effective # of spaces).
|
| 204 |
+
|
| 205 |
+
The effective # of spaces is the length after properly "expanding"
|
| 206 |
+
the tabs into spaces, as done by str.expandtabs(tabwidth).
|
| 207 |
+
"""
|
| 208 |
+
m = _line_indent_re.match(line)
|
| 209 |
+
return m.end(), len(m.group().expandtabs(tabwidth))
|
| 210 |
+
|
| 211 |
+
|
| 212 |
+
class FormatRegion:
|
| 213 |
+
"Format selected text (region)."
|
| 214 |
+
|
| 215 |
+
def __init__(self, editwin):
|
| 216 |
+
self.editwin = editwin
|
| 217 |
+
|
| 218 |
+
def get_region(self):
|
| 219 |
+
"""Return line information about the selected text region.
|
| 220 |
+
|
| 221 |
+
If text is selected, the first and last indices will be
|
| 222 |
+
for the selection. If there is no text selected, the
|
| 223 |
+
indices will be the current cursor location.
|
| 224 |
+
|
| 225 |
+
Return a tuple containing (first index, last index,
|
| 226 |
+
string representation of text, list of text lines).
|
| 227 |
+
"""
|
| 228 |
+
text = self.editwin.text
|
| 229 |
+
first, last = self.editwin.get_selection_indices()
|
| 230 |
+
if first and last:
|
| 231 |
+
head = text.index(first + " linestart")
|
| 232 |
+
tail = text.index(last + "-1c lineend +1c")
|
| 233 |
+
else:
|
| 234 |
+
head = text.index("insert linestart")
|
| 235 |
+
tail = text.index("insert lineend +1c")
|
| 236 |
+
chars = text.get(head, tail)
|
| 237 |
+
lines = chars.split("\n")
|
| 238 |
+
return head, tail, chars, lines
|
| 239 |
+
|
| 240 |
+
def set_region(self, head, tail, chars, lines):
|
| 241 |
+
"""Replace the text between the given indices.
|
| 242 |
+
|
| 243 |
+
Args:
|
| 244 |
+
head: Starting index of text to replace.
|
| 245 |
+
tail: Ending index of text to replace.
|
| 246 |
+
chars: Expected to be string of current text
|
| 247 |
+
between head and tail.
|
| 248 |
+
lines: List of new lines to insert between head
|
| 249 |
+
and tail.
|
| 250 |
+
"""
|
| 251 |
+
text = self.editwin.text
|
| 252 |
+
newchars = "\n".join(lines)
|
| 253 |
+
if newchars == chars:
|
| 254 |
+
text.bell()
|
| 255 |
+
return
|
| 256 |
+
text.tag_remove("sel", "1.0", "end")
|
| 257 |
+
text.mark_set("insert", head)
|
| 258 |
+
text.undo_block_start()
|
| 259 |
+
text.delete(head, tail)
|
| 260 |
+
text.insert(head, newchars)
|
| 261 |
+
text.undo_block_stop()
|
| 262 |
+
text.tag_add("sel", head, "insert")
|
| 263 |
+
|
| 264 |
+
def indent_region_event(self, event=None):
|
| 265 |
+
"Indent region by indentwidth spaces."
|
| 266 |
+
head, tail, chars, lines = self.get_region()
|
| 267 |
+
for pos in range(len(lines)):
|
| 268 |
+
line = lines[pos]
|
| 269 |
+
if line:
|
| 270 |
+
raw, effective = get_line_indent(line, self.editwin.tabwidth)
|
| 271 |
+
effective = effective + self.editwin.indentwidth
|
| 272 |
+
lines[pos] = self.editwin._make_blanks(effective) + line[raw:]
|
| 273 |
+
self.set_region(head, tail, chars, lines)
|
| 274 |
+
return "break"
|
| 275 |
+
|
| 276 |
+
def dedent_region_event(self, event=None):
|
| 277 |
+
"Dedent region by indentwidth spaces."
|
| 278 |
+
head, tail, chars, lines = self.get_region()
|
| 279 |
+
for pos in range(len(lines)):
|
| 280 |
+
line = lines[pos]
|
| 281 |
+
if line:
|
| 282 |
+
raw, effective = get_line_indent(line, self.editwin.tabwidth)
|
| 283 |
+
effective = max(effective - self.editwin.indentwidth, 0)
|
| 284 |
+
lines[pos] = self.editwin._make_blanks(effective) + line[raw:]
|
| 285 |
+
self.set_region(head, tail, chars, lines)
|
| 286 |
+
return "break"
|
| 287 |
+
|
| 288 |
+
def comment_region_event(self, event=None):
|
| 289 |
+
"""Comment out each line in region.
|
| 290 |
+
|
| 291 |
+
## is appended to the beginning of each line to comment it out.
|
| 292 |
+
"""
|
| 293 |
+
head, tail, chars, lines = self.get_region()
|
| 294 |
+
for pos in range(len(lines) - 1):
|
| 295 |
+
line = lines[pos]
|
| 296 |
+
lines[pos] = '##' + line
|
| 297 |
+
self.set_region(head, tail, chars, lines)
|
| 298 |
+
return "break"
|
| 299 |
+
|
| 300 |
+
def uncomment_region_event(self, event=None):
|
| 301 |
+
"""Uncomment each line in region.
|
| 302 |
+
|
| 303 |
+
Remove ## or # in the first positions of a line. If the comment
|
| 304 |
+
is not in the beginning position, this command will have no effect.
|
| 305 |
+
"""
|
| 306 |
+
head, tail, chars, lines = self.get_region()
|
| 307 |
+
for pos in range(len(lines)):
|
| 308 |
+
line = lines[pos]
|
| 309 |
+
if not line:
|
| 310 |
+
continue
|
| 311 |
+
if line[:2] == '##':
|
| 312 |
+
line = line[2:]
|
| 313 |
+
elif line[:1] == '#':
|
| 314 |
+
line = line[1:]
|
| 315 |
+
lines[pos] = line
|
| 316 |
+
self.set_region(head, tail, chars, lines)
|
| 317 |
+
return "break"
|
| 318 |
+
|
| 319 |
+
def tabify_region_event(self, event=None):
|
| 320 |
+
"Convert leading spaces to tabs for each line in selected region."
|
| 321 |
+
head, tail, chars, lines = self.get_region()
|
| 322 |
+
tabwidth = self._asktabwidth()
|
| 323 |
+
if tabwidth is None:
|
| 324 |
+
return
|
| 325 |
+
for pos in range(len(lines)):
|
| 326 |
+
line = lines[pos]
|
| 327 |
+
if line:
|
| 328 |
+
raw, effective = get_line_indent(line, tabwidth)
|
| 329 |
+
ntabs, nspaces = divmod(effective, tabwidth)
|
| 330 |
+
lines[pos] = '\t' * ntabs + ' ' * nspaces + line[raw:]
|
| 331 |
+
self.set_region(head, tail, chars, lines)
|
| 332 |
+
return "break"
|
| 333 |
+
|
| 334 |
+
def untabify_region_event(self, event=None):
|
| 335 |
+
"Expand tabs to spaces for each line in region."
|
| 336 |
+
head, tail, chars, lines = self.get_region()
|
| 337 |
+
tabwidth = self._asktabwidth()
|
| 338 |
+
if tabwidth is None:
|
| 339 |
+
return
|
| 340 |
+
for pos in range(len(lines)):
|
| 341 |
+
lines[pos] = lines[pos].expandtabs(tabwidth)
|
| 342 |
+
self.set_region(head, tail, chars, lines)
|
| 343 |
+
return "break"
|
| 344 |
+
|
| 345 |
+
def _asktabwidth(self):
|
| 346 |
+
"Return value for tab width."
|
| 347 |
+
return askinteger(
|
| 348 |
+
"Tab width",
|
| 349 |
+
"Columns per tab? (2-16)",
|
| 350 |
+
parent=self.editwin.text,
|
| 351 |
+
initialvalue=self.editwin.indentwidth,
|
| 352 |
+
minvalue=2,
|
| 353 |
+
maxvalue=16)
|
| 354 |
+
|
| 355 |
+
|
| 356 |
+
class Indents:
|
| 357 |
+
"Change future indents."
|
| 358 |
+
|
| 359 |
+
def __init__(self, editwin):
|
| 360 |
+
self.editwin = editwin
|
| 361 |
+
|
| 362 |
+
def toggle_tabs_event(self, event):
|
| 363 |
+
editwin = self.editwin
|
| 364 |
+
usetabs = editwin.usetabs
|
| 365 |
+
if askyesno(
|
| 366 |
+
"Toggle tabs",
|
| 367 |
+
"Turn tabs " + ("on", "off")[usetabs] +
|
| 368 |
+
"?\nIndent width " +
|
| 369 |
+
("will be", "remains at")[usetabs] + " 8." +
|
| 370 |
+
"\n Note: a tab is always 8 columns",
|
| 371 |
+
parent=editwin.text):
|
| 372 |
+
editwin.usetabs = not usetabs
|
| 373 |
+
# Try to prevent inconsistent indentation.
|
| 374 |
+
# User must change indent width manually after using tabs.
|
| 375 |
+
editwin.indentwidth = 8
|
| 376 |
+
return "break"
|
| 377 |
+
|
| 378 |
+
def change_indentwidth_event(self, event):
|
| 379 |
+
editwin = self.editwin
|
| 380 |
+
new = askinteger(
|
| 381 |
+
"Indent width",
|
| 382 |
+
"New indent width (2-16)\n(Always use 8 when using tabs)",
|
| 383 |
+
parent=editwin.text,
|
| 384 |
+
initialvalue=editwin.indentwidth,
|
| 385 |
+
minvalue=2,
|
| 386 |
+
maxvalue=16)
|
| 387 |
+
if new and new != editwin.indentwidth and not editwin.usetabs:
|
| 388 |
+
editwin.indentwidth = new
|
| 389 |
+
return "break"
|
| 390 |
+
|
| 391 |
+
|
| 392 |
+
class Rstrip: # 'Strip Trailing Whitespace" on "Format" menu.
|
| 393 |
+
def __init__(self, editwin):
|
| 394 |
+
self.editwin = editwin
|
| 395 |
+
|
| 396 |
+
def do_rstrip(self, event=None):
|
| 397 |
+
text = self.editwin.text
|
| 398 |
+
undo = self.editwin.undo
|
| 399 |
+
undo.undo_block_start()
|
| 400 |
+
|
| 401 |
+
end_line = int(float(text.index('end')))
|
| 402 |
+
for cur in range(1, end_line):
|
| 403 |
+
txt = text.get('%i.0' % cur, '%i.end' % cur)
|
| 404 |
+
raw = len(txt)
|
| 405 |
+
cut = len(txt.rstrip())
|
| 406 |
+
# Since text.delete() marks file as changed, even if not,
|
| 407 |
+
# only call it when needed to actually delete something.
|
| 408 |
+
if cut < raw:
|
| 409 |
+
text.delete('%i.%i' % (cur, cut), '%i.end' % cur)
|
| 410 |
+
|
| 411 |
+
if (text.get('end-2c') == '\n' # File ends with at least 1 newline;
|
| 412 |
+
and not hasattr(self.editwin, 'interp')): # & is not Shell.
|
| 413 |
+
# Delete extra user endlines.
|
| 414 |
+
while (text.index('end-1c') > '1.0' # Stop if file empty.
|
| 415 |
+
and text.get('end-3c') == '\n'):
|
| 416 |
+
text.delete('end-3c')
|
| 417 |
+
# Because tk indexes are slice indexes and never raise,
|
| 418 |
+
# a file with only newlines will be emptied.
|
| 419 |
+
# patchcheck.py does the same.
|
| 420 |
+
|
| 421 |
+
undo.undo_block_stop()
|
| 422 |
+
|
| 423 |
+
|
| 424 |
+
if __name__ == "__main__":
|
| 425 |
+
from unittest import main
|
| 426 |
+
main('idlelib.idle_test.test_format', verbosity=2, exit=False)
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/grep.py
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Grep dialog for Find in Files functionality.
|
| 2 |
+
|
| 3 |
+
Inherits from SearchDialogBase for GUI and uses searchengine
|
| 4 |
+
to prepare search pattern.
|
| 5 |
+
"""
|
| 6 |
+
import fnmatch
|
| 7 |
+
import os
|
| 8 |
+
import sys
|
| 9 |
+
|
| 10 |
+
from tkinter import StringVar, BooleanVar
|
| 11 |
+
from tkinter.ttk import Checkbutton # Frame imported in ...Base
|
| 12 |
+
|
| 13 |
+
from idlelib.searchbase import SearchDialogBase
|
| 14 |
+
from idlelib import searchengine
|
| 15 |
+
|
| 16 |
+
# Importing OutputWindow here fails due to import loop
|
| 17 |
+
# EditorWindow -> GrepDialog -> OutputWindow -> EditorWindow
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
def grep(text, io=None, flist=None):
|
| 21 |
+
"""Open the Find in Files dialog.
|
| 22 |
+
|
| 23 |
+
Module-level function to access the singleton GrepDialog
|
| 24 |
+
instance and open the dialog. If text is selected, it is
|
| 25 |
+
used as the search phrase; otherwise, the previous entry
|
| 26 |
+
is used.
|
| 27 |
+
|
| 28 |
+
Args:
|
| 29 |
+
text: Text widget that contains the selected text for
|
| 30 |
+
default search phrase.
|
| 31 |
+
io: iomenu.IOBinding instance with default path to search.
|
| 32 |
+
flist: filelist.FileList instance for OutputWindow parent.
|
| 33 |
+
"""
|
| 34 |
+
root = text._root()
|
| 35 |
+
engine = searchengine.get(root)
|
| 36 |
+
if not hasattr(engine, "_grepdialog"):
|
| 37 |
+
engine._grepdialog = GrepDialog(root, engine, flist)
|
| 38 |
+
dialog = engine._grepdialog
|
| 39 |
+
searchphrase = text.get("sel.first", "sel.last")
|
| 40 |
+
dialog.open(text, searchphrase, io)
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def walk_error(msg):
|
| 44 |
+
"Handle os.walk error."
|
| 45 |
+
print(msg)
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
def findfiles(folder, pattern, recursive):
|
| 49 |
+
"""Generate file names in dir that match pattern.
|
| 50 |
+
|
| 51 |
+
Args:
|
| 52 |
+
folder: Root directory to search.
|
| 53 |
+
pattern: File pattern to match.
|
| 54 |
+
recursive: True to include subdirectories.
|
| 55 |
+
"""
|
| 56 |
+
for dirpath, _, filenames in os.walk(folder, onerror=walk_error):
|
| 57 |
+
yield from (os.path.join(dirpath, name)
|
| 58 |
+
for name in filenames
|
| 59 |
+
if fnmatch.fnmatch(name, pattern))
|
| 60 |
+
if not recursive:
|
| 61 |
+
break
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
class GrepDialog(SearchDialogBase):
|
| 65 |
+
"Dialog for searching multiple files."
|
| 66 |
+
|
| 67 |
+
title = "Find in Files Dialog"
|
| 68 |
+
icon = "Grep"
|
| 69 |
+
needwrapbutton = 0
|
| 70 |
+
|
| 71 |
+
def __init__(self, root, engine, flist):
|
| 72 |
+
"""Create search dialog for searching for a phrase in the file system.
|
| 73 |
+
|
| 74 |
+
Uses SearchDialogBase as the basis for the GUI and a
|
| 75 |
+
searchengine instance to prepare the search.
|
| 76 |
+
|
| 77 |
+
Attributes:
|
| 78 |
+
flist: filelist.Filelist instance for OutputWindow parent.
|
| 79 |
+
globvar: String value of Entry widget for path to search.
|
| 80 |
+
globent: Entry widget for globvar. Created in
|
| 81 |
+
create_entries().
|
| 82 |
+
recvar: Boolean value of Checkbutton widget for
|
| 83 |
+
traversing through subdirectories.
|
| 84 |
+
"""
|
| 85 |
+
super().__init__(root, engine)
|
| 86 |
+
self.flist = flist
|
| 87 |
+
self.globvar = StringVar(root)
|
| 88 |
+
self.recvar = BooleanVar(root)
|
| 89 |
+
|
| 90 |
+
def open(self, text, searchphrase, io=None):
|
| 91 |
+
"""Make dialog visible on top of others and ready to use.
|
| 92 |
+
|
| 93 |
+
Extend the SearchDialogBase open() to set the initial value
|
| 94 |
+
for globvar.
|
| 95 |
+
|
| 96 |
+
Args:
|
| 97 |
+
text: Multicall object containing the text information.
|
| 98 |
+
searchphrase: String phrase to search.
|
| 99 |
+
io: iomenu.IOBinding instance containing file path.
|
| 100 |
+
"""
|
| 101 |
+
SearchDialogBase.open(self, text, searchphrase)
|
| 102 |
+
if io:
|
| 103 |
+
path = io.filename or ""
|
| 104 |
+
else:
|
| 105 |
+
path = ""
|
| 106 |
+
dir, base = os.path.split(path)
|
| 107 |
+
head, tail = os.path.splitext(base)
|
| 108 |
+
if not tail:
|
| 109 |
+
tail = ".py"
|
| 110 |
+
self.globvar.set(os.path.join(dir, "*" + tail))
|
| 111 |
+
|
| 112 |
+
def create_entries(self):
|
| 113 |
+
"Create base entry widgets and add widget for search path."
|
| 114 |
+
SearchDialogBase.create_entries(self)
|
| 115 |
+
self.globent = self.make_entry("In files:", self.globvar)[0]
|
| 116 |
+
|
| 117 |
+
def create_other_buttons(self):
|
| 118 |
+
"Add check button to recurse down subdirectories."
|
| 119 |
+
btn = Checkbutton(
|
| 120 |
+
self.make_frame()[0], variable=self.recvar,
|
| 121 |
+
text="Recurse down subdirectories")
|
| 122 |
+
btn.pack(side="top", fill="both")
|
| 123 |
+
|
| 124 |
+
def create_command_buttons(self):
|
| 125 |
+
"Create base command buttons and add button for Search Files."
|
| 126 |
+
SearchDialogBase.create_command_buttons(self)
|
| 127 |
+
self.make_button("Search Files", self.default_command, isdef=True)
|
| 128 |
+
|
| 129 |
+
def default_command(self, event=None):
|
| 130 |
+
"""Grep for search pattern in file path. The default command is bound
|
| 131 |
+
to <Return>.
|
| 132 |
+
|
| 133 |
+
If entry values are populated, set OutputWindow as stdout
|
| 134 |
+
and perform search. The search dialog is closed automatically
|
| 135 |
+
when the search begins.
|
| 136 |
+
"""
|
| 137 |
+
prog = self.engine.getprog()
|
| 138 |
+
if not prog:
|
| 139 |
+
return
|
| 140 |
+
path = self.globvar.get()
|
| 141 |
+
if not path:
|
| 142 |
+
self.top.bell()
|
| 143 |
+
return
|
| 144 |
+
from idlelib.outwin import OutputWindow # leave here!
|
| 145 |
+
save = sys.stdout
|
| 146 |
+
try:
|
| 147 |
+
sys.stdout = OutputWindow(self.flist)
|
| 148 |
+
self.grep_it(prog, path)
|
| 149 |
+
finally:
|
| 150 |
+
sys.stdout = save
|
| 151 |
+
|
| 152 |
+
def grep_it(self, prog, path):
|
| 153 |
+
"""Search for prog within the lines of the files in path.
|
| 154 |
+
|
| 155 |
+
For the each file in the path directory, open the file and
|
| 156 |
+
search each line for the matching pattern. If the pattern is
|
| 157 |
+
found, write the file and line information to stdout (which
|
| 158 |
+
is an OutputWindow).
|
| 159 |
+
|
| 160 |
+
Args:
|
| 161 |
+
prog: The compiled, cooked search pattern.
|
| 162 |
+
path: String containing the search path.
|
| 163 |
+
"""
|
| 164 |
+
folder, filepat = os.path.split(path)
|
| 165 |
+
if not folder:
|
| 166 |
+
folder = os.curdir
|
| 167 |
+
filelist = sorted(findfiles(folder, filepat, self.recvar.get()))
|
| 168 |
+
self.close()
|
| 169 |
+
pat = self.engine.getpat()
|
| 170 |
+
print(f"Searching {pat!r} in {path} ...")
|
| 171 |
+
hits = 0
|
| 172 |
+
try:
|
| 173 |
+
for fn in filelist:
|
| 174 |
+
try:
|
| 175 |
+
with open(fn, errors='replace') as f:
|
| 176 |
+
for lineno, line in enumerate(f, 1):
|
| 177 |
+
if line[-1:] == '\n':
|
| 178 |
+
line = line[:-1]
|
| 179 |
+
if prog.search(line):
|
| 180 |
+
sys.stdout.write(f"{fn}: {lineno}: {line}\n")
|
| 181 |
+
hits += 1
|
| 182 |
+
except OSError as msg:
|
| 183 |
+
print(msg)
|
| 184 |
+
print(f"Hits found: {hits}\n(Hint: right-click to open locations.)"
|
| 185 |
+
if hits else "No hits.")
|
| 186 |
+
except AttributeError:
|
| 187 |
+
# Tk window has been closed, OutputWindow.text = None,
|
| 188 |
+
# so in OW.write, OW.text.insert fails.
|
| 189 |
+
pass
|
| 190 |
+
|
| 191 |
+
|
| 192 |
+
def _grep_dialog(parent): # htest #
|
| 193 |
+
from tkinter import Toplevel, Text, SEL, END
|
| 194 |
+
from tkinter.ttk import Frame, Button
|
| 195 |
+
from idlelib.pyshell import PyShellFileList
|
| 196 |
+
|
| 197 |
+
top = Toplevel(parent)
|
| 198 |
+
top.title("Test GrepDialog")
|
| 199 |
+
x, y = map(int, parent.geometry().split('+')[1:])
|
| 200 |
+
top.geometry(f"+{x}+{y + 175}")
|
| 201 |
+
|
| 202 |
+
flist = PyShellFileList(top)
|
| 203 |
+
frame = Frame(top)
|
| 204 |
+
frame.pack()
|
| 205 |
+
text = Text(frame, height=5)
|
| 206 |
+
text.pack()
|
| 207 |
+
|
| 208 |
+
def show_grep_dialog():
|
| 209 |
+
text.tag_add(SEL, "1.0", END)
|
| 210 |
+
grep(text, flist=flist)
|
| 211 |
+
text.tag_remove(SEL, "1.0", END)
|
| 212 |
+
|
| 213 |
+
button = Button(frame, text="Show GrepDialog", command=show_grep_dialog)
|
| 214 |
+
button.pack()
|
| 215 |
+
|
| 216 |
+
if __name__ == "__main__":
|
| 217 |
+
from unittest import main
|
| 218 |
+
main('idlelib.idle_test.test_grep', verbosity=2, exit=False)
|
| 219 |
+
|
| 220 |
+
from idlelib.idle_test.htest import run
|
| 221 |
+
run(_grep_dialog)
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/help.html
ADDED
|
@@ -0,0 +1,1014 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
<!DOCTYPE html>
|
| 3 |
+
|
| 4 |
+
<html>
|
| 5 |
+
<head>
|
| 6 |
+
<meta charset="utf-8" />
|
| 7 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 8 |
+
<title>IDLE — Python 3.10.0a6 documentation</title>
|
| 9 |
+
<link rel="stylesheet" href="../_static/pydoctheme.css" type="text/css" />
|
| 10 |
+
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
|
| 11 |
+
|
| 12 |
+
<script id="documentation_options" data-url_root="../" src="../_static/documentation_options.js"></script>
|
| 13 |
+
<script src="../_static/jquery.js"></script>
|
| 14 |
+
<script src="../_static/underscore.js"></script>
|
| 15 |
+
<script src="../_static/doctools.js"></script>
|
| 16 |
+
<script src="../_static/language_data.js"></script>
|
| 17 |
+
|
| 18 |
+
<script src="../_static/sidebar.js"></script>
|
| 19 |
+
|
| 20 |
+
<link rel="search" type="application/opensearchdescription+xml"
|
| 21 |
+
title="Search within Python 3.10.0a6 documentation"
|
| 22 |
+
href="../_static/opensearch.xml"/>
|
| 23 |
+
<link rel="author" title="About these documents" href="../about.html" />
|
| 24 |
+
<link rel="index" title="Index" href="../genindex.html" />
|
| 25 |
+
<link rel="search" title="Search" href="../search.html" />
|
| 26 |
+
<link rel="copyright" title="Copyright" href="../copyright.html" />
|
| 27 |
+
<link rel="next" title="Other Graphical User Interface Packages" href="othergui.html" />
|
| 28 |
+
<link rel="prev" title="tkinter.tix — Extension widgets for Tk" href="tkinter.tix.html" />
|
| 29 |
+
<link rel="canonical" href="https://docs.python.org/3/library/idle.html" />
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
<style>
|
| 36 |
+
@media only screen {
|
| 37 |
+
table.full-width-table {
|
| 38 |
+
width: 100%;
|
| 39 |
+
}
|
| 40 |
+
}
|
| 41 |
+
</style>
|
| 42 |
+
|
| 43 |
+
<link rel="shortcut icon" type="image/png" href="../_static/py.png" />
|
| 44 |
+
|
| 45 |
+
<script type="text/javascript" src="../_static/copybutton.js"></script>
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
</head><body>
|
| 51 |
+
|
| 52 |
+
<div class="related" role="navigation" aria-label="related navigation">
|
| 53 |
+
<h3>Navigation</h3>
|
| 54 |
+
<ul>
|
| 55 |
+
<li class="right" style="margin-right: 10px">
|
| 56 |
+
<a href="../genindex.html" title="General Index"
|
| 57 |
+
accesskey="I">index</a></li>
|
| 58 |
+
<li class="right" >
|
| 59 |
+
<a href="../py-modindex.html" title="Python Module Index"
|
| 60 |
+
>modules</a> |</li>
|
| 61 |
+
<li class="right" >
|
| 62 |
+
<a href="othergui.html" title="Other Graphical User Interface Packages"
|
| 63 |
+
accesskey="N">next</a> |</li>
|
| 64 |
+
<li class="right" >
|
| 65 |
+
<a href="tkinter.tix.html" title="tkinter.tix — Extension widgets for Tk"
|
| 66 |
+
accesskey="P">previous</a> |</li>
|
| 67 |
+
|
| 68 |
+
<li><img src="../_static/py.png" alt=""
|
| 69 |
+
style="vertical-align: middle; margin-top: -1px"/></li>
|
| 70 |
+
<li><a href="https://www.python.org/">Python</a> »</li>
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
<li id="cpython-language-and-version">
|
| 74 |
+
<a href="../index.html">3.10.0a6 Documentation</a> »
|
| 75 |
+
</li>
|
| 76 |
+
|
| 77 |
+
<li class="nav-item nav-item-1"><a href="index.html" >The Python Standard Library</a> »</li>
|
| 78 |
+
<li class="nav-item nav-item-2"><a href="tk.html" accesskey="U">Graphical User Interfaces with Tk</a> »</li>
|
| 79 |
+
<li class="nav-item nav-item-this"><a href="">IDLE</a></li>
|
| 80 |
+
<li class="right">
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
<div class="inline-search" style="display: none" role="search">
|
| 84 |
+
<form class="inline-search" action="../search.html" method="get">
|
| 85 |
+
<input placeholder="Quick search" type="text" name="q" />
|
| 86 |
+
<input type="submit" value="Go" />
|
| 87 |
+
<input type="hidden" name="check_keywords" value="yes" />
|
| 88 |
+
<input type="hidden" name="area" value="default" />
|
| 89 |
+
</form>
|
| 90 |
+
</div>
|
| 91 |
+
<script type="text/javascript">$('.inline-search').show(0);</script>
|
| 92 |
+
|
|
| 93 |
+
</li>
|
| 94 |
+
|
| 95 |
+
</ul>
|
| 96 |
+
</div>
|
| 97 |
+
|
| 98 |
+
<div class="document">
|
| 99 |
+
<div class="documentwrapper">
|
| 100 |
+
<div class="bodywrapper">
|
| 101 |
+
<div class="body" role="main">
|
| 102 |
+
|
| 103 |
+
<div class="section" id="idle">
|
| 104 |
+
<span id="id1"></span><h1>IDLE<a class="headerlink" href="#idle" title="Permalink to this headline">¶</a></h1>
|
| 105 |
+
<p><strong>Source code:</strong> <a class="reference external" href="https://github.com/python/cpython/tree/master/Lib/idlelib/">Lib/idlelib/</a></p>
|
| 106 |
+
<hr class="docutils" id="index-0" />
|
| 107 |
+
<p>IDLE is Python’s Integrated Development and Learning Environment.</p>
|
| 108 |
+
<p>IDLE has the following features:</p>
|
| 109 |
+
<ul class="simple">
|
| 110 |
+
<li><p>coded in 100% pure Python, using the <a class="reference internal" href="tkinter.html#module-tkinter" title="tkinter: Interface to Tcl/Tk for graphical user interfaces"><code class="xref py py-mod docutils literal notranslate"><span class="pre">tkinter</span></code></a> GUI toolkit</p></li>
|
| 111 |
+
<li><p>cross-platform: works mostly the same on Windows, Unix, and macOS</p></li>
|
| 112 |
+
<li><p>Python shell window (interactive interpreter) with colorizing
|
| 113 |
+
of code input, output, and error messages</p></li>
|
| 114 |
+
<li><p>multi-window text editor with multiple undo, Python colorizing,
|
| 115 |
+
smart indent, call tips, auto completion, and other features</p></li>
|
| 116 |
+
<li><p>search within any window, replace within editor windows, and search
|
| 117 |
+
through multiple files (grep)</p></li>
|
| 118 |
+
<li><p>debugger with persistent breakpoints, stepping, and viewing
|
| 119 |
+
of global and local namespaces</p></li>
|
| 120 |
+
<li><p>configuration, browsers, and other dialogs</p></li>
|
| 121 |
+
</ul>
|
| 122 |
+
<div class="section" id="menus">
|
| 123 |
+
<h2>Menus<a class="headerlink" href="#menus" title="Permalink to this headline">¶</a></h2>
|
| 124 |
+
<p>IDLE has two main window types, the Shell window and the Editor window. It is
|
| 125 |
+
possible to have multiple editor windows simultaneously. On Windows and
|
| 126 |
+
Linux, each has its own top menu. Each menu documented below indicates
|
| 127 |
+
which window type it is associated with.</p>
|
| 128 |
+
<p>Output windows, such as used for Edit => Find in Files, are a subtype of editor
|
| 129 |
+
window. They currently have the same top menu but a different
|
| 130 |
+
default title and context menu.</p>
|
| 131 |
+
<p>On macOS, there is one application menu. It dynamically changes according
|
| 132 |
+
to the window currently selected. It has an IDLE menu, and some entries
|
| 133 |
+
described below are moved around to conform to Apple guidelines.</p>
|
| 134 |
+
<div class="section" id="file-menu-shell-and-editor">
|
| 135 |
+
<h3>File menu (Shell and Editor)<a class="headerlink" href="#file-menu-shell-and-editor" title="Permalink to this headline">¶</a></h3>
|
| 136 |
+
<dl class="simple">
|
| 137 |
+
<dt>New File</dt><dd><p>Create a new file editing window.</p>
|
| 138 |
+
</dd>
|
| 139 |
+
<dt>Open…</dt><dd><p>Open an existing file with an Open dialog.</p>
|
| 140 |
+
</dd>
|
| 141 |
+
<dt>Recent Files</dt><dd><p>Open a list of recent files. Click one to open it.</p>
|
| 142 |
+
</dd>
|
| 143 |
+
<dt>Open Module…</dt><dd><p>Open an existing module (searches sys.path).</p>
|
| 144 |
+
</dd>
|
| 145 |
+
</dl>
|
| 146 |
+
<dl class="simple" id="index-1">
|
| 147 |
+
<dt>Class Browser</dt><dd><p>Show functions, classes, and methods in the current Editor file in a
|
| 148 |
+
tree structure. In the shell, open a module first.</p>
|
| 149 |
+
</dd>
|
| 150 |
+
<dt>Path Browser</dt><dd><p>Show sys.path directories, modules, functions, classes and methods in a
|
| 151 |
+
tree structure.</p>
|
| 152 |
+
</dd>
|
| 153 |
+
<dt>Save</dt><dd><p>Save the current window to the associated file, if there is one. Windows
|
| 154 |
+
that have been changed since being opened or last saved have a * before
|
| 155 |
+
and after the window title. If there is no associated file,
|
| 156 |
+
do Save As instead.</p>
|
| 157 |
+
</dd>
|
| 158 |
+
<dt>Save As…</dt><dd><p>Save the current window with a Save As dialog. The file saved becomes the
|
| 159 |
+
new associated file for the window.</p>
|
| 160 |
+
</dd>
|
| 161 |
+
<dt>Save Copy As…</dt><dd><p>Save the current window to different file without changing the associated
|
| 162 |
+
file.</p>
|
| 163 |
+
</dd>
|
| 164 |
+
<dt>Print Window</dt><dd><p>Print the current window to the default printer.</p>
|
| 165 |
+
</dd>
|
| 166 |
+
<dt>Close</dt><dd><p>Close the current window (ask to save if unsaved).</p>
|
| 167 |
+
</dd>
|
| 168 |
+
<dt>Exit</dt><dd><p>Close all windows and quit IDLE (ask to save unsaved windows).</p>
|
| 169 |
+
</dd>
|
| 170 |
+
</dl>
|
| 171 |
+
</div>
|
| 172 |
+
<div class="section" id="edit-menu-shell-and-editor">
|
| 173 |
+
<h3>Edit menu (Shell and Editor)<a class="headerlink" href="#edit-menu-shell-and-editor" title="Permalink to this headline">¶</a></h3>
|
| 174 |
+
<dl class="simple">
|
| 175 |
+
<dt>Undo</dt><dd><p>Undo the last change to the current window. A maximum of 1000 changes may
|
| 176 |
+
be undone.</p>
|
| 177 |
+
</dd>
|
| 178 |
+
<dt>Redo</dt><dd><p>Redo the last undone change to the current window.</p>
|
| 179 |
+
</dd>
|
| 180 |
+
<dt>Cut</dt><dd><p>Copy selection into the system-wide clipboard; then delete the selection.</p>
|
| 181 |
+
</dd>
|
| 182 |
+
<dt>Copy</dt><dd><p>Copy selection into the system-wide clipboard.</p>
|
| 183 |
+
</dd>
|
| 184 |
+
<dt>Paste</dt><dd><p>Insert contents of the system-wide clipboard into the current window.</p>
|
| 185 |
+
</dd>
|
| 186 |
+
</dl>
|
| 187 |
+
<p>The clipboard functions are also available in context menus.</p>
|
| 188 |
+
<dl class="simple">
|
| 189 |
+
<dt>Select All</dt><dd><p>Select the entire contents of the current window.</p>
|
| 190 |
+
</dd>
|
| 191 |
+
<dt>Find…</dt><dd><p>Open a search dialog with many options</p>
|
| 192 |
+
</dd>
|
| 193 |
+
<dt>Find Again</dt><dd><p>Repeat the last search, if there is one.</p>
|
| 194 |
+
</dd>
|
| 195 |
+
<dt>Find Selection</dt><dd><p>Search for the currently selected string, if there is one.</p>
|
| 196 |
+
</dd>
|
| 197 |
+
<dt>Find in Files…</dt><dd><p>Open a file search dialog. Put results in a new output window.</p>
|
| 198 |
+
</dd>
|
| 199 |
+
<dt>Replace…</dt><dd><p>Open a search-and-replace dialog.</p>
|
| 200 |
+
</dd>
|
| 201 |
+
<dt>Go to Line</dt><dd><p>Move the cursor to the beginning of the line requested and make that
|
| 202 |
+
line visible. A request past the end of the file goes to the end.
|
| 203 |
+
Clear any selection and update the line and column status.</p>
|
| 204 |
+
</dd>
|
| 205 |
+
<dt>Show Completions</dt><dd><p>Open a scrollable list allowing selection of existing names. See
|
| 206 |
+
<a class="reference internal" href="#completions"><span class="std std-ref">Completions</span></a> in the Editing and navigation section below.</p>
|
| 207 |
+
</dd>
|
| 208 |
+
<dt>Expand Word</dt><dd><p>Expand a prefix you have typed to match a full word in the same window;
|
| 209 |
+
repeat to get a different expansion.</p>
|
| 210 |
+
</dd>
|
| 211 |
+
<dt>Show call tip</dt><dd><p>After an unclosed parenthesis for a function, open a small window with
|
| 212 |
+
function parameter hints. See <a class="reference internal" href="#calltips"><span class="std std-ref">Calltips</span></a> in the
|
| 213 |
+
Editing and navigation section below.</p>
|
| 214 |
+
</dd>
|
| 215 |
+
<dt>Show surrounding parens</dt><dd><p>Highlight the surrounding parenthesis.</p>
|
| 216 |
+
</dd>
|
| 217 |
+
</dl>
|
| 218 |
+
</div>
|
| 219 |
+
<div class="section" id="format-menu-editor-window-only">
|
| 220 |
+
<span id="format-menu"></span><h3>Format menu (Editor window only)<a class="headerlink" href="#format-menu-editor-window-only" title="Permalink to this headline">¶</a></h3>
|
| 221 |
+
<dl class="simple">
|
| 222 |
+
<dt>Indent Region</dt><dd><p>Shift selected lines right by the indent width (default 4 spaces).</p>
|
| 223 |
+
</dd>
|
| 224 |
+
<dt>Dedent Region</dt><dd><p>Shift selected lines left by the indent width (default 4 spaces).</p>
|
| 225 |
+
</dd>
|
| 226 |
+
<dt>Comment Out Region</dt><dd><p>Insert ## in front of selected lines.</p>
|
| 227 |
+
</dd>
|
| 228 |
+
<dt>Uncomment Region</dt><dd><p>Remove leading # or ## from selected lines.</p>
|
| 229 |
+
</dd>
|
| 230 |
+
<dt>Tabify Region</dt><dd><p>Turn <em>leading</em> stretches of spaces into tabs. (Note: We recommend using
|
| 231 |
+
4 space blocks to indent Python code.)</p>
|
| 232 |
+
</dd>
|
| 233 |
+
<dt>Untabify Region</dt><dd><p>Turn <em>all</em> tabs into the correct number of spaces.</p>
|
| 234 |
+
</dd>
|
| 235 |
+
<dt>Toggle Tabs</dt><dd><p>Open a dialog to switch between indenting with spaces and tabs.</p>
|
| 236 |
+
</dd>
|
| 237 |
+
<dt>New Indent Width</dt><dd><p>Open a dialog to change indent width. The accepted default by the Python
|
| 238 |
+
community is 4 spaces.</p>
|
| 239 |
+
</dd>
|
| 240 |
+
<dt>Format Paragraph</dt><dd><p>Reformat the current blank-line-delimited paragraph in comment block or
|
| 241 |
+
multiline string or selected line in a string. All lines in the
|
| 242 |
+
paragraph will be formatted to less than N columns, where N defaults to 72.</p>
|
| 243 |
+
</dd>
|
| 244 |
+
<dt>Strip trailing whitespace</dt><dd><p>Remove trailing space and other whitespace characters after the last
|
| 245 |
+
non-whitespace character of a line by applying str.rstrip to each line,
|
| 246 |
+
including lines within multiline strings. Except for Shell windows,
|
| 247 |
+
remove extra newlines at the end of the file.</p>
|
| 248 |
+
</dd>
|
| 249 |
+
</dl>
|
| 250 |
+
</div>
|
| 251 |
+
<div class="section" id="run-menu-editor-window-only">
|
| 252 |
+
<span id="index-2"></span><h3>Run menu (Editor window only)<a class="headerlink" href="#run-menu-editor-window-only" title="Permalink to this headline">¶</a></h3>
|
| 253 |
+
<dl class="simple" id="run-module">
|
| 254 |
+
<dt>Run Module</dt><dd><p>Do <a class="reference internal" href="#check-module"><span class="std std-ref">Check Module</span></a>. If no error, restart the shell to clean the
|
| 255 |
+
environment, then execute the module. Output is displayed in the Shell
|
| 256 |
+
window. Note that output requires use of <code class="docutils literal notranslate"><span class="pre">print</span></code> or <code class="docutils literal notranslate"><span class="pre">write</span></code>.
|
| 257 |
+
When execution is complete, the Shell retains focus and displays a prompt.
|
| 258 |
+
At this point, one may interactively explore the result of execution.
|
| 259 |
+
This is similar to executing a file with <code class="docutils literal notranslate"><span class="pre">python</span> <span class="pre">-i</span> <span class="pre">file</span></code> at a command
|
| 260 |
+
line.</p>
|
| 261 |
+
</dd>
|
| 262 |
+
</dl>
|
| 263 |
+
<dl class="simple" id="run-custom">
|
| 264 |
+
<dt>Run… Customized</dt><dd><p>Same as <a class="reference internal" href="#run-module"><span class="std std-ref">Run Module</span></a>, but run the module with customized
|
| 265 |
+
settings. <em>Command Line Arguments</em> extend <a class="reference internal" href="sys.html#sys.argv" title="sys.argv"><code class="xref py py-data docutils literal notranslate"><span class="pre">sys.argv</span></code></a> as if passed
|
| 266 |
+
on a command line. The module can be run in the Shell without restarting.</p>
|
| 267 |
+
</dd>
|
| 268 |
+
</dl>
|
| 269 |
+
<dl class="simple" id="check-module">
|
| 270 |
+
<dt>Check Module</dt><dd><p>Check the syntax of the module currently open in the Editor window. If the
|
| 271 |
+
module has not been saved IDLE will either prompt the user to save or
|
| 272 |
+
autosave, as selected in the General tab of the Idle Settings dialog. If
|
| 273 |
+
there is a syntax error, the approximate location is indicated in the
|
| 274 |
+
Editor window.</p>
|
| 275 |
+
</dd>
|
| 276 |
+
</dl>
|
| 277 |
+
<dl class="simple" id="python-shell">
|
| 278 |
+
<dt>Python Shell</dt><dd><p>Open or wake up the Python Shell window.</p>
|
| 279 |
+
</dd>
|
| 280 |
+
</dl>
|
| 281 |
+
</div>
|
| 282 |
+
<div class="section" id="shell-menu-shell-window-only">
|
| 283 |
+
<h3>Shell menu (Shell window only)<a class="headerlink" href="#shell-menu-shell-window-only" title="Permalink to this headline">¶</a></h3>
|
| 284 |
+
<dl class="simple">
|
| 285 |
+
<dt>View Last Restart</dt><dd><p>Scroll the shell window to the last Shell restart.</p>
|
| 286 |
+
</dd>
|
| 287 |
+
<dt>Restart Shell</dt><dd><p>Restart the shell to clean the environment and reset display and exception handling.</p>
|
| 288 |
+
</dd>
|
| 289 |
+
<dt>Previous History</dt><dd><p>Cycle through earlier commands in history which match the current entry.</p>
|
| 290 |
+
</dd>
|
| 291 |
+
<dt>Next History</dt><dd><p>Cycle through later commands in history which match the current entry.</p>
|
| 292 |
+
</dd>
|
| 293 |
+
<dt>Interrupt Execution</dt><dd><p>Stop a running program.</p>
|
| 294 |
+
</dd>
|
| 295 |
+
</dl>
|
| 296 |
+
</div>
|
| 297 |
+
<div class="section" id="debug-menu-shell-window-only">
|
| 298 |
+
<h3>Debug menu (Shell window only)<a class="headerlink" href="#debug-menu-shell-window-only" title="Permalink to this headline">¶</a></h3>
|
| 299 |
+
<dl class="simple">
|
| 300 |
+
<dt>Go to File/Line</dt><dd><p>Look on the current line. with the cursor, and the line above for a filename
|
| 301 |
+
and line number. If found, open the file if not already open, and show the
|
| 302 |
+
line. Use this to view source lines referenced in an exception traceback
|
| 303 |
+
and lines found by Find in Files. Also available in the context menu of
|
| 304 |
+
the Shell window and Output windows.</p>
|
| 305 |
+
</dd>
|
| 306 |
+
</dl>
|
| 307 |
+
<dl class="simple" id="index-3">
|
| 308 |
+
<dt>Debugger (toggle)</dt><dd><p>When activated, code entered in the Shell or run from an Editor will run
|
| 309 |
+
under the debugger. In the Editor, breakpoints can be set with the context
|
| 310 |
+
menu. This feature is still incomplete and somewhat experimental.</p>
|
| 311 |
+
</dd>
|
| 312 |
+
<dt>Stack Viewer</dt><dd><p>Show the stack traceback of the last exception in a tree widget, with
|
| 313 |
+
access to locals and globals.</p>
|
| 314 |
+
</dd>
|
| 315 |
+
<dt>Auto-open Stack Viewer</dt><dd><p>Toggle automatically opening the stack viewer on an unhandled exception.</p>
|
| 316 |
+
</dd>
|
| 317 |
+
</dl>
|
| 318 |
+
</div>
|
| 319 |
+
<div class="section" id="options-menu-shell-and-editor">
|
| 320 |
+
<h3>Options menu (Shell and Editor)<a class="headerlink" href="#options-menu-shell-and-editor" title="Permalink to this headline">¶</a></h3>
|
| 321 |
+
<dl class="simple">
|
| 322 |
+
<dt>Configure IDLE</dt><dd><p>Open a configuration dialog and change preferences for the following:
|
| 323 |
+
fonts, indentation, keybindings, text color themes, startup windows and
|
| 324 |
+
size, additional help sources, and extensions. On macOS, open the
|
| 325 |
+
configuration dialog by selecting Preferences in the application
|
| 326 |
+
menu. For more details, see
|
| 327 |
+
<a class="reference internal" href="#preferences"><span class="std std-ref">Setting preferences</span></a> under Help and preferences.</p>
|
| 328 |
+
</dd>
|
| 329 |
+
</dl>
|
| 330 |
+
<p>Most configuration options apply to all windows or all future windows.
|
| 331 |
+
The option items below only apply to the active window.</p>
|
| 332 |
+
<dl class="simple">
|
| 333 |
+
<dt>Show/Hide Code Context (Editor Window only)</dt><dd><p>Open a pane at the top of the edit window which shows the block context
|
| 334 |
+
of the code which has scrolled above the top of the window. See
|
| 335 |
+
<a class="reference internal" href="#code-context"><span class="std std-ref">Code Context</span></a> in the Editing and Navigation section
|
| 336 |
+
below.</p>
|
| 337 |
+
</dd>
|
| 338 |
+
<dt>Show/Hide Line Numbers (Editor Window only)</dt><dd><p>Open a column to the left of the edit window which shows the number
|
| 339 |
+
of each line of text. The default is off, which may be changed in the
|
| 340 |
+
preferences (see <a class="reference internal" href="#preferences"><span class="std std-ref">Setting preferences</span></a>).</p>
|
| 341 |
+
</dd>
|
| 342 |
+
<dt>Zoom/Restore Height</dt><dd><p>Toggles the window between normal size and maximum height. The initial size
|
| 343 |
+
defaults to 40 lines by 80 chars unless changed on the General tab of the
|
| 344 |
+
Configure IDLE dialog. The maximum height for a screen is determined by
|
| 345 |
+
momentarily maximizing a window the first time one is zoomed on the screen.
|
| 346 |
+
Changing screen settings may invalidate the saved height. This toggle has
|
| 347 |
+
no effect when a window is maximized.</p>
|
| 348 |
+
</dd>
|
| 349 |
+
</dl>
|
| 350 |
+
</div>
|
| 351 |
+
<div class="section" id="window-menu-shell-and-editor">
|
| 352 |
+
<h3>Window menu (Shell and Editor)<a class="headerlink" href="#window-menu-shell-and-editor" title="Permalink to this headline">¶</a></h3>
|
| 353 |
+
<p>Lists the names of all open windows; select one to bring it to the foreground
|
| 354 |
+
(deiconifying it if necessary).</p>
|
| 355 |
+
</div>
|
| 356 |
+
<div class="section" id="help-menu-shell-and-editor">
|
| 357 |
+
<h3>Help menu (Shell and Editor)<a class="headerlink" href="#help-menu-shell-and-editor" title="Permalink to this headline">¶</a></h3>
|
| 358 |
+
<dl class="simple">
|
| 359 |
+
<dt>About IDLE</dt><dd><p>Display version, copyright, license, credits, and more.</p>
|
| 360 |
+
</dd>
|
| 361 |
+
<dt>IDLE Help</dt><dd><p>Display this IDLE document, detailing the menu options, basic editing and
|
| 362 |
+
navigation, and other tips.</p>
|
| 363 |
+
</dd>
|
| 364 |
+
<dt>Python Docs</dt><dd><p>Access local Python documentation, if installed, or start a web browser
|
| 365 |
+
and open docs.python.org showing the latest Python documentation.</p>
|
| 366 |
+
</dd>
|
| 367 |
+
<dt>Turtle Demo</dt><dd><p>Run the turtledemo module with example Python code and turtle drawings.</p>
|
| 368 |
+
</dd>
|
| 369 |
+
</dl>
|
| 370 |
+
<p>Additional help sources may be added here with the Configure IDLE dialog under
|
| 371 |
+
the General tab. See the <a class="reference internal" href="#help-sources"><span class="std std-ref">Help sources</span></a> subsection below
|
| 372 |
+
for more on Help menu choices.</p>
|
| 373 |
+
</div>
|
| 374 |
+
<div class="section" id="context-menus">
|
| 375 |
+
<span id="index-4"></span><h3>Context Menus<a class="headerlink" href="#context-menus" title="Permalink to this headline">¶</a></h3>
|
| 376 |
+
<p>Open a context menu by right-clicking in a window (Control-click on macOS).
|
| 377 |
+
Context menus have the standard clipboard functions also on the Edit menu.</p>
|
| 378 |
+
<dl class="simple">
|
| 379 |
+
<dt>Cut</dt><dd><p>Copy selection into the system-wide clipboard; then delete the selection.</p>
|
| 380 |
+
</dd>
|
| 381 |
+
<dt>Copy</dt><dd><p>Copy selection into the system-wide clipboard.</p>
|
| 382 |
+
</dd>
|
| 383 |
+
<dt>Paste</dt><dd><p>Insert contents of the system-wide clipboard into the current window.</p>
|
| 384 |
+
</dd>
|
| 385 |
+
</dl>
|
| 386 |
+
<p>Editor windows also have breakpoint functions. Lines with a breakpoint set are
|
| 387 |
+
specially marked. Breakpoints only have an effect when running under the
|
| 388 |
+
debugger. Breakpoints for a file are saved in the user’s <code class="docutils literal notranslate"><span class="pre">.idlerc</span></code>
|
| 389 |
+
directory.</p>
|
| 390 |
+
<dl class="simple">
|
| 391 |
+
<dt>Set Breakpoint</dt><dd><p>Set a breakpoint on the current line.</p>
|
| 392 |
+
</dd>
|
| 393 |
+
<dt>Clear Breakpoint</dt><dd><p>Clear the breakpoint on that line.</p>
|
| 394 |
+
</dd>
|
| 395 |
+
</dl>
|
| 396 |
+
<p>Shell and Output windows also have the following.</p>
|
| 397 |
+
<dl class="simple">
|
| 398 |
+
<dt>Go to file/line</dt><dd><p>Same as in Debug menu.</p>
|
| 399 |
+
</dd>
|
| 400 |
+
</dl>
|
| 401 |
+
<p>The Shell window also has an output squeezing facility explained in the <em>Python
|
| 402 |
+
Shell window</em> subsection below.</p>
|
| 403 |
+
<dl class="simple">
|
| 404 |
+
<dt>Squeeze</dt><dd><p>If the cursor is over an output line, squeeze all the output between
|
| 405 |
+
the code above and the prompt below down to a ‘Squeezed text’ label.</p>
|
| 406 |
+
</dd>
|
| 407 |
+
</dl>
|
| 408 |
+
</div>
|
| 409 |
+
</div>
|
| 410 |
+
<div class="section" id="editing-and-navigation">
|
| 411 |
+
<span id="id2"></span><h2>Editing and navigation<a class="headerlink" href="#editing-and-navigation" title="Permalink to this headline">¶</a></h2>
|
| 412 |
+
<div class="section" id="editor-windows">
|
| 413 |
+
<h3>Editor windows<a class="headerlink" href="#editor-windows" title="Permalink to this headline">¶</a></h3>
|
| 414 |
+
<p>IDLE may open editor windows when it starts, depending on settings
|
| 415 |
+
and how you start IDLE. Thereafter, use the File menu. There can be only
|
| 416 |
+
one open editor window for a given file.</p>
|
| 417 |
+
<p>The title bar contains the name of the file, the full path, and the version
|
| 418 |
+
of Python and IDLE running the window. The status bar contains the line
|
| 419 |
+
number (‘Ln’) and column number (‘Col’). Line numbers start with 1;
|
| 420 |
+
column numbers with 0.</p>
|
| 421 |
+
<p>IDLE assumes that files with a known .py* extension contain Python code
|
| 422 |
+
and that other files do not. Run Python code with the Run menu.</p>
|
| 423 |
+
</div>
|
| 424 |
+
<div class="section" id="key-bindings">
|
| 425 |
+
<h3>Key bindings<a class="headerlink" href="#key-bindings" title="Permalink to this headline">¶</a></h3>
|
| 426 |
+
<p>In this section, ‘C’ refers to the <kbd class="kbd docutils literal notranslate">Control</kbd> key on Windows and Unix and
|
| 427 |
+
the <kbd class="kbd docutils literal notranslate">Command</kbd> key on macOS.</p>
|
| 428 |
+
<ul>
|
| 429 |
+
<li><p><kbd class="kbd docutils literal notranslate">Backspace</kbd> deletes to the left; <kbd class="kbd docutils literal notranslate">Del</kbd> deletes to the right</p></li>
|
| 430 |
+
<li><p><kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">C</kbd>-<kbd class="kbd docutils literal notranslate">Backspace</kbd></kbd> delete word left; <kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">C</kbd>-<kbd class="kbd docutils literal notranslate">Del</kbd></kbd> delete word to the right</p></li>
|
| 431 |
+
<li><p>Arrow keys and <kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">Page</kbd> <kbd class="kbd docutils literal notranslate">Up</kbd></kbd>/<kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">Page</kbd> <kbd class="kbd docutils literal notranslate">Down</kbd></kbd> to move around</p></li>
|
| 432 |
+
<li><p><kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">C</kbd>-<kbd class="kbd docutils literal notranslate">LeftArrow</kbd></kbd> and <kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">C</kbd>-<kbd class="kbd docutils literal notranslate">RightArrow</kbd></kbd> moves by words</p></li>
|
| 433 |
+
<li><p><kbd class="kbd docutils literal notranslate">Home</kbd>/<kbd class="kbd docutils literal notranslate">End</kbd> go to begin/end of line</p></li>
|
| 434 |
+
<li><p><kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">C</kbd>-<kbd class="kbd docutils literal notranslate">Home</kbd></kbd>/<kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">C</kbd>-<kbd class="kbd docutils literal notranslate">End</kbd></kbd> go to begin/end of file</p></li>
|
| 435 |
+
<li><p>Some useful Emacs bindings are inherited from Tcl/Tk:</p>
|
| 436 |
+
<blockquote>
|
| 437 |
+
<div><ul class="simple">
|
| 438 |
+
<li><p><kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">C</kbd>-<kbd class="kbd docutils literal notranslate">a</kbd></kbd> beginning of line</p></li>
|
| 439 |
+
<li><p><kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">C</kbd>-<kbd class="kbd docutils literal notranslate">e</kbd></kbd> end of line</p></li>
|
| 440 |
+
<li><p><kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">C</kbd>-<kbd class="kbd docutils literal notranslate">k</kbd></kbd> kill line (but doesn’t put it in clipboard)</p></li>
|
| 441 |
+
<li><p><kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">C</kbd>-<kbd class="kbd docutils literal notranslate">l</kbd></kbd> center window around the insertion point</p></li>
|
| 442 |
+
<li><p><kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">C</kbd>-<kbd class="kbd docutils literal notranslate">b</kbd></kbd> go backward one character without deleting (usually you can
|
| 443 |
+
also use the cursor key for this)</p></li>
|
| 444 |
+
<li><p><kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">C</kbd>-<kbd class="kbd docutils literal notranslate">f</kbd></kbd> go forward one character without deleting (usually you can
|
| 445 |
+
also use the cursor key for this)</p></li>
|
| 446 |
+
<li><p><kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">C</kbd>-<kbd class="kbd docutils literal notranslate">p</kbd></kbd> go up one line (usually you can also use the cursor key for
|
| 447 |
+
this)</p></li>
|
| 448 |
+
<li><p><kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">C</kbd>-<kbd class="kbd docutils literal notranslate">d</kbd></kbd> delete next character</p></li>
|
| 449 |
+
</ul>
|
| 450 |
+
</div></blockquote>
|
| 451 |
+
</li>
|
| 452 |
+
</ul>
|
| 453 |
+
<p>Standard keybindings (like <kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">C</kbd>-<kbd class="kbd docutils literal notranslate">c</kbd></kbd> to copy and <kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">C</kbd>-<kbd class="kbd docutils literal notranslate">v</kbd></kbd> to paste)
|
| 454 |
+
may work. Keybindings are selected in the Configure IDLE dialog.</p>
|
| 455 |
+
</div>
|
| 456 |
+
<div class="section" id="automatic-indentation">
|
| 457 |
+
<h3>Automatic indentation<a class="headerlink" href="#automatic-indentation" title="Permalink to this headline">¶</a></h3>
|
| 458 |
+
<p>After a block-opening statement, the next line is indented by 4 spaces (in the
|
| 459 |
+
Python Shell window by one tab). After certain keywords (break, return etc.)
|
| 460 |
+
the next line is dedented. In leading indentation, <kbd class="kbd docutils literal notranslate">Backspace</kbd> deletes up
|
| 461 |
+
to 4 spaces if they are there. <kbd class="kbd docutils literal notranslate">Tab</kbd> inserts spaces (in the Python
|
| 462 |
+
Shell window one tab), number depends on Indent width. Currently, tabs
|
| 463 |
+
are restricted to four spaces due to Tcl/Tk limitations.</p>
|
| 464 |
+
<p>See also the indent/dedent region commands on the
|
| 465 |
+
<a class="reference internal" href="#format-menu"><span class="std std-ref">Format menu</span></a>.</p>
|
| 466 |
+
</div>
|
| 467 |
+
<div class="section" id="completions">
|
| 468 |
+
<span id="id3"></span><h3>Completions<a class="headerlink" href="#completions" title="Permalink to this headline">¶</a></h3>
|
| 469 |
+
<p>Completions are supplied, when requested and available, for module
|
| 470 |
+
names, attributes of classes or functions, or filenames. Each request
|
| 471 |
+
method displays a completion box with existing names. (See tab
|
| 472 |
+
completions below for an exception.) For any box, change the name
|
| 473 |
+
being completed and the item highlighted in the box by
|
| 474 |
+
typing and deleting characters; by hitting <kbd class="kbd docutils literal notranslate">Up</kbd>, <kbd class="kbd docutils literal notranslate">Down</kbd>,
|
| 475 |
+
<kbd class="kbd docutils literal notranslate">PageUp</kbd>, <kbd class="kbd docutils literal notranslate">PageDown</kbd>, <kbd class="kbd docutils literal notranslate">Home</kbd>, and <kbd class="kbd docutils literal notranslate">End</kbd> keys;
|
| 476 |
+
and by a single click within the box. Close the box with <kbd class="kbd docutils literal notranslate">Escape</kbd>,
|
| 477 |
+
<kbd class="kbd docutils literal notranslate">Enter</kbd>, and double <kbd class="kbd docutils literal notranslate">Tab</kbd> keys or clicks outside the box.
|
| 478 |
+
A double click within the box selects and closes.</p>
|
| 479 |
+
<p>One way to open a box is to type a key character and wait for a
|
| 480 |
+
predefined interval. This defaults to 2 seconds; customize it
|
| 481 |
+
in the settings dialog. (To prevent auto popups, set the delay to a
|
| 482 |
+
large number of milliseconds, such as 100000000.) For imported module
|
| 483 |
+
names or class or function attributes, type ‘.’.
|
| 484 |
+
For filenames in the root directory, type <a class="reference internal" href="os.html#os.sep" title="os.sep"><code class="xref py py-data docutils literal notranslate"><span class="pre">os.sep</span></code></a> or
|
| 485 |
+
<a class="reference internal" href="os.html#os.altsep" title="os.altsep"><code class="xref py py-data docutils literal notranslate"><span class="pre">os.altsep</span></code></a> immediately after an opening quote. (On Windows,
|
| 486 |
+
one can specify a drive first.) Move into subdirectories by typing a
|
| 487 |
+
directory name and a separator.</p>
|
| 488 |
+
<p>Instead of waiting, or after a box is closed, open a completion box
|
| 489 |
+
immediately with Show Completions on the Edit menu. The default hot
|
| 490 |
+
key is <kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">C</kbd>-<kbd class="kbd docutils literal notranslate">space</kbd></kbd>. If one types a prefix for the desired name
|
| 491 |
+
before opening the box, the first match or near miss is made visible.
|
| 492 |
+
The result is the same as if one enters a prefix
|
| 493 |
+
after the box is displayed. Show Completions after a quote completes
|
| 494 |
+
filenames in the current directory instead of a root directory.</p>
|
| 495 |
+
<p>Hitting <kbd class="kbd docutils literal notranslate">Tab</kbd> after a prefix usually has the same effect as Show
|
| 496 |
+
Completions. (With no prefix, it indents.) However, if there is only
|
| 497 |
+
one match to the prefix, that match is immediately added to the editor
|
| 498 |
+
text without opening a box.</p>
|
| 499 |
+
<p>Invoking ‘Show Completions’, or hitting <kbd class="kbd docutils literal notranslate">Tab</kbd> after a prefix,
|
| 500 |
+
outside of a string and without a preceding ‘.’ opens a box with
|
| 501 |
+
keywords, builtin names, and available module-level names.</p>
|
| 502 |
+
<p>When editing code in an editor (as oppose to Shell), increase the
|
| 503 |
+
available module-level names by running your code
|
| 504 |
+
and not restarting the Shell thereafter. This is especially useful
|
| 505 |
+
after adding imports at the top of a file. This also increases
|
| 506 |
+
possible attribute completions.</p>
|
| 507 |
+
<p>Completion boxes intially exclude names beginning with ‘_’ or, for
|
| 508 |
+
modules, not included in ‘__all__’. The hidden names can be accessed
|
| 509 |
+
by typing ‘_’ after ‘.’, either before or after the box is opened.</p>
|
| 510 |
+
</div>
|
| 511 |
+
<div class="section" id="calltips">
|
| 512 |
+
<span id="id4"></span><h3>Calltips<a class="headerlink" href="#calltips" title="Permalink to this headline">¶</a></h3>
|
| 513 |
+
<p>A calltip is shown automatically when one types <kbd class="kbd docutils literal notranslate">(</kbd> after the name
|
| 514 |
+
of an <em>accessible</em> function. A function name expression may include
|
| 515 |
+
dots and subscripts. A calltip remains until it is clicked, the cursor
|
| 516 |
+
is moved out of the argument area, or <kbd class="kbd docutils literal notranslate">)</kbd> is typed. Whenever the
|
| 517 |
+
cursor is in the argument part of a definition, select Edit and “Show
|
| 518 |
+
Call Tip” on the menu or enter its shortcut to display a calltip.</p>
|
| 519 |
+
<p>The calltip consists of the function’s signature and docstring up to
|
| 520 |
+
the latter’s first blank line or the fifth non-blank line. (Some builtin
|
| 521 |
+
functions lack an accessible signature.) A ‘/’ or ‘*’ in the signature
|
| 522 |
+
indicates that the preceding or following arguments are passed by
|
| 523 |
+
position or name (keyword) only. Details are subject to change.</p>
|
| 524 |
+
<p>In Shell, the accessible functions depends on what modules have been
|
| 525 |
+
imported into the user process, including those imported by Idle itself,
|
| 526 |
+
and which definitions have been run, all since the last restart.</p>
|
| 527 |
+
<p>For example, restart the Shell and enter <code class="docutils literal notranslate"><span class="pre">itertools.count(</span></code>. A calltip
|
| 528 |
+
appears because Idle imports itertools into the user process for its own
|
| 529 |
+
use. (This could change.) Enter <code class="docutils literal notranslate"><span class="pre">turtle.write(</span></code> and nothing appears.
|
| 530 |
+
Idle does not itself import turtle. The menu entry and shortcut also do
|
| 531 |
+
nothing. Enter <code class="docutils literal notranslate"><span class="pre">import</span> <span class="pre">turtle</span></code>. Thereafter, <code class="docutils literal notranslate"><span class="pre">turtle.write(</span></code>
|
| 532 |
+
will display a calltip.</p>
|
| 533 |
+
<p>In an editor, import statements have no effect until one runs the file.
|
| 534 |
+
One might want to run a file after writing import statements, after
|
| 535 |
+
adding function definitions, or after opening an existing file.</p>
|
| 536 |
+
</div>
|
| 537 |
+
<div class="section" id="code-context">
|
| 538 |
+
<span id="id5"></span><h3>Code Context<a class="headerlink" href="#code-context" title="Permalink to this headline">¶</a></h3>
|
| 539 |
+
<p>Within an editor window containing Python code, code context can be toggled
|
| 540 |
+
in order to show or hide a pane at the top of the window. When shown, this
|
| 541 |
+
pane freezes the opening lines for block code, such as those beginning with
|
| 542 |
+
<code class="docutils literal notranslate"><span class="pre">class</span></code>, <code class="docutils literal notranslate"><span class="pre">def</span></code>, or <code class="docutils literal notranslate"><span class="pre">if</span></code> keywords, that would have otherwise scrolled
|
| 543 |
+
out of view. The size of the pane will be expanded and contracted as needed
|
| 544 |
+
to show the all current levels of context, up to the maximum number of
|
| 545 |
+
lines defined in the Configure IDLE dialog (which defaults to 15). If there
|
| 546 |
+
are no current context lines and the feature is toggled on, a single blank
|
| 547 |
+
line will display. Clicking on a line in the context pane will move that
|
| 548 |
+
line to the top of the editor.</p>
|
| 549 |
+
<p>The text and background colors for the context pane can be configured under
|
| 550 |
+
the Highlights tab in the Configure IDLE dialog.</p>
|
| 551 |
+
</div>
|
| 552 |
+
<div class="section" id="python-shell-window">
|
| 553 |
+
<h3>Python Shell window<a class="headerlink" href="#python-shell-window" title="Permalink to this headline">¶</a></h3>
|
| 554 |
+
<p>With IDLE’s Shell, one enters, edits, and recalls complete statements.
|
| 555 |
+
Most consoles and terminals only work with a single physical line at a time.</p>
|
| 556 |
+
<p>When one pastes code into Shell, it is not compiled and possibly executed
|
| 557 |
+
until one hits <kbd class="kbd docutils literal notranslate">Return</kbd>. One may edit pasted code first.
|
| 558 |
+
If one pastes more that one statement into Shell, the result will be a
|
| 559 |
+
<a class="reference internal" href="exceptions.html#SyntaxError" title="SyntaxError"><code class="xref py py-exc docutils literal notranslate"><span class="pre">SyntaxError</span></code></a> when multiple statements are compiled as if they were one.</p>
|
| 560 |
+
<p>The editing features described in previous subsections work when entering
|
| 561 |
+
code interactively. IDLE’s Shell window also responds to the following keys.</p>
|
| 562 |
+
<ul>
|
| 563 |
+
<li><p><kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">C</kbd>-<kbd class="kbd docutils literal notranslate">c</kbd></kbd> interrupts executing command</p></li>
|
| 564 |
+
<li><p><kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">C</kbd>-<kbd class="kbd docutils literal notranslate">d</kbd></kbd> sends end-of-file; closes window if typed at a <code class="docutils literal notranslate"><span class="pre">>>></span></code> prompt</p></li>
|
| 565 |
+
<li><p><kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">Alt</kbd>-<kbd class="kbd docutils literal notranslate">/</kbd></kbd> (Expand word) is also useful to reduce typing</p>
|
| 566 |
+
<p>Command history</p>
|
| 567 |
+
<ul class="simple">
|
| 568 |
+
<li><p><kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">Alt</kbd>-<kbd class="kbd docutils literal notranslate">p</kbd></kbd> retrieves previous command matching what you have typed. On
|
| 569 |
+
macOS use <kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">C</kbd>-<kbd class="kbd docutils literal notranslate">p</kbd></kbd>.</p></li>
|
| 570 |
+
<li><p><kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">Alt</kbd>-<kbd class="kbd docutils literal notranslate">n</kbd></kbd> retrieves next. On macOS use <kbd class="kbd docutils literal notranslate"><kbd class="kbd docutils literal notranslate">C</kbd>-<kbd class="kbd docutils literal notranslate">n</kbd></kbd>.</p></li>
|
| 571 |
+
<li><p><kbd class="kbd docutils literal notranslate">Return</kbd> while on any previous command retrieves that command</p></li>
|
| 572 |
+
</ul>
|
| 573 |
+
</li>
|
| 574 |
+
</ul>
|
| 575 |
+
</div>
|
| 576 |
+
<div class="section" id="text-colors">
|
| 577 |
+
<h3>Text colors<a class="headerlink" href="#text-colors" title="Permalink to this headline">¶</a></h3>
|
| 578 |
+
<p>Idle defaults to black on white text, but colors text with special meanings.
|
| 579 |
+
For the shell, these are shell output, shell error, user output, and
|
| 580 |
+
user error. For Python code, at the shell prompt or in an editor, these are
|
| 581 |
+
keywords, builtin class and function names, names following <code class="docutils literal notranslate"><span class="pre">class</span></code> and
|
| 582 |
+
<code class="docutils literal notranslate"><span class="pre">def</span></code>, strings, and comments. For any text window, these are the cursor (when
|
| 583 |
+
present), found text (when possible), and selected text.</p>
|
| 584 |
+
<p>Text coloring is done in the background, so uncolorized text is occasionally
|
| 585 |
+
visible. To change the color scheme, use the Configure IDLE dialog
|
| 586 |
+
Highlighting tab. The marking of debugger breakpoint lines in the editor and
|
| 587 |
+
text in popups and dialogs is not user-configurable.</p>
|
| 588 |
+
</div>
|
| 589 |
+
</div>
|
| 590 |
+
<div class="section" id="startup-and-code-execution">
|
| 591 |
+
<h2>Startup and code execution<a class="headerlink" href="#startup-and-code-execution" title="Permalink to this headline">¶</a></h2>
|
| 592 |
+
<p>Upon startup with the <code class="docutils literal notranslate"><span class="pre">-s</span></code> option, IDLE will execute the file referenced by
|
| 593 |
+
the environment variables <span class="target" id="index-5"></span><code class="xref std std-envvar docutils literal notranslate"><span class="pre">IDLESTARTUP</span></code> or <span class="target" id="index-6"></span><a class="reference internal" href="../using/cmdline.html#envvar-PYTHONSTARTUP"><code class="xref std std-envvar docutils literal notranslate"><span class="pre">PYTHONSTARTUP</span></code></a>.
|
| 594 |
+
IDLE first checks for <code class="docutils literal notranslate"><span class="pre">IDLESTARTUP</span></code>; if <code class="docutils literal notranslate"><span class="pre">IDLESTARTUP</span></code> is present the file
|
| 595 |
+
referenced is run. If <code class="docutils literal notranslate"><span class="pre">IDLESTARTUP</span></code> is not present, IDLE checks for
|
| 596 |
+
<code class="docutils literal notranslate"><span class="pre">PYTHONSTARTUP</span></code>. Files referenced by these environment variables are
|
| 597 |
+
convenient places to store functions that are used frequently from the IDLE
|
| 598 |
+
shell, or for executing import statements to import common modules.</p>
|
| 599 |
+
<p>In addition, <code class="docutils literal notranslate"><span class="pre">Tk</span></code> also loads a startup file if it is present. Note that the
|
| 600 |
+
Tk file is loaded unconditionally. This additional file is <code class="docutils literal notranslate"><span class="pre">.Idle.py</span></code> and is
|
| 601 |
+
looked for in the user’s home directory. Statements in this file will be
|
| 602 |
+
executed in the Tk namespace, so this file is not useful for importing
|
| 603 |
+
functions to be used from IDLE’s Python shell.</p>
|
| 604 |
+
<div class="section" id="command-line-usage">
|
| 605 |
+
<h3>Command line usage<a class="headerlink" href="#command-line-usage" title="Permalink to this headline">¶</a></h3>
|
| 606 |
+
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>idle.py [-c command] [-d] [-e] [-h] [-i] [-r file] [-s] [-t title] [-] [arg] ...
|
| 607 |
+
|
| 608 |
+
-c command run command in the shell window
|
| 609 |
+
-d enable debugger and open shell window
|
| 610 |
+
-e open editor window
|
| 611 |
+
-h print help message with legal combinations and exit
|
| 612 |
+
-i open shell window
|
| 613 |
+
-r file run file in shell window
|
| 614 |
+
-s run $IDLESTARTUP or $PYTHONSTARTUP first, in shell window
|
| 615 |
+
-t title set title of shell window
|
| 616 |
+
- run stdin in shell (- must be last option before args)
|
| 617 |
+
</pre></div>
|
| 618 |
+
</div>
|
| 619 |
+
<p>If there are arguments:</p>
|
| 620 |
+
<ul class="simple">
|
| 621 |
+
<li><p>If <code class="docutils literal notranslate"><span class="pre">-</span></code>, <code class="docutils literal notranslate"><span class="pre">-c</span></code>, or <code class="docutils literal notranslate"><span class="pre">r</span></code> is used, all arguments are placed in
|
| 622 |
+
<code class="docutils literal notranslate"><span class="pre">sys.argv[1:...]</span></code> and <code class="docutils literal notranslate"><span class="pre">sys.argv[0]</span></code> is set to <code class="docutils literal notranslate"><span class="pre">''</span></code>, <code class="docutils literal notranslate"><span class="pre">'-c'</span></code>,
|
| 623 |
+
or <code class="docutils literal notranslate"><span class="pre">'-r'</span></code>. No editor window is opened, even if that is the default
|
| 624 |
+
set in the Options dialog.</p></li>
|
| 625 |
+
<li><p>Otherwise, arguments are files opened for editing and
|
| 626 |
+
<code class="docutils literal notranslate"><span class="pre">sys.argv</span></code> reflects the arguments passed to IDLE itself.</p></li>
|
| 627 |
+
</ul>
|
| 628 |
+
</div>
|
| 629 |
+
<div class="section" id="startup-failure">
|
| 630 |
+
<h3>Startup failure<a class="headerlink" href="#startup-failure" title="Permalink to this headline">¶</a></h3>
|
| 631 |
+
<p>IDLE uses a socket to communicate between the IDLE GUI process and the user
|
| 632 |
+
code execution process. A connection must be established whenever the Shell
|
| 633 |
+
starts or restarts. (The latter is indicated by a divider line that says
|
| 634 |
+
‘RESTART’). If the user process fails to connect to the GUI process, it
|
| 635 |
+
usually displays a <code class="docutils literal notranslate"><span class="pre">Tk</span></code> error box with a ‘cannot connect’ message
|
| 636 |
+
that directs the user here. It then exits.</p>
|
| 637 |
+
<p>One specific connection failure on Unix systems results from
|
| 638 |
+
misconfigured masquerading rules somewhere in a system’s network setup.
|
| 639 |
+
When IDLE is started from a terminal, one will see a message starting
|
| 640 |
+
with <code class="docutils literal notranslate"><span class="pre">**</span> <span class="pre">Invalid</span> <span class="pre">host:</span></code>.
|
| 641 |
+
The valid value is <code class="docutils literal notranslate"><span class="pre">127.0.0.1</span> <span class="pre">(idlelib.rpc.LOCALHOST)</span></code>.
|
| 642 |
+
One can diagnose with <code class="docutils literal notranslate"><span class="pre">tcpconnect</span> <span class="pre">-irv</span> <span class="pre">127.0.0.1</span> <span class="pre">6543</span></code> in one
|
| 643 |
+
terminal window and <code class="docutils literal notranslate"><span class="pre">tcplisten</span> <span class="pre"><same</span> <span class="pre">args></span></code> in another.</p>
|
| 644 |
+
<p>A common cause of failure is a user-written file with the same name as a
|
| 645 |
+
standard library module, such as <em>random.py</em> and <em>tkinter.py</em>. When such a
|
| 646 |
+
file is located in the same directory as a file that is about to be run,
|
| 647 |
+
IDLE cannot import the stdlib file. The current fix is to rename the
|
| 648 |
+
user file.</p>
|
| 649 |
+
<p>Though less common than in the past, an antivirus or firewall program may
|
| 650 |
+
stop the connection. If the program cannot be taught to allow the
|
| 651 |
+
connection, then it must be turned off for IDLE to work. It is safe to
|
| 652 |
+
allow this internal connection because no data is visible on external
|
| 653 |
+
ports. A similar problem is a network mis-configuration that blocks
|
| 654 |
+
connections.</p>
|
| 655 |
+
<p>Python installation issues occasionally stop IDLE: multiple versions can
|
| 656 |
+
clash, or a single installation might need admin access. If one undo the
|
| 657 |
+
clash, or cannot or does not want to run as admin, it might be easiest to
|
| 658 |
+
completely remove Python and start over.</p>
|
| 659 |
+
<p>A zombie pythonw.exe process could be a problem. On Windows, use Task
|
| 660 |
+
Manager to check for one and stop it if there is. Sometimes a restart
|
| 661 |
+
initiated by a program crash or Keyboard Interrupt (control-C) may fail
|
| 662 |
+
to connect. Dismissing the error box or using Restart Shell on the Shell
|
| 663 |
+
menu may fix a temporary problem.</p>
|
| 664 |
+
<p>When IDLE first starts, it attempts to read user configuration files in
|
| 665 |
+
<code class="docutils literal notranslate"><span class="pre">~/.idlerc/</span></code> (~ is one’s home directory). If there is a problem, an error
|
| 666 |
+
message should be displayed. Leaving aside random disk glitches, this can
|
| 667 |
+
be prevented by never editing the files by hand. Instead, use the
|
| 668 |
+
configuration dialog, under Options. Once there is an error in a user
|
| 669 |
+
configuration file, the best solution may be to delete it and start over
|
| 670 |
+
with the settings dialog.</p>
|
| 671 |
+
<p>If IDLE quits with no message, and it was not started from a console, try
|
| 672 |
+
starting it from a console or terminal (<code class="docutils literal notranslate"><span class="pre">python</span> <span class="pre">-m</span> <span class="pre">idlelib</span></code>) and see if
|
| 673 |
+
this results in an error message.</p>
|
| 674 |
+
<p>On Unix-based systems with tcl/tk older than <code class="docutils literal notranslate"><span class="pre">8.6.11</span></code> (see
|
| 675 |
+
<code class="docutils literal notranslate"><span class="pre">About</span> <span class="pre">IDLE</span></code>) certain characters of certain fonts can cause
|
| 676 |
+
a tk failure with a message to the terminal. This can happen either
|
| 677 |
+
if one starts IDLE to edit a file with such a character or later
|
| 678 |
+
when entering such a character. If one cannot upgrade tcl/tk,
|
| 679 |
+
then re-configure IDLE to use a font that works better.</p>
|
| 680 |
+
</div>
|
| 681 |
+
<div class="section" id="running-user-code">
|
| 682 |
+
<h3>Running user code<a class="headerlink" href="#running-user-code" title="Permalink to this headline">¶</a></h3>
|
| 683 |
+
<p>With rare exceptions, the result of executing Python code with IDLE is
|
| 684 |
+
intended to be the same as executing the same code by the default method,
|
| 685 |
+
directly with Python in a text-mode system console or terminal window.
|
| 686 |
+
However, the different interface and operation occasionally affect
|
| 687 |
+
visible results. For instance, <code class="docutils literal notranslate"><span class="pre">sys.modules</span></code> starts with more entries,
|
| 688 |
+
and <code class="docutils literal notranslate"><span class="pre">threading.activeCount()</span></code> returns 2 instead of 1.</p>
|
| 689 |
+
<p>By default, IDLE runs user code in a separate OS process rather than in
|
| 690 |
+
the user interface process that runs the shell and editor. In the execution
|
| 691 |
+
process, it replaces <code class="docutils literal notranslate"><span class="pre">sys.stdin</span></code>, <code class="docutils literal notranslate"><span class="pre">sys.stdout</span></code>, and <code class="docutils literal notranslate"><span class="pre">sys.stderr</span></code>
|
| 692 |
+
with objects that get input from and send output to the Shell window.
|
| 693 |
+
The original values stored in <code class="docutils literal notranslate"><span class="pre">sys.__stdin__</span></code>, <code class="docutils literal notranslate"><span class="pre">sys.__stdout__</span></code>, and
|
| 694 |
+
<code class="docutils literal notranslate"><span class="pre">sys.__stderr__</span></code> are not touched, but may be <code class="docutils literal notranslate"><span class="pre">None</span></code>.</p>
|
| 695 |
+
<p>Sending print output from one process to a text widget in another is
|
| 696 |
+
slower than printing to a system terminal in the same process.
|
| 697 |
+
This has the most effect when printing multiple arguments, as the string
|
| 698 |
+
for each argument, each separator, the newline are sent separately.
|
| 699 |
+
For development, this is usually not a problem, but if one wants to
|
| 700 |
+
print faster in IDLE, format and join together everything one wants
|
| 701 |
+
displayed together and then print a single string. Both format strings
|
| 702 |
+
and <a class="reference internal" href="stdtypes.html#str.join" title="str.join"><code class="xref py py-meth docutils literal notranslate"><span class="pre">str.join()</span></code></a> can help combine fields and lines.</p>
|
| 703 |
+
<p>IDLE’s standard stream replacements are not inherited by subprocesses
|
| 704 |
+
created in the execution process, whether directly by user code or by
|
| 705 |
+
modules such as multiprocessing. If such subprocess use <code class="docutils literal notranslate"><span class="pre">input</span></code> from
|
| 706 |
+
sys.stdin or <code class="docutils literal notranslate"><span class="pre">print</span></code> or <code class="docutils literal notranslate"><span class="pre">write</span></code> to sys.stdout or sys.stderr,
|
| 707 |
+
IDLE should be started in a command line window. The secondary subprocess
|
| 708 |
+
will then be attached to that window for input and output.</p>
|
| 709 |
+
<p>If <code class="docutils literal notranslate"><span class="pre">sys</span></code> is reset by user code, such as with <code class="docutils literal notranslate"><span class="pre">importlib.reload(sys)</span></code>,
|
| 710 |
+
IDLE’s changes are lost and input from the keyboard and output to the screen
|
| 711 |
+
will not work correctly.</p>
|
| 712 |
+
<p>When Shell has the focus, it controls the keyboard and screen. This is
|
| 713 |
+
normally transparent, but functions that directly access the keyboard
|
| 714 |
+
and screen will not work. These include system-specific functions that
|
| 715 |
+
determine whether a key has been pressed and if so, which.</p>
|
| 716 |
+
<p>The IDLE code running in the execution process adds frames to the call stack
|
| 717 |
+
that would not be there otherwise. IDLE wraps <code class="docutils literal notranslate"><span class="pre">sys.getrecursionlimit</span></code> and
|
| 718 |
+
<code class="docutils literal notranslate"><span class="pre">sys.setrecursionlimit</span></code> to reduce the effect of the additional stack
|
| 719 |
+
frames.</p>
|
| 720 |
+
<p>When user code raises SystemExit either directly or by calling sys.exit,
|
| 721 |
+
IDLE returns to a Shell prompt instead of exiting.</p>
|
| 722 |
+
</div>
|
| 723 |
+
<div class="section" id="user-output-in-shell">
|
| 724 |
+
<h3>User output in Shell<a class="headerlink" href="#user-output-in-shell" title="Permalink to this headline">¶</a></h3>
|
| 725 |
+
<p>When a program outputs text, the result is determined by the
|
| 726 |
+
corresponding output device. When IDLE executes user code, <code class="docutils literal notranslate"><span class="pre">sys.stdout</span></code>
|
| 727 |
+
and <code class="docutils literal notranslate"><span class="pre">sys.stderr</span></code> are connected to the display area of IDLE’s Shell. Some of
|
| 728 |
+
its features are inherited from the underlying Tk Text widget. Others
|
| 729 |
+
are programmed additions. Where it matters, Shell is designed for development
|
| 730 |
+
rather than production runs.</p>
|
| 731 |
+
<p>For instance, Shell never throws away output. A program that sends unlimited
|
| 732 |
+
output to Shell will eventually fill memory, resulting in a memory error.
|
| 733 |
+
In contrast, some system text windows only keep the last n lines of output.
|
| 734 |
+
A Windows console, for instance, keeps a user-settable 1 to 9999 lines,
|
| 735 |
+
with 300 the default.</p>
|
| 736 |
+
<p>A Tk Text widget, and hence IDLE’s Shell, displays characters (codepoints) in
|
| 737 |
+
the BMP (Basic Multilingual Plane) subset of Unicode. Which characters are
|
| 738 |
+
displayed with a proper glyph and which with a replacement box depends on the
|
| 739 |
+
operating system and installed fonts. Tab characters cause the following text
|
| 740 |
+
to begin after the next tab stop. (They occur every 8 ‘characters’). Newline
|
| 741 |
+
characters cause following text to appear on a new line. Other control
|
| 742 |
+
characters are ignored or displayed as a space, box, or something else,
|
| 743 |
+
depending on the operating system and font. (Moving the text cursor through
|
| 744 |
+
such output with arrow keys may exhibit some surprising spacing behavior.)</p>
|
| 745 |
+
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">s</span> <span class="o">=</span> <span class="s1">'a</span><span class="se">\t</span><span class="s1">b</span><span class="se">\a</span><span class="s1"><</span><span class="se">\x02</span><span class="s1">><</span><span class="se">\r</span><span class="s1">></span><span class="se">\b</span><span class="s1">c</span><span class="se">\n</span><span class="s1">d'</span> <span class="c1"># Enter 22 chars.</span>
|
| 746 |
+
<span class="gp">>>> </span><span class="nb">len</span><span class="p">(</span><span class="n">s</span><span class="p">)</span>
|
| 747 |
+
<span class="go">14</span>
|
| 748 |
+
<span class="gp">>>> </span><span class="n">s</span> <span class="c1"># Display repr(s)</span>
|
| 749 |
+
<span class="go">'a\tb\x07<\x02><\r>\x08c\nd'</span>
|
| 750 |
+
<span class="gp">>>> </span><span class="nb">print</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="s1">''</span><span class="p">)</span> <span class="c1"># Display s as is.</span>
|
| 751 |
+
<span class="go"># Result varies by OS and font. Try it.</span>
|
| 752 |
+
</pre></div>
|
| 753 |
+
</div>
|
| 754 |
+
<p>The <code class="docutils literal notranslate"><span class="pre">repr</span></code> function is used for interactive echo of expression
|
| 755 |
+
values. It returns an altered version of the input string in which
|
| 756 |
+
control codes, some BMP codepoints, and all non-BMP codepoints are
|
| 757 |
+
replaced with escape codes. As demonstrated above, it allows one to
|
| 758 |
+
identify the characters in a string, regardless of how they are displayed.</p>
|
| 759 |
+
<p>Normal and error output are generally kept separate (on separate lines)
|
| 760 |
+
from code input and each other. They each get different highlight colors.</p>
|
| 761 |
+
<p>For SyntaxError tracebacks, the normal ‘^’ marking where the error was
|
| 762 |
+
detected is replaced by coloring the text with an error highlight.
|
| 763 |
+
When code run from a file causes other exceptions, one may right click
|
| 764 |
+
on a traceback line to jump to the corresponding line in an IDLE editor.
|
| 765 |
+
The file will be opened if necessary.</p>
|
| 766 |
+
<p>Shell has a special facility for squeezing output lines down to a
|
| 767 |
+
‘Squeezed text’ label. This is done automatically
|
| 768 |
+
for output over N lines (N = 50 by default).
|
| 769 |
+
N can be changed in the PyShell section of the General
|
| 770 |
+
page of the Settings dialog. Output with fewer lines can be squeezed by
|
| 771 |
+
right clicking on the output. This can be useful lines long enough to slow
|
| 772 |
+
down scrolling.</p>
|
| 773 |
+
<p>Squeezed output is expanded in place by double-clicking the label.
|
| 774 |
+
It can also be sent to the clipboard or a separate view window by
|
| 775 |
+
right-clicking the label.</p>
|
| 776 |
+
</div>
|
| 777 |
+
<div class="section" id="developing-tkinter-applications">
|
| 778 |
+
<h3>Developing tkinter applications<a class="headerlink" href="#developing-tkinter-applications" title="Permalink to this headline">¶</a></h3>
|
| 779 |
+
<p>IDLE is intentionally different from standard Python in order to
|
| 780 |
+
facilitate development of tkinter programs. Enter <code class="docutils literal notranslate"><span class="pre">import</span> <span class="pre">tkinter</span> <span class="pre">as</span> <span class="pre">tk;</span>
|
| 781 |
+
<span class="pre">root</span> <span class="pre">=</span> <span class="pre">tk.Tk()</span></code> in standard Python and nothing appears. Enter the same
|
| 782 |
+
in IDLE and a tk window appears. In standard Python, one must also enter
|
| 783 |
+
<code class="docutils literal notranslate"><span class="pre">root.update()</span></code> to see the window. IDLE does the equivalent in the
|
| 784 |
+
background, about 20 times a second, which is about every 50 milliseconds.
|
| 785 |
+
Next enter <code class="docutils literal notranslate"><span class="pre">b</span> <span class="pre">=</span> <span class="pre">tk.Button(root,</span> <span class="pre">text='button');</span> <span class="pre">b.pack()</span></code>. Again,
|
| 786 |
+
nothing visibly changes in standard Python until one enters <code class="docutils literal notranslate"><span class="pre">root.update()</span></code>.</p>
|
| 787 |
+
<p>Most tkinter programs run <code class="docutils literal notranslate"><span class="pre">root.mainloop()</span></code>, which usually does not
|
| 788 |
+
return until the tk app is destroyed. If the program is run with
|
| 789 |
+
<code class="docutils literal notranslate"><span class="pre">python</span> <span class="pre">-i</span></code> or from an IDLE editor, a <code class="docutils literal notranslate"><span class="pre">>>></span></code> shell prompt does not
|
| 790 |
+
appear until <code class="docutils literal notranslate"><span class="pre">mainloop()</span></code> returns, at which time there is nothing left
|
| 791 |
+
to interact with.</p>
|
| 792 |
+
<p>When running a tkinter program from an IDLE editor, one can comment out
|
| 793 |
+
the mainloop call. One then gets a shell prompt immediately and can
|
| 794 |
+
interact with the live application. One just has to remember to
|
| 795 |
+
re-enable the mainloop call when running in standard Python.</p>
|
| 796 |
+
</div>
|
| 797 |
+
<div class="section" id="running-without-a-subprocess">
|
| 798 |
+
<h3>Running without a subprocess<a class="headerlink" href="#running-without-a-subprocess" title="Permalink to this headline">¶</a></h3>
|
| 799 |
+
<p>By default, IDLE executes user code in a separate subprocess via a socket,
|
| 800 |
+
which uses the internal loopback interface. This connection is not
|
| 801 |
+
externally visible and no data is sent to or received from the Internet.
|
| 802 |
+
If firewall software complains anyway, you can ignore it.</p>
|
| 803 |
+
<p>If the attempt to make the socket connection fails, Idle will notify you.
|
| 804 |
+
Such failures are sometimes transient, but if persistent, the problem
|
| 805 |
+
may be either a firewall blocking the connection or misconfiguration of
|
| 806 |
+
a particular system. Until the problem is fixed, one can run Idle with
|
| 807 |
+
the -n command line switch.</p>
|
| 808 |
+
<p>If IDLE is started with the -n command line switch it will run in a
|
| 809 |
+
single process and will not create the subprocess which runs the RPC
|
| 810 |
+
Python execution server. This can be useful if Python cannot create
|
| 811 |
+
the subprocess or the RPC socket interface on your platform. However,
|
| 812 |
+
in this mode user code is not isolated from IDLE itself. Also, the
|
| 813 |
+
environment is not restarted when Run/Run Module (F5) is selected. If
|
| 814 |
+
your code has been modified, you must reload() the affected modules and
|
| 815 |
+
re-import any specific items (e.g. from foo import baz) if the changes
|
| 816 |
+
are to take effect. For these reasons, it is preferable to run IDLE
|
| 817 |
+
with the default subprocess if at all possible.</p>
|
| 818 |
+
<div class="deprecated">
|
| 819 |
+
<p><span class="versionmodified deprecated">Deprecated since version 3.4.</span></p>
|
| 820 |
+
</div>
|
| 821 |
+
</div>
|
| 822 |
+
</div>
|
| 823 |
+
<div class="section" id="help-and-preferences">
|
| 824 |
+
<h2>Help and preferences<a class="headerlink" href="#help-and-preferences" title="Permalink to this headline">¶</a></h2>
|
| 825 |
+
<div class="section" id="help-sources">
|
| 826 |
+
<span id="id6"></span><h3>Help sources<a class="headerlink" href="#help-sources" title="Permalink to this headline">¶</a></h3>
|
| 827 |
+
<p>Help menu entry “IDLE Help” displays a formatted html version of the
|
| 828 |
+
IDLE chapter of the Library Reference. The result, in a read-only
|
| 829 |
+
tkinter text window, is close to what one sees in a web browser.
|
| 830 |
+
Navigate through the text with a mousewheel,
|
| 831 |
+
the scrollbar, or up and down arrow keys held down.
|
| 832 |
+
Or click the TOC (Table of Contents) button and select a section
|
| 833 |
+
header in the opened box.</p>
|
| 834 |
+
<p>Help menu entry “Python Docs” opens the extensive sources of help,
|
| 835 |
+
including tutorials, available at <code class="docutils literal notranslate"><span class="pre">docs.python.org/x.y</span></code>, where ‘x.y’
|
| 836 |
+
is the currently running Python version. If your system
|
| 837 |
+
has an off-line copy of the docs (this may be an installation option),
|
| 838 |
+
that will be opened instead.</p>
|
| 839 |
+
<p>Selected URLs can be added or removed from the help menu at any time using the
|
| 840 |
+
General tab of the Configure IDLE dialog.</p>
|
| 841 |
+
</div>
|
| 842 |
+
<div class="section" id="setting-preferences">
|
| 843 |
+
<span id="preferences"></span><h3>Setting preferences<a class="headerlink" href="#setting-preferences" title="Permalink to this headline">¶</a></h3>
|
| 844 |
+
<p>The font preferences, highlighting, keys, and general preferences can be
|
| 845 |
+
changed via Configure IDLE on the Option menu.
|
| 846 |
+
Non-default user settings are saved in a <code class="docutils literal notranslate"><span class="pre">.idlerc</span></code> directory in the user’s
|
| 847 |
+
home directory. Problems caused by bad user configuration files are solved
|
| 848 |
+
by editing or deleting one or more of the files in <code class="docutils literal notranslate"><span class="pre">.idlerc</span></code>.</p>
|
| 849 |
+
<p>On the Font tab, see the text sample for the effect of font face and size
|
| 850 |
+
on multiple characters in multiple languages. Edit the sample to add
|
| 851 |
+
other characters of personal interest. Use the sample to select
|
| 852 |
+
monospaced fonts. If particular characters have problems in Shell or an
|
| 853 |
+
editor, add them to the top of the sample and try changing first size
|
| 854 |
+
and then font.</p>
|
| 855 |
+
<p>On the Highlights and Keys tab, select a built-in or custom color theme
|
| 856 |
+
and key set. To use a newer built-in color theme or key set with older
|
| 857 |
+
IDLEs, save it as a new custom theme or key set and it well be accessible
|
| 858 |
+
to older IDLEs.</p>
|
| 859 |
+
</div>
|
| 860 |
+
<div class="section" id="idle-on-macos">
|
| 861 |
+
<h3>IDLE on macOS<a class="headerlink" href="#idle-on-macos" title="Permalink to this headline">¶</a></h3>
|
| 862 |
+
<p>Under System Preferences: Dock, one can set “Prefer tabs when opening
|
| 863 |
+
documents” to “Always”. This setting is not compatible with the tk/tkinter
|
| 864 |
+
GUI framework used by IDLE, and it breaks a few IDLE features.</p>
|
| 865 |
+
</div>
|
| 866 |
+
<div class="section" id="extensions">
|
| 867 |
+
<h3>Extensions<a class="headerlink" href="#extensions" title="Permalink to this headline">¶</a></h3>
|
| 868 |
+
<p>IDLE contains an extension facility. Preferences for extensions can be
|
| 869 |
+
changed with the Extensions tab of the preferences dialog. See the
|
| 870 |
+
beginning of config-extensions.def in the idlelib directory for further
|
| 871 |
+
information. The only current default extension is zzdummy, an example
|
| 872 |
+
also used for testing.</p>
|
| 873 |
+
</div>
|
| 874 |
+
</div>
|
| 875 |
+
</div>
|
| 876 |
+
|
| 877 |
+
|
| 878 |
+
<div class="clearer"></div>
|
| 879 |
+
</div>
|
| 880 |
+
</div>
|
| 881 |
+
</div>
|
| 882 |
+
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
| 883 |
+
<div class="sphinxsidebarwrapper">
|
| 884 |
+
<h3><a href="../contents.html">Table of Contents</a></h3>
|
| 885 |
+
<ul>
|
| 886 |
+
<li><a class="reference internal" href="#">IDLE</a><ul>
|
| 887 |
+
<li><a class="reference internal" href="#menus">Menus</a><ul>
|
| 888 |
+
<li><a class="reference internal" href="#file-menu-shell-and-editor">File menu (Shell and Editor)</a></li>
|
| 889 |
+
<li><a class="reference internal" href="#edit-menu-shell-and-editor">Edit menu (Shell and Editor)</a></li>
|
| 890 |
+
<li><a class="reference internal" href="#format-menu-editor-window-only">Format menu (Editor window only)</a></li>
|
| 891 |
+
<li><a class="reference internal" href="#run-menu-editor-window-only">Run menu (Editor window only)</a></li>
|
| 892 |
+
<li><a class="reference internal" href="#shell-menu-shell-window-only">Shell menu (Shell window only)</a></li>
|
| 893 |
+
<li><a class="reference internal" href="#debug-menu-shell-window-only">Debug menu (Shell window only)</a></li>
|
| 894 |
+
<li><a class="reference internal" href="#options-menu-shell-and-editor">Options menu (Shell and Editor)</a></li>
|
| 895 |
+
<li><a class="reference internal" href="#window-menu-shell-and-editor">Window menu (Shell and Editor)</a></li>
|
| 896 |
+
<li><a class="reference internal" href="#help-menu-shell-and-editor">Help menu (Shell and Editor)</a></li>
|
| 897 |
+
<li><a class="reference internal" href="#context-menus">Context Menus</a></li>
|
| 898 |
+
</ul>
|
| 899 |
+
</li>
|
| 900 |
+
<li><a class="reference internal" href="#editing-and-navigation">Editing and navigation</a><ul>
|
| 901 |
+
<li><a class="reference internal" href="#editor-windows">Editor windows</a></li>
|
| 902 |
+
<li><a class="reference internal" href="#key-bindings">Key bindings</a></li>
|
| 903 |
+
<li><a class="reference internal" href="#automatic-indentation">Automatic indentation</a></li>
|
| 904 |
+
<li><a class="reference internal" href="#completions">Completions</a></li>
|
| 905 |
+
<li><a class="reference internal" href="#calltips">Calltips</a></li>
|
| 906 |
+
<li><a class="reference internal" href="#code-context">Code Context</a></li>
|
| 907 |
+
<li><a class="reference internal" href="#python-shell-window">Python Shell window</a></li>
|
| 908 |
+
<li><a class="reference internal" href="#text-colors">Text colors</a></li>
|
| 909 |
+
</ul>
|
| 910 |
+
</li>
|
| 911 |
+
<li><a class="reference internal" href="#startup-and-code-execution">Startup and code execution</a><ul>
|
| 912 |
+
<li><a class="reference internal" href="#command-line-usage">Command line usage</a></li>
|
| 913 |
+
<li><a class="reference internal" href="#startup-failure">Startup failure</a></li>
|
| 914 |
+
<li><a class="reference internal" href="#running-user-code">Running user code</a></li>
|
| 915 |
+
<li><a class="reference internal" href="#user-output-in-shell">User output in Shell</a></li>
|
| 916 |
+
<li><a class="reference internal" href="#developing-tkinter-applications">Developing tkinter applications</a></li>
|
| 917 |
+
<li><a class="reference internal" href="#running-without-a-subprocess">Running without a subprocess</a></li>
|
| 918 |
+
</ul>
|
| 919 |
+
</li>
|
| 920 |
+
<li><a class="reference internal" href="#help-and-preferences">Help and preferences</a><ul>
|
| 921 |
+
<li><a class="reference internal" href="#help-sources">Help sources</a></li>
|
| 922 |
+
<li><a class="reference internal" href="#setting-preferences">Setting preferences</a></li>
|
| 923 |
+
<li><a class="reference internal" href="#idle-on-macos">IDLE on macOS</a></li>
|
| 924 |
+
<li><a class="reference internal" href="#extensions">Extensions</a></li>
|
| 925 |
+
</ul>
|
| 926 |
+
</li>
|
| 927 |
+
</ul>
|
| 928 |
+
</li>
|
| 929 |
+
</ul>
|
| 930 |
+
|
| 931 |
+
<h4>Previous topic</h4>
|
| 932 |
+
<p class="topless"><a href="tkinter.tix.html"
|
| 933 |
+
title="previous chapter"><code class="xref py py-mod docutils literal notranslate"><span class="pre">tkinter.tix</span></code> — Extension widgets for Tk</a></p>
|
| 934 |
+
<h4>Next topic</h4>
|
| 935 |
+
<p class="topless"><a href="othergui.html"
|
| 936 |
+
title="next chapter">Other Graphical User Interface Packages</a></p>
|
| 937 |
+
<div role="note" aria-label="source link">
|
| 938 |
+
<h3>This Page</h3>
|
| 939 |
+
<ul class="this-page-menu">
|
| 940 |
+
<li><a href="../bugs.html">Report a Bug</a></li>
|
| 941 |
+
<li>
|
| 942 |
+
<a href="https://github.com/python/cpython/blob/master/Doc/library/idle.rst"
|
| 943 |
+
rel="nofollow">Show Source
|
| 944 |
+
</a>
|
| 945 |
+
</li>
|
| 946 |
+
</ul>
|
| 947 |
+
</div>
|
| 948 |
+
</div>
|
| 949 |
+
</div>
|
| 950 |
+
<div class="clearer"></div>
|
| 951 |
+
</div>
|
| 952 |
+
<div class="related" role="navigation" aria-label="related navigation">
|
| 953 |
+
<h3>Navigation</h3>
|
| 954 |
+
<ul>
|
| 955 |
+
<li class="right" style="margin-right: 10px">
|
| 956 |
+
<a href="../genindex.html" title="General Index"
|
| 957 |
+
>index</a></li>
|
| 958 |
+
<li class="right" >
|
| 959 |
+
<a href="../py-modindex.html" title="Python Module Index"
|
| 960 |
+
>modules</a> |</li>
|
| 961 |
+
<li class="right" >
|
| 962 |
+
<a href="othergui.html" title="Other Graphical User Interface Packages"
|
| 963 |
+
>next</a> |</li>
|
| 964 |
+
<li class="right" >
|
| 965 |
+
<a href="tkinter.tix.html" title="tkinter.tix — Extension widgets for Tk"
|
| 966 |
+
>previous</a> |</li>
|
| 967 |
+
|
| 968 |
+
<li><img src="../_static/py.png" alt=""
|
| 969 |
+
style="vertical-align: middle; margin-top: -1px"/></li>
|
| 970 |
+
<li><a href="https://www.python.org/">Python</a> »</li>
|
| 971 |
+
|
| 972 |
+
|
| 973 |
+
<li id="cpython-language-and-version">
|
| 974 |
+
<a href="../index.html">3.10.0a6 Documentation</a> »
|
| 975 |
+
</li>
|
| 976 |
+
|
| 977 |
+
<li class="nav-item nav-item-1"><a href="index.html" >The Python Standard Library</a> »</li>
|
| 978 |
+
<li class="nav-item nav-item-2"><a href="tk.html" >Graphical User Interfaces with Tk</a> »</li>
|
| 979 |
+
<li class="nav-item nav-item-this"><a href="">IDLE</a></li>
|
| 980 |
+
<li class="right">
|
| 981 |
+
|
| 982 |
+
|
| 983 |
+
<div class="inline-search" style="display: none" role="search">
|
| 984 |
+
<form class="inline-search" action="../search.html" method="get">
|
| 985 |
+
<input placeholder="Quick search" type="text" name="q" />
|
| 986 |
+
<input type="submit" value="Go" />
|
| 987 |
+
<input type="hidden" name="check_keywords" value="yes" />
|
| 988 |
+
<input type="hidden" name="area" value="default" />
|
| 989 |
+
</form>
|
| 990 |
+
</div>
|
| 991 |
+
<script type="text/javascript">$('.inline-search').show(0);</script>
|
| 992 |
+
|
|
| 993 |
+
</li>
|
| 994 |
+
|
| 995 |
+
</ul>
|
| 996 |
+
</div>
|
| 997 |
+
<div class="footer">
|
| 998 |
+
© <a href="../copyright.html">Copyright</a> 2001-2021, Python Software Foundation.
|
| 999 |
+
<br />
|
| 1000 |
+
|
| 1001 |
+
The Python Software Foundation is a non-profit corporation.
|
| 1002 |
+
<a href="https://www.python.org/psf/donations/">Please donate.</a>
|
| 1003 |
+
<br />
|
| 1004 |
+
<br />
|
| 1005 |
+
|
| 1006 |
+
Last updated on Mar 29, 2021.
|
| 1007 |
+
<a href="https://docs.python.org/3/bugs.html">Found a bug</a>?
|
| 1008 |
+
<br />
|
| 1009 |
+
|
| 1010 |
+
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
|
| 1011 |
+
</div>
|
| 1012 |
+
|
| 1013 |
+
</body>
|
| 1014 |
+
</html>
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/help.py
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
""" help.py: Implement the Idle help menu.
|
| 2 |
+
Contents are subject to revision at any time, without notice.
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
Help => About IDLE: display About Idle dialog
|
| 6 |
+
|
| 7 |
+
<to be moved here from help_about.py>
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
Help => IDLE Help: Display help.html with proper formatting.
|
| 11 |
+
Doc/library/idle.rst (Sphinx)=> Doc/build/html/library/idle.html
|
| 12 |
+
(help.copy_strip)=> Lib/idlelib/help.html
|
| 13 |
+
|
| 14 |
+
HelpParser - Parse help.html and render to tk Text.
|
| 15 |
+
|
| 16 |
+
HelpText - Display formatted help.html.
|
| 17 |
+
|
| 18 |
+
HelpFrame - Contain text, scrollbar, and table-of-contents.
|
| 19 |
+
(This will be needed for display in a future tabbed window.)
|
| 20 |
+
|
| 21 |
+
HelpWindow - Display HelpFrame in a standalone window.
|
| 22 |
+
|
| 23 |
+
copy_strip - Copy idle.html to help.html, rstripping each line.
|
| 24 |
+
|
| 25 |
+
show_idlehelp - Create HelpWindow. Called in EditorWindow.help_dialog.
|
| 26 |
+
"""
|
| 27 |
+
from html.parser import HTMLParser
|
| 28 |
+
from os.path import abspath, dirname, isfile, join
|
| 29 |
+
from platform import python_version
|
| 30 |
+
|
| 31 |
+
from tkinter import Toplevel, Text, Menu
|
| 32 |
+
from tkinter.ttk import Frame, Menubutton, Scrollbar, Style
|
| 33 |
+
from tkinter import font as tkfont
|
| 34 |
+
|
| 35 |
+
from idlelib.config import idleConf
|
| 36 |
+
|
| 37 |
+
## About IDLE ##
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
## IDLE Help ##
|
| 41 |
+
|
| 42 |
+
class HelpParser(HTMLParser):
|
| 43 |
+
"""Render help.html into a text widget.
|
| 44 |
+
|
| 45 |
+
The overridden handle_xyz methods handle a subset of html tags.
|
| 46 |
+
The supplied text should have the needed tag configurations.
|
| 47 |
+
The behavior for unsupported tags, such as table, is undefined.
|
| 48 |
+
If the tags generated by Sphinx change, this class, especially
|
| 49 |
+
the handle_starttag and handle_endtags methods, might have to also.
|
| 50 |
+
"""
|
| 51 |
+
def __init__(self, text):
|
| 52 |
+
HTMLParser.__init__(self, convert_charrefs=True)
|
| 53 |
+
self.text = text # Text widget we're rendering into.
|
| 54 |
+
self.tags = '' # Current block level text tags to apply.
|
| 55 |
+
self.chartags = '' # Current character level text tags.
|
| 56 |
+
self.show = False # Exclude html page navigation.
|
| 57 |
+
self.hdrlink = False # Exclude html header links.
|
| 58 |
+
self.level = 0 # Track indentation level.
|
| 59 |
+
self.pre = False # Displaying preformatted text?
|
| 60 |
+
self.hprefix = '' # Heading prefix (like '25.5'?) to remove.
|
| 61 |
+
self.nested_dl = False # In a nested <dl>?
|
| 62 |
+
self.simplelist = False # In a simple list (no double spacing)?
|
| 63 |
+
self.toc = [] # Pair headers with text indexes for toc.
|
| 64 |
+
self.header = '' # Text within header tags for toc.
|
| 65 |
+
self.prevtag = None # Previous tag info (opener?, tag).
|
| 66 |
+
|
| 67 |
+
def indent(self, amt=1):
|
| 68 |
+
"Change indent (+1, 0, -1) and tags."
|
| 69 |
+
self.level += amt
|
| 70 |
+
self.tags = '' if self.level == 0 else 'l'+str(self.level)
|
| 71 |
+
|
| 72 |
+
def handle_starttag(self, tag, attrs):
|
| 73 |
+
"Handle starttags in help.html."
|
| 74 |
+
class_ = ''
|
| 75 |
+
for a, v in attrs:
|
| 76 |
+
if a == 'class':
|
| 77 |
+
class_ = v
|
| 78 |
+
s = ''
|
| 79 |
+
if tag == 'div' and class_ == 'section':
|
| 80 |
+
self.show = True # Start main content.
|
| 81 |
+
elif tag == 'div' and class_ == 'sphinxsidebar':
|
| 82 |
+
self.show = False # End main content.
|
| 83 |
+
elif tag == 'p' and self.prevtag and not self.prevtag[0]:
|
| 84 |
+
# Begin a new block for <p> tags after a closed tag.
|
| 85 |
+
# Avoid extra lines, e.g. after <pre> tags.
|
| 86 |
+
lastline = self.text.get('end-1c linestart', 'end-1c')
|
| 87 |
+
s = '\n\n' if lastline and not lastline.isspace() else '\n'
|
| 88 |
+
elif tag == 'span' and class_ == 'pre':
|
| 89 |
+
self.chartags = 'pre'
|
| 90 |
+
elif tag == 'span' and class_ == 'versionmodified':
|
| 91 |
+
self.chartags = 'em'
|
| 92 |
+
elif tag == 'em':
|
| 93 |
+
self.chartags = 'em'
|
| 94 |
+
elif tag in ['ul', 'ol']:
|
| 95 |
+
if class_.find('simple') != -1:
|
| 96 |
+
s = '\n'
|
| 97 |
+
self.simplelist = True
|
| 98 |
+
else:
|
| 99 |
+
self.simplelist = False
|
| 100 |
+
self.indent()
|
| 101 |
+
elif tag == 'dl':
|
| 102 |
+
if self.level > 0:
|
| 103 |
+
self.nested_dl = True
|
| 104 |
+
elif tag == 'li':
|
| 105 |
+
s = '\n* ' if self.simplelist else '\n\n* '
|
| 106 |
+
elif tag == 'dt':
|
| 107 |
+
s = '\n\n' if not self.nested_dl else '\n' # Avoid extra line.
|
| 108 |
+
self.nested_dl = False
|
| 109 |
+
elif tag == 'dd':
|
| 110 |
+
self.indent()
|
| 111 |
+
s = '\n'
|
| 112 |
+
elif tag == 'pre':
|
| 113 |
+
self.pre = True
|
| 114 |
+
if self.show:
|
| 115 |
+
self.text.insert('end', '\n\n')
|
| 116 |
+
self.tags = 'preblock'
|
| 117 |
+
elif tag == 'a' and class_ == 'headerlink':
|
| 118 |
+
self.hdrlink = True
|
| 119 |
+
elif tag == 'h1':
|
| 120 |
+
self.tags = tag
|
| 121 |
+
elif tag in ['h2', 'h3']:
|
| 122 |
+
if self.show:
|
| 123 |
+
self.header = ''
|
| 124 |
+
self.text.insert('end', '\n\n')
|
| 125 |
+
self.tags = tag
|
| 126 |
+
if self.show:
|
| 127 |
+
self.text.insert('end', s, (self.tags, self.chartags))
|
| 128 |
+
self.prevtag = (True, tag)
|
| 129 |
+
|
| 130 |
+
def handle_endtag(self, tag):
|
| 131 |
+
"Handle endtags in help.html."
|
| 132 |
+
if tag in ['h1', 'h2', 'h3']:
|
| 133 |
+
assert self.level == 0
|
| 134 |
+
if self.show:
|
| 135 |
+
indent = (' ' if tag == 'h3' else
|
| 136 |
+
' ' if tag == 'h2' else
|
| 137 |
+
'')
|
| 138 |
+
self.toc.append((indent+self.header, self.text.index('insert')))
|
| 139 |
+
self.tags = ''
|
| 140 |
+
elif tag in ['span', 'em']:
|
| 141 |
+
self.chartags = ''
|
| 142 |
+
elif tag == 'a':
|
| 143 |
+
self.hdrlink = False
|
| 144 |
+
elif tag == 'pre':
|
| 145 |
+
self.pre = False
|
| 146 |
+
self.tags = ''
|
| 147 |
+
elif tag in ['ul', 'dd', 'ol']:
|
| 148 |
+
self.indent(-1)
|
| 149 |
+
self.prevtag = (False, tag)
|
| 150 |
+
|
| 151 |
+
def handle_data(self, data):
|
| 152 |
+
"Handle date segments in help.html."
|
| 153 |
+
if self.show and not self.hdrlink:
|
| 154 |
+
d = data if self.pre else data.replace('\n', ' ')
|
| 155 |
+
if self.tags == 'h1':
|
| 156 |
+
try:
|
| 157 |
+
self.hprefix = d[0:d.index(' ')]
|
| 158 |
+
except ValueError:
|
| 159 |
+
self.hprefix = ''
|
| 160 |
+
if self.tags in ['h1', 'h2', 'h3']:
|
| 161 |
+
if (self.hprefix != '' and
|
| 162 |
+
d[0:len(self.hprefix)] == self.hprefix):
|
| 163 |
+
d = d[len(self.hprefix):]
|
| 164 |
+
self.header += d.strip()
|
| 165 |
+
self.text.insert('end', d, (self.tags, self.chartags))
|
| 166 |
+
|
| 167 |
+
|
| 168 |
+
class HelpText(Text):
|
| 169 |
+
"Display help.html."
|
| 170 |
+
def __init__(self, parent, filename):
|
| 171 |
+
"Configure tags and feed file to parser."
|
| 172 |
+
uwide = idleConf.GetOption('main', 'EditorWindow', 'width', type='int')
|
| 173 |
+
uhigh = idleConf.GetOption('main', 'EditorWindow', 'height', type='int')
|
| 174 |
+
uhigh = 3 * uhigh // 4 # Lines average 4/3 of editor line height.
|
| 175 |
+
Text.__init__(self, parent, wrap='word', highlightthickness=0,
|
| 176 |
+
padx=5, borderwidth=0, width=uwide, height=uhigh)
|
| 177 |
+
|
| 178 |
+
normalfont = self.findfont(['TkDefaultFont', 'arial', 'helvetica'])
|
| 179 |
+
fixedfont = self.findfont(['TkFixedFont', 'monaco', 'courier'])
|
| 180 |
+
self['font'] = (normalfont, 12)
|
| 181 |
+
self.tag_configure('em', font=(normalfont, 12, 'italic'))
|
| 182 |
+
self.tag_configure('h1', font=(normalfont, 20, 'bold'))
|
| 183 |
+
self.tag_configure('h2', font=(normalfont, 18, 'bold'))
|
| 184 |
+
self.tag_configure('h3', font=(normalfont, 15, 'bold'))
|
| 185 |
+
self.tag_configure('pre', font=(fixedfont, 12), background='#f6f6ff')
|
| 186 |
+
self.tag_configure('preblock', font=(fixedfont, 10), lmargin1=25,
|
| 187 |
+
borderwidth=1, relief='solid', background='#eeffcc')
|
| 188 |
+
self.tag_configure('l1', lmargin1=25, lmargin2=25)
|
| 189 |
+
self.tag_configure('l2', lmargin1=50, lmargin2=50)
|
| 190 |
+
self.tag_configure('l3', lmargin1=75, lmargin2=75)
|
| 191 |
+
self.tag_configure('l4', lmargin1=100, lmargin2=100)
|
| 192 |
+
|
| 193 |
+
self.parser = HelpParser(self)
|
| 194 |
+
with open(filename, encoding='utf-8') as f:
|
| 195 |
+
contents = f.read()
|
| 196 |
+
self.parser.feed(contents)
|
| 197 |
+
self['state'] = 'disabled'
|
| 198 |
+
|
| 199 |
+
def findfont(self, names):
|
| 200 |
+
"Return name of first font family derived from names."
|
| 201 |
+
for name in names:
|
| 202 |
+
if name.lower() in (x.lower() for x in tkfont.names(root=self)):
|
| 203 |
+
font = tkfont.Font(name=name, exists=True, root=self)
|
| 204 |
+
return font.actual()['family']
|
| 205 |
+
elif name.lower() in (x.lower()
|
| 206 |
+
for x in tkfont.families(root=self)):
|
| 207 |
+
return name
|
| 208 |
+
|
| 209 |
+
|
| 210 |
+
class HelpFrame(Frame):
|
| 211 |
+
"Display html text, scrollbar, and toc."
|
| 212 |
+
def __init__(self, parent, filename):
|
| 213 |
+
Frame.__init__(self, parent)
|
| 214 |
+
self.text = text = HelpText(self, filename)
|
| 215 |
+
self.style = Style(parent)
|
| 216 |
+
self['style'] = 'helpframe.TFrame'
|
| 217 |
+
self.style.configure('helpframe.TFrame', background=text['background'])
|
| 218 |
+
self.toc = toc = self.toc_menu(text)
|
| 219 |
+
self.scroll = scroll = Scrollbar(self, command=text.yview)
|
| 220 |
+
text['yscrollcommand'] = scroll.set
|
| 221 |
+
|
| 222 |
+
self.rowconfigure(0, weight=1)
|
| 223 |
+
self.columnconfigure(1, weight=1) # Only expand the text widget.
|
| 224 |
+
toc.grid(row=0, column=0, sticky='nw')
|
| 225 |
+
text.grid(row=0, column=1, sticky='nsew')
|
| 226 |
+
scroll.grid(row=0, column=2, sticky='ns')
|
| 227 |
+
|
| 228 |
+
def toc_menu(self, text):
|
| 229 |
+
"Create table of contents as drop-down menu."
|
| 230 |
+
toc = Menubutton(self, text='TOC')
|
| 231 |
+
drop = Menu(toc, tearoff=False)
|
| 232 |
+
for lbl, dex in text.parser.toc:
|
| 233 |
+
drop.add_command(label=lbl, command=lambda dex=dex:text.yview(dex))
|
| 234 |
+
toc['menu'] = drop
|
| 235 |
+
return toc
|
| 236 |
+
|
| 237 |
+
|
| 238 |
+
class HelpWindow(Toplevel):
|
| 239 |
+
"Display frame with rendered html."
|
| 240 |
+
def __init__(self, parent, filename, title):
|
| 241 |
+
Toplevel.__init__(self, parent)
|
| 242 |
+
self.wm_title(title)
|
| 243 |
+
self.protocol("WM_DELETE_WINDOW", self.destroy)
|
| 244 |
+
HelpFrame(self, filename).grid(column=0, row=0, sticky='nsew')
|
| 245 |
+
self.grid_columnconfigure(0, weight=1)
|
| 246 |
+
self.grid_rowconfigure(0, weight=1)
|
| 247 |
+
|
| 248 |
+
|
| 249 |
+
def copy_strip():
|
| 250 |
+
"""Copy idle.html to idlelib/help.html, stripping trailing whitespace.
|
| 251 |
+
|
| 252 |
+
Files with trailing whitespace cannot be pushed to the git cpython
|
| 253 |
+
repository. For 3.x (on Windows), help.html is generated, after
|
| 254 |
+
editing idle.rst on the master branch, with
|
| 255 |
+
sphinx-build -bhtml . build/html
|
| 256 |
+
python_d.exe -c "from idlelib.help import copy_strip; copy_strip()"
|
| 257 |
+
Check build/html/library/idle.html, the help.html diff, and the text
|
| 258 |
+
displayed by Help => IDLE Help. Add a blurb and create a PR.
|
| 259 |
+
|
| 260 |
+
It can be worthwhile to occasionally generate help.html without
|
| 261 |
+
touching idle.rst. Changes to the master version and to the doc
|
| 262 |
+
build system may result in changes that should not changed
|
| 263 |
+
the displayed text, but might break HelpParser.
|
| 264 |
+
|
| 265 |
+
As long as master and maintenance versions of idle.rst remain the
|
| 266 |
+
same, help.html can be backported. The internal Python version
|
| 267 |
+
number is not displayed. If maintenance idle.rst diverges from
|
| 268 |
+
the master version, then instead of backporting help.html from
|
| 269 |
+
master, repeat the procedure above to generate a maintenance
|
| 270 |
+
version.
|
| 271 |
+
"""
|
| 272 |
+
src = join(abspath(dirname(dirname(dirname(__file__)))),
|
| 273 |
+
'Doc', 'build', 'html', 'library', 'idle.html')
|
| 274 |
+
dst = join(abspath(dirname(__file__)), 'help.html')
|
| 275 |
+
with open(src, 'rb') as inn,\
|
| 276 |
+
open(dst, 'wb') as out:
|
| 277 |
+
for line in inn:
|
| 278 |
+
out.write(line.rstrip() + b'\n')
|
| 279 |
+
print(f'{src} copied to {dst}')
|
| 280 |
+
|
| 281 |
+
def show_idlehelp(parent):
|
| 282 |
+
"Create HelpWindow; called from Idle Help event handler."
|
| 283 |
+
filename = join(abspath(dirname(__file__)), 'help.html')
|
| 284 |
+
if not isfile(filename):
|
| 285 |
+
# Try copy_strip, present message.
|
| 286 |
+
return
|
| 287 |
+
HelpWindow(parent, filename, 'IDLE Help (%s)' % python_version())
|
| 288 |
+
|
| 289 |
+
if __name__ == '__main__':
|
| 290 |
+
from unittest import main
|
| 291 |
+
main('idlelib.idle_test.test_help', verbosity=2, exit=False)
|
| 292 |
+
|
| 293 |
+
from idlelib.idle_test.htest import run
|
| 294 |
+
run(show_idlehelp)
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/help_about.py
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""About Dialog for IDLE
|
| 2 |
+
|
| 3 |
+
"""
|
| 4 |
+
import os
|
| 5 |
+
import sys
|
| 6 |
+
from platform import python_version, architecture
|
| 7 |
+
|
| 8 |
+
from tkinter import Toplevel, Frame, Label, Button, PhotoImage
|
| 9 |
+
from tkinter import SUNKEN, TOP, BOTTOM, LEFT, X, BOTH, W, EW, NSEW, E
|
| 10 |
+
|
| 11 |
+
from idlelib import textview
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def build_bits():
|
| 15 |
+
"Return bits for platform."
|
| 16 |
+
if sys.platform == 'darwin':
|
| 17 |
+
return '64' if sys.maxsize > 2**32 else '32'
|
| 18 |
+
else:
|
| 19 |
+
return architecture()[0][:2]
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
class AboutDialog(Toplevel):
|
| 23 |
+
"""Modal about dialog for idle
|
| 24 |
+
|
| 25 |
+
"""
|
| 26 |
+
def __init__(self, parent, title=None, *, _htest=False, _utest=False):
|
| 27 |
+
"""Create popup, do not return until tk widget destroyed.
|
| 28 |
+
|
| 29 |
+
parent - parent of this dialog
|
| 30 |
+
title - string which is title of popup dialog
|
| 31 |
+
_htest - bool, change box location when running htest
|
| 32 |
+
_utest - bool, don't wait_window when running unittest
|
| 33 |
+
"""
|
| 34 |
+
Toplevel.__init__(self, parent)
|
| 35 |
+
self.configure(borderwidth=5)
|
| 36 |
+
# place dialog below parent if running htest
|
| 37 |
+
self.geometry("+%d+%d" % (
|
| 38 |
+
parent.winfo_rootx()+30,
|
| 39 |
+
parent.winfo_rooty()+(30 if not _htest else 100)))
|
| 40 |
+
self.bg = "#bbbbbb"
|
| 41 |
+
self.fg = "#000000"
|
| 42 |
+
self.create_widgets()
|
| 43 |
+
self.resizable(height=False, width=False)
|
| 44 |
+
self.title(title or
|
| 45 |
+
f'About IDLE {python_version()} ({build_bits()} bit)')
|
| 46 |
+
self.transient(parent)
|
| 47 |
+
self.grab_set()
|
| 48 |
+
self.protocol("WM_DELETE_WINDOW", self.ok)
|
| 49 |
+
self.parent = parent
|
| 50 |
+
self.button_ok.focus_set()
|
| 51 |
+
self.bind('<Return>', self.ok) # dismiss dialog
|
| 52 |
+
self.bind('<Escape>', self.ok) # dismiss dialog
|
| 53 |
+
self._current_textview = None
|
| 54 |
+
self._utest = _utest
|
| 55 |
+
|
| 56 |
+
if not _utest:
|
| 57 |
+
self.deiconify()
|
| 58 |
+
self.wait_window()
|
| 59 |
+
|
| 60 |
+
def create_widgets(self):
|
| 61 |
+
frame = Frame(self, borderwidth=2, relief=SUNKEN)
|
| 62 |
+
frame_buttons = Frame(self)
|
| 63 |
+
frame_buttons.pack(side=BOTTOM, fill=X)
|
| 64 |
+
frame.pack(side=TOP, expand=True, fill=BOTH)
|
| 65 |
+
self.button_ok = Button(frame_buttons, text='Close',
|
| 66 |
+
command=self.ok)
|
| 67 |
+
self.button_ok.pack(padx=5, pady=5)
|
| 68 |
+
|
| 69 |
+
frame_background = Frame(frame, bg=self.bg)
|
| 70 |
+
frame_background.pack(expand=True, fill=BOTH)
|
| 71 |
+
|
| 72 |
+
header = Label(frame_background, text='IDLE', fg=self.fg,
|
| 73 |
+
bg=self.bg, font=('courier', 24, 'bold'))
|
| 74 |
+
header.grid(row=0, column=0, sticky=E, padx=10, pady=10)
|
| 75 |
+
|
| 76 |
+
tk_patchlevel = self.tk.call('info', 'patchlevel')
|
| 77 |
+
ext = '.png' if tk_patchlevel >= '8.6' else '.gif'
|
| 78 |
+
icon = os.path.join(os.path.abspath(os.path.dirname(__file__)),
|
| 79 |
+
'Icons', f'idle_48{ext}')
|
| 80 |
+
self.icon_image = PhotoImage(master=self._root(), file=icon)
|
| 81 |
+
logo = Label(frame_background, image=self.icon_image, bg=self.bg)
|
| 82 |
+
logo.grid(row=0, column=0, sticky=W, rowspan=2, padx=10, pady=10)
|
| 83 |
+
|
| 84 |
+
byline_text = "Python's Integrated Development\nand Learning Environment" + 5*'\n'
|
| 85 |
+
byline = Label(frame_background, text=byline_text, justify=LEFT,
|
| 86 |
+
fg=self.fg, bg=self.bg)
|
| 87 |
+
byline.grid(row=2, column=0, sticky=W, columnspan=3, padx=10, pady=5)
|
| 88 |
+
email = Label(frame_background, text='email: idle-dev@python.org',
|
| 89 |
+
justify=LEFT, fg=self.fg, bg=self.bg)
|
| 90 |
+
email.grid(row=6, column=0, columnspan=2, sticky=W, padx=10, pady=0)
|
| 91 |
+
docs = Label(frame_background, text='https://docs.python.org/' +
|
| 92 |
+
python_version()[:3] + '/library/idle.html',
|
| 93 |
+
justify=LEFT, fg=self.fg, bg=self.bg)
|
| 94 |
+
docs.grid(row=7, column=0, columnspan=2, sticky=W, padx=10, pady=0)
|
| 95 |
+
|
| 96 |
+
Frame(frame_background, borderwidth=1, relief=SUNKEN,
|
| 97 |
+
height=2, bg=self.bg).grid(row=8, column=0, sticky=EW,
|
| 98 |
+
columnspan=3, padx=5, pady=5)
|
| 99 |
+
|
| 100 |
+
pyver = Label(frame_background,
|
| 101 |
+
text='Python version: ' + python_version(),
|
| 102 |
+
fg=self.fg, bg=self.bg)
|
| 103 |
+
pyver.grid(row=9, column=0, sticky=W, padx=10, pady=0)
|
| 104 |
+
tkver = Label(frame_background, text='Tk version: ' + tk_patchlevel,
|
| 105 |
+
fg=self.fg, bg=self.bg)
|
| 106 |
+
tkver.grid(row=9, column=1, sticky=W, padx=2, pady=0)
|
| 107 |
+
py_buttons = Frame(frame_background, bg=self.bg)
|
| 108 |
+
py_buttons.grid(row=10, column=0, columnspan=2, sticky=NSEW)
|
| 109 |
+
self.py_license = Button(py_buttons, text='License', width=8,
|
| 110 |
+
highlightbackground=self.bg,
|
| 111 |
+
command=self.show_py_license)
|
| 112 |
+
self.py_license.pack(side=LEFT, padx=10, pady=10)
|
| 113 |
+
self.py_copyright = Button(py_buttons, text='Copyright', width=8,
|
| 114 |
+
highlightbackground=self.bg,
|
| 115 |
+
command=self.show_py_copyright)
|
| 116 |
+
self.py_copyright.pack(side=LEFT, padx=10, pady=10)
|
| 117 |
+
self.py_credits = Button(py_buttons, text='Credits', width=8,
|
| 118 |
+
highlightbackground=self.bg,
|
| 119 |
+
command=self.show_py_credits)
|
| 120 |
+
self.py_credits.pack(side=LEFT, padx=10, pady=10)
|
| 121 |
+
|
| 122 |
+
Frame(frame_background, borderwidth=1, relief=SUNKEN,
|
| 123 |
+
height=2, bg=self.bg).grid(row=11, column=0, sticky=EW,
|
| 124 |
+
columnspan=3, padx=5, pady=5)
|
| 125 |
+
|
| 126 |
+
idlever = Label(frame_background,
|
| 127 |
+
text='IDLE version: ' + python_version(),
|
| 128 |
+
fg=self.fg, bg=self.bg)
|
| 129 |
+
idlever.grid(row=12, column=0, sticky=W, padx=10, pady=0)
|
| 130 |
+
idle_buttons = Frame(frame_background, bg=self.bg)
|
| 131 |
+
idle_buttons.grid(row=13, column=0, columnspan=3, sticky=NSEW)
|
| 132 |
+
self.readme = Button(idle_buttons, text='README', width=8,
|
| 133 |
+
highlightbackground=self.bg,
|
| 134 |
+
command=self.show_readme)
|
| 135 |
+
self.readme.pack(side=LEFT, padx=10, pady=10)
|
| 136 |
+
self.idle_news = Button(idle_buttons, text='NEWS', width=8,
|
| 137 |
+
highlightbackground=self.bg,
|
| 138 |
+
command=self.show_idle_news)
|
| 139 |
+
self.idle_news.pack(side=LEFT, padx=10, pady=10)
|
| 140 |
+
self.idle_credits = Button(idle_buttons, text='Credits', width=8,
|
| 141 |
+
highlightbackground=self.bg,
|
| 142 |
+
command=self.show_idle_credits)
|
| 143 |
+
self.idle_credits.pack(side=LEFT, padx=10, pady=10)
|
| 144 |
+
|
| 145 |
+
# License, copyright, and credits are of type _sitebuiltins._Printer
|
| 146 |
+
def show_py_license(self):
|
| 147 |
+
"Handle License button event."
|
| 148 |
+
self.display_printer_text('About - License', license)
|
| 149 |
+
|
| 150 |
+
def show_py_copyright(self):
|
| 151 |
+
"Handle Copyright button event."
|
| 152 |
+
self.display_printer_text('About - Copyright', copyright)
|
| 153 |
+
|
| 154 |
+
def show_py_credits(self):
|
| 155 |
+
"Handle Python Credits button event."
|
| 156 |
+
self.display_printer_text('About - Python Credits', credits)
|
| 157 |
+
|
| 158 |
+
# Encode CREDITS.txt to utf-8 for proper version of Loewis.
|
| 159 |
+
# Specify others as ascii until need utf-8, so catch errors.
|
| 160 |
+
def show_idle_credits(self):
|
| 161 |
+
"Handle Idle Credits button event."
|
| 162 |
+
self.display_file_text('About - Credits', 'CREDITS.txt', 'utf-8')
|
| 163 |
+
|
| 164 |
+
def show_readme(self):
|
| 165 |
+
"Handle Readme button event."
|
| 166 |
+
self.display_file_text('About - Readme', 'README.txt', 'ascii')
|
| 167 |
+
|
| 168 |
+
def show_idle_news(self):
|
| 169 |
+
"Handle News button event."
|
| 170 |
+
self.display_file_text('About - NEWS', 'NEWS.txt', 'utf-8')
|
| 171 |
+
|
| 172 |
+
def display_printer_text(self, title, printer):
|
| 173 |
+
"""Create textview for built-in constants.
|
| 174 |
+
|
| 175 |
+
Built-in constants have type _sitebuiltins._Printer. The
|
| 176 |
+
text is extracted from the built-in and then sent to a text
|
| 177 |
+
viewer with self as the parent and title as the title of
|
| 178 |
+
the popup.
|
| 179 |
+
"""
|
| 180 |
+
printer._Printer__setup()
|
| 181 |
+
text = '\n'.join(printer._Printer__lines)
|
| 182 |
+
self._current_textview = textview.view_text(
|
| 183 |
+
self, title, text, _utest=self._utest)
|
| 184 |
+
|
| 185 |
+
def display_file_text(self, title, filename, encoding=None):
|
| 186 |
+
"""Create textview for filename.
|
| 187 |
+
|
| 188 |
+
The filename needs to be in the current directory. The path
|
| 189 |
+
is sent to a text viewer with self as the parent, title as
|
| 190 |
+
the title of the popup, and the file encoding.
|
| 191 |
+
"""
|
| 192 |
+
fn = os.path.join(os.path.abspath(os.path.dirname(__file__)), filename)
|
| 193 |
+
self._current_textview = textview.view_file(
|
| 194 |
+
self, title, fn, encoding, _utest=self._utest)
|
| 195 |
+
|
| 196 |
+
def ok(self, event=None):
|
| 197 |
+
"Dismiss help_about dialog."
|
| 198 |
+
self.grab_release()
|
| 199 |
+
self.destroy()
|
| 200 |
+
|
| 201 |
+
|
| 202 |
+
if __name__ == '__main__':
|
| 203 |
+
from unittest import main
|
| 204 |
+
main('idlelib.idle_test.test_help_about', verbosity=2, exit=False)
|
| 205 |
+
|
| 206 |
+
from idlelib.idle_test.htest import run
|
| 207 |
+
run(AboutDialog)
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/history.py
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"Implement Idle Shell history mechanism with History class"
|
| 2 |
+
|
| 3 |
+
from idlelib.config import idleConf
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
class History:
|
| 7 |
+
''' Implement Idle Shell history mechanism.
|
| 8 |
+
|
| 9 |
+
store - Store source statement (called from pyshell.resetoutput).
|
| 10 |
+
fetch - Fetch stored statement matching prefix already entered.
|
| 11 |
+
history_next - Bound to <<history-next>> event (default Alt-N).
|
| 12 |
+
history_prev - Bound to <<history-prev>> event (default Alt-P).
|
| 13 |
+
'''
|
| 14 |
+
def __init__(self, text):
|
| 15 |
+
'''Initialize data attributes and bind event methods.
|
| 16 |
+
|
| 17 |
+
.text - Idle wrapper of tk Text widget, with .bell().
|
| 18 |
+
.history - source statements, possibly with multiple lines.
|
| 19 |
+
.prefix - source already entered at prompt; filters history list.
|
| 20 |
+
.pointer - index into history.
|
| 21 |
+
.cyclic - wrap around history list (or not).
|
| 22 |
+
'''
|
| 23 |
+
self.text = text
|
| 24 |
+
self.history = []
|
| 25 |
+
self.prefix = None
|
| 26 |
+
self.pointer = None
|
| 27 |
+
self.cyclic = idleConf.GetOption("main", "History", "cyclic", 1, "bool")
|
| 28 |
+
text.bind("<<history-previous>>", self.history_prev)
|
| 29 |
+
text.bind("<<history-next>>", self.history_next)
|
| 30 |
+
|
| 31 |
+
def history_next(self, event):
|
| 32 |
+
"Fetch later statement; start with ealiest if cyclic."
|
| 33 |
+
self.fetch(reverse=False)
|
| 34 |
+
return "break"
|
| 35 |
+
|
| 36 |
+
def history_prev(self, event):
|
| 37 |
+
"Fetch earlier statement; start with most recent."
|
| 38 |
+
self.fetch(reverse=True)
|
| 39 |
+
return "break"
|
| 40 |
+
|
| 41 |
+
def fetch(self, reverse):
|
| 42 |
+
'''Fetch statement and replace current line in text widget.
|
| 43 |
+
|
| 44 |
+
Set prefix and pointer as needed for successive fetches.
|
| 45 |
+
Reset them to None, None when returning to the start line.
|
| 46 |
+
Sound bell when return to start line or cannot leave a line
|
| 47 |
+
because cyclic is False.
|
| 48 |
+
'''
|
| 49 |
+
nhist = len(self.history)
|
| 50 |
+
pointer = self.pointer
|
| 51 |
+
prefix = self.prefix
|
| 52 |
+
if pointer is not None and prefix is not None:
|
| 53 |
+
if self.text.compare("insert", "!=", "end-1c") or \
|
| 54 |
+
self.text.get("iomark", "end-1c") != self.history[pointer]:
|
| 55 |
+
pointer = prefix = None
|
| 56 |
+
self.text.mark_set("insert", "end-1c") # != after cursor move
|
| 57 |
+
if pointer is None or prefix is None:
|
| 58 |
+
prefix = self.text.get("iomark", "end-1c")
|
| 59 |
+
if reverse:
|
| 60 |
+
pointer = nhist # will be decremented
|
| 61 |
+
else:
|
| 62 |
+
if self.cyclic:
|
| 63 |
+
pointer = -1 # will be incremented
|
| 64 |
+
else: # abort history_next
|
| 65 |
+
self.text.bell()
|
| 66 |
+
return
|
| 67 |
+
nprefix = len(prefix)
|
| 68 |
+
while 1:
|
| 69 |
+
pointer += -1 if reverse else 1
|
| 70 |
+
if pointer < 0 or pointer >= nhist:
|
| 71 |
+
self.text.bell()
|
| 72 |
+
if not self.cyclic and pointer < 0: # abort history_prev
|
| 73 |
+
return
|
| 74 |
+
else:
|
| 75 |
+
if self.text.get("iomark", "end-1c") != prefix:
|
| 76 |
+
self.text.delete("iomark", "end-1c")
|
| 77 |
+
self.text.insert("iomark", prefix)
|
| 78 |
+
pointer = prefix = None
|
| 79 |
+
break
|
| 80 |
+
item = self.history[pointer]
|
| 81 |
+
if item[:nprefix] == prefix and len(item) > nprefix:
|
| 82 |
+
self.text.delete("iomark", "end-1c")
|
| 83 |
+
self.text.insert("iomark", item)
|
| 84 |
+
break
|
| 85 |
+
self.text.see("insert")
|
| 86 |
+
self.text.tag_remove("sel", "1.0", "end")
|
| 87 |
+
self.pointer = pointer
|
| 88 |
+
self.prefix = prefix
|
| 89 |
+
|
| 90 |
+
def store(self, source):
|
| 91 |
+
"Store Shell input statement into history list."
|
| 92 |
+
source = source.strip()
|
| 93 |
+
if len(source) > 2:
|
| 94 |
+
# avoid duplicates
|
| 95 |
+
try:
|
| 96 |
+
self.history.remove(source)
|
| 97 |
+
except ValueError:
|
| 98 |
+
pass
|
| 99 |
+
self.history.append(source)
|
| 100 |
+
self.pointer = None
|
| 101 |
+
self.prefix = None
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
if __name__ == "__main__":
|
| 105 |
+
from unittest import main
|
| 106 |
+
main('idlelib.idle_test.test_history', verbosity=2, exit=False)
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/hyperparser.py
ADDED
|
@@ -0,0 +1,312 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Provide advanced parsing abilities for ParenMatch and other extensions.
|
| 2 |
+
|
| 3 |
+
HyperParser uses PyParser. PyParser mostly gives information on the
|
| 4 |
+
proper indentation of code. HyperParser gives additional information on
|
| 5 |
+
the structure of code.
|
| 6 |
+
"""
|
| 7 |
+
from keyword import iskeyword
|
| 8 |
+
import string
|
| 9 |
+
|
| 10 |
+
from idlelib import pyparse
|
| 11 |
+
|
| 12 |
+
# all ASCII chars that may be in an identifier
|
| 13 |
+
_ASCII_ID_CHARS = frozenset(string.ascii_letters + string.digits + "_")
|
| 14 |
+
# all ASCII chars that may be the first char of an identifier
|
| 15 |
+
_ASCII_ID_FIRST_CHARS = frozenset(string.ascii_letters + "_")
|
| 16 |
+
|
| 17 |
+
# lookup table for whether 7-bit ASCII chars are valid in a Python identifier
|
| 18 |
+
_IS_ASCII_ID_CHAR = [(chr(x) in _ASCII_ID_CHARS) for x in range(128)]
|
| 19 |
+
# lookup table for whether 7-bit ASCII chars are valid as the first
|
| 20 |
+
# char in a Python identifier
|
| 21 |
+
_IS_ASCII_ID_FIRST_CHAR = \
|
| 22 |
+
[(chr(x) in _ASCII_ID_FIRST_CHARS) for x in range(128)]
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
class HyperParser:
|
| 26 |
+
def __init__(self, editwin, index):
|
| 27 |
+
"To initialize, analyze the surroundings of the given index."
|
| 28 |
+
|
| 29 |
+
self.editwin = editwin
|
| 30 |
+
self.text = text = editwin.text
|
| 31 |
+
|
| 32 |
+
parser = pyparse.Parser(editwin.indentwidth, editwin.tabwidth)
|
| 33 |
+
|
| 34 |
+
def index2line(index):
|
| 35 |
+
return int(float(index))
|
| 36 |
+
lno = index2line(text.index(index))
|
| 37 |
+
|
| 38 |
+
if not editwin.prompt_last_line:
|
| 39 |
+
for context in editwin.num_context_lines:
|
| 40 |
+
startat = max(lno - context, 1)
|
| 41 |
+
startatindex = repr(startat) + ".0"
|
| 42 |
+
stopatindex = "%d.end" % lno
|
| 43 |
+
# We add the newline because PyParse requires a newline
|
| 44 |
+
# at end. We add a space so that index won't be at end
|
| 45 |
+
# of line, so that its status will be the same as the
|
| 46 |
+
# char before it, if should.
|
| 47 |
+
parser.set_code(text.get(startatindex, stopatindex)+' \n')
|
| 48 |
+
bod = parser.find_good_parse_start(
|
| 49 |
+
editwin._build_char_in_string_func(startatindex))
|
| 50 |
+
if bod is not None or startat == 1:
|
| 51 |
+
break
|
| 52 |
+
parser.set_lo(bod or 0)
|
| 53 |
+
else:
|
| 54 |
+
r = text.tag_prevrange("console", index)
|
| 55 |
+
if r:
|
| 56 |
+
startatindex = r[1]
|
| 57 |
+
else:
|
| 58 |
+
startatindex = "1.0"
|
| 59 |
+
stopatindex = "%d.end" % lno
|
| 60 |
+
# We add the newline because PyParse requires it. We add a
|
| 61 |
+
# space so that index won't be at end of line, so that its
|
| 62 |
+
# status will be the same as the char before it, if should.
|
| 63 |
+
parser.set_code(text.get(startatindex, stopatindex)+' \n')
|
| 64 |
+
parser.set_lo(0)
|
| 65 |
+
|
| 66 |
+
# We want what the parser has, minus the last newline and space.
|
| 67 |
+
self.rawtext = parser.code[:-2]
|
| 68 |
+
# Parser.code apparently preserves the statement we are in, so
|
| 69 |
+
# that stopatindex can be used to synchronize the string with
|
| 70 |
+
# the text box indices.
|
| 71 |
+
self.stopatindex = stopatindex
|
| 72 |
+
self.bracketing = parser.get_last_stmt_bracketing()
|
| 73 |
+
# find which pairs of bracketing are openers. These always
|
| 74 |
+
# correspond to a character of rawtext.
|
| 75 |
+
self.isopener = [i>0 and self.bracketing[i][1] >
|
| 76 |
+
self.bracketing[i-1][1]
|
| 77 |
+
for i in range(len(self.bracketing))]
|
| 78 |
+
|
| 79 |
+
self.set_index(index)
|
| 80 |
+
|
| 81 |
+
def set_index(self, index):
|
| 82 |
+
"""Set the index to which the functions relate.
|
| 83 |
+
|
| 84 |
+
The index must be in the same statement.
|
| 85 |
+
"""
|
| 86 |
+
indexinrawtext = (len(self.rawtext) -
|
| 87 |
+
len(self.text.get(index, self.stopatindex)))
|
| 88 |
+
if indexinrawtext < 0:
|
| 89 |
+
raise ValueError("Index %s precedes the analyzed statement"
|
| 90 |
+
% index)
|
| 91 |
+
self.indexinrawtext = indexinrawtext
|
| 92 |
+
# find the rightmost bracket to which index belongs
|
| 93 |
+
self.indexbracket = 0
|
| 94 |
+
while (self.indexbracket < len(self.bracketing)-1 and
|
| 95 |
+
self.bracketing[self.indexbracket+1][0] < self.indexinrawtext):
|
| 96 |
+
self.indexbracket += 1
|
| 97 |
+
if (self.indexbracket < len(self.bracketing)-1 and
|
| 98 |
+
self.bracketing[self.indexbracket+1][0] == self.indexinrawtext and
|
| 99 |
+
not self.isopener[self.indexbracket+1]):
|
| 100 |
+
self.indexbracket += 1
|
| 101 |
+
|
| 102 |
+
def is_in_string(self):
|
| 103 |
+
"""Is the index given to the HyperParser in a string?"""
|
| 104 |
+
# The bracket to which we belong should be an opener.
|
| 105 |
+
# If it's an opener, it has to have a character.
|
| 106 |
+
return (self.isopener[self.indexbracket] and
|
| 107 |
+
self.rawtext[self.bracketing[self.indexbracket][0]]
|
| 108 |
+
in ('"', "'"))
|
| 109 |
+
|
| 110 |
+
def is_in_code(self):
|
| 111 |
+
"""Is the index given to the HyperParser in normal code?"""
|
| 112 |
+
return (not self.isopener[self.indexbracket] or
|
| 113 |
+
self.rawtext[self.bracketing[self.indexbracket][0]]
|
| 114 |
+
not in ('#', '"', "'"))
|
| 115 |
+
|
| 116 |
+
def get_surrounding_brackets(self, openers='([{', mustclose=False):
|
| 117 |
+
"""Return bracket indexes or None.
|
| 118 |
+
|
| 119 |
+
If the index given to the HyperParser is surrounded by a
|
| 120 |
+
bracket defined in openers (or at least has one before it),
|
| 121 |
+
return the indices of the opening bracket and the closing
|
| 122 |
+
bracket (or the end of line, whichever comes first).
|
| 123 |
+
|
| 124 |
+
If it is not surrounded by brackets, or the end of line comes
|
| 125 |
+
before the closing bracket and mustclose is True, returns None.
|
| 126 |
+
"""
|
| 127 |
+
|
| 128 |
+
bracketinglevel = self.bracketing[self.indexbracket][1]
|
| 129 |
+
before = self.indexbracket
|
| 130 |
+
while (not self.isopener[before] or
|
| 131 |
+
self.rawtext[self.bracketing[before][0]] not in openers or
|
| 132 |
+
self.bracketing[before][1] > bracketinglevel):
|
| 133 |
+
before -= 1
|
| 134 |
+
if before < 0:
|
| 135 |
+
return None
|
| 136 |
+
bracketinglevel = min(bracketinglevel, self.bracketing[before][1])
|
| 137 |
+
after = self.indexbracket + 1
|
| 138 |
+
while (after < len(self.bracketing) and
|
| 139 |
+
self.bracketing[after][1] >= bracketinglevel):
|
| 140 |
+
after += 1
|
| 141 |
+
|
| 142 |
+
beforeindex = self.text.index("%s-%dc" %
|
| 143 |
+
(self.stopatindex, len(self.rawtext)-self.bracketing[before][0]))
|
| 144 |
+
if (after >= len(self.bracketing) or
|
| 145 |
+
self.bracketing[after][0] > len(self.rawtext)):
|
| 146 |
+
if mustclose:
|
| 147 |
+
return None
|
| 148 |
+
afterindex = self.stopatindex
|
| 149 |
+
else:
|
| 150 |
+
# We are after a real char, so it is a ')' and we give the
|
| 151 |
+
# index before it.
|
| 152 |
+
afterindex = self.text.index(
|
| 153 |
+
"%s-%dc" % (self.stopatindex,
|
| 154 |
+
len(self.rawtext)-(self.bracketing[after][0]-1)))
|
| 155 |
+
|
| 156 |
+
return beforeindex, afterindex
|
| 157 |
+
|
| 158 |
+
# the set of built-in identifiers which are also keywords,
|
| 159 |
+
# i.e. keyword.iskeyword() returns True for them
|
| 160 |
+
_ID_KEYWORDS = frozenset({"True", "False", "None"})
|
| 161 |
+
|
| 162 |
+
@classmethod
|
| 163 |
+
def _eat_identifier(cls, str, limit, pos):
|
| 164 |
+
"""Given a string and pos, return the number of chars in the
|
| 165 |
+
identifier which ends at pos, or 0 if there is no such one.
|
| 166 |
+
|
| 167 |
+
This ignores non-identifier eywords are not identifiers.
|
| 168 |
+
"""
|
| 169 |
+
is_ascii_id_char = _IS_ASCII_ID_CHAR
|
| 170 |
+
|
| 171 |
+
# Start at the end (pos) and work backwards.
|
| 172 |
+
i = pos
|
| 173 |
+
|
| 174 |
+
# Go backwards as long as the characters are valid ASCII
|
| 175 |
+
# identifier characters. This is an optimization, since it
|
| 176 |
+
# is faster in the common case where most of the characters
|
| 177 |
+
# are ASCII.
|
| 178 |
+
while i > limit and (
|
| 179 |
+
ord(str[i - 1]) < 128 and
|
| 180 |
+
is_ascii_id_char[ord(str[i - 1])]
|
| 181 |
+
):
|
| 182 |
+
i -= 1
|
| 183 |
+
|
| 184 |
+
# If the above loop ended due to reaching a non-ASCII
|
| 185 |
+
# character, continue going backwards using the most generic
|
| 186 |
+
# test for whether a string contains only valid identifier
|
| 187 |
+
# characters.
|
| 188 |
+
if i > limit and ord(str[i - 1]) >= 128:
|
| 189 |
+
while i - 4 >= limit and ('a' + str[i - 4:pos]).isidentifier():
|
| 190 |
+
i -= 4
|
| 191 |
+
if i - 2 >= limit and ('a' + str[i - 2:pos]).isidentifier():
|
| 192 |
+
i -= 2
|
| 193 |
+
if i - 1 >= limit and ('a' + str[i - 1:pos]).isidentifier():
|
| 194 |
+
i -= 1
|
| 195 |
+
|
| 196 |
+
# The identifier candidate starts here. If it isn't a valid
|
| 197 |
+
# identifier, don't eat anything. At this point that is only
|
| 198 |
+
# possible if the first character isn't a valid first
|
| 199 |
+
# character for an identifier.
|
| 200 |
+
if not str[i:pos].isidentifier():
|
| 201 |
+
return 0
|
| 202 |
+
elif i < pos:
|
| 203 |
+
# All characters in str[i:pos] are valid ASCII identifier
|
| 204 |
+
# characters, so it is enough to check that the first is
|
| 205 |
+
# valid as the first character of an identifier.
|
| 206 |
+
if not _IS_ASCII_ID_FIRST_CHAR[ord(str[i])]:
|
| 207 |
+
return 0
|
| 208 |
+
|
| 209 |
+
# All keywords are valid identifiers, but should not be
|
| 210 |
+
# considered identifiers here, except for True, False and None.
|
| 211 |
+
if i < pos and (
|
| 212 |
+
iskeyword(str[i:pos]) and
|
| 213 |
+
str[i:pos] not in cls._ID_KEYWORDS
|
| 214 |
+
):
|
| 215 |
+
return 0
|
| 216 |
+
|
| 217 |
+
return pos - i
|
| 218 |
+
|
| 219 |
+
# This string includes all chars that may be in a white space
|
| 220 |
+
_whitespace_chars = " \t\n\\"
|
| 221 |
+
|
| 222 |
+
def get_expression(self):
|
| 223 |
+
"""Return a string with the Python expression which ends at the
|
| 224 |
+
given index, which is empty if there is no real one.
|
| 225 |
+
"""
|
| 226 |
+
if not self.is_in_code():
|
| 227 |
+
raise ValueError("get_expression should only be called "
|
| 228 |
+
"if index is inside a code.")
|
| 229 |
+
|
| 230 |
+
rawtext = self.rawtext
|
| 231 |
+
bracketing = self.bracketing
|
| 232 |
+
|
| 233 |
+
brck_index = self.indexbracket
|
| 234 |
+
brck_limit = bracketing[brck_index][0]
|
| 235 |
+
pos = self.indexinrawtext
|
| 236 |
+
|
| 237 |
+
last_identifier_pos = pos
|
| 238 |
+
postdot_phase = True
|
| 239 |
+
|
| 240 |
+
while 1:
|
| 241 |
+
# Eat whitespaces, comments, and if postdot_phase is False - a dot
|
| 242 |
+
while 1:
|
| 243 |
+
if pos>brck_limit and rawtext[pos-1] in self._whitespace_chars:
|
| 244 |
+
# Eat a whitespace
|
| 245 |
+
pos -= 1
|
| 246 |
+
elif (not postdot_phase and
|
| 247 |
+
pos > brck_limit and rawtext[pos-1] == '.'):
|
| 248 |
+
# Eat a dot
|
| 249 |
+
pos -= 1
|
| 250 |
+
postdot_phase = True
|
| 251 |
+
# The next line will fail if we are *inside* a comment,
|
| 252 |
+
# but we shouldn't be.
|
| 253 |
+
elif (pos == brck_limit and brck_index > 0 and
|
| 254 |
+
rawtext[bracketing[brck_index-1][0]] == '#'):
|
| 255 |
+
# Eat a comment
|
| 256 |
+
brck_index -= 2
|
| 257 |
+
brck_limit = bracketing[brck_index][0]
|
| 258 |
+
pos = bracketing[brck_index+1][0]
|
| 259 |
+
else:
|
| 260 |
+
# If we didn't eat anything, quit.
|
| 261 |
+
break
|
| 262 |
+
|
| 263 |
+
if not postdot_phase:
|
| 264 |
+
# We didn't find a dot, so the expression end at the
|
| 265 |
+
# last identifier pos.
|
| 266 |
+
break
|
| 267 |
+
|
| 268 |
+
ret = self._eat_identifier(rawtext, brck_limit, pos)
|
| 269 |
+
if ret:
|
| 270 |
+
# There is an identifier to eat
|
| 271 |
+
pos = pos - ret
|
| 272 |
+
last_identifier_pos = pos
|
| 273 |
+
# Now, to continue the search, we must find a dot.
|
| 274 |
+
postdot_phase = False
|
| 275 |
+
# (the loop continues now)
|
| 276 |
+
|
| 277 |
+
elif pos == brck_limit:
|
| 278 |
+
# We are at a bracketing limit. If it is a closing
|
| 279 |
+
# bracket, eat the bracket, otherwise, stop the search.
|
| 280 |
+
level = bracketing[brck_index][1]
|
| 281 |
+
while brck_index > 0 and bracketing[brck_index-1][1] > level:
|
| 282 |
+
brck_index -= 1
|
| 283 |
+
if bracketing[brck_index][0] == brck_limit:
|
| 284 |
+
# We were not at the end of a closing bracket
|
| 285 |
+
break
|
| 286 |
+
pos = bracketing[brck_index][0]
|
| 287 |
+
brck_index -= 1
|
| 288 |
+
brck_limit = bracketing[brck_index][0]
|
| 289 |
+
last_identifier_pos = pos
|
| 290 |
+
if rawtext[pos] in "([":
|
| 291 |
+
# [] and () may be used after an identifier, so we
|
| 292 |
+
# continue. postdot_phase is True, so we don't allow a dot.
|
| 293 |
+
pass
|
| 294 |
+
else:
|
| 295 |
+
# We can't continue after other types of brackets
|
| 296 |
+
if rawtext[pos] in "'\"":
|
| 297 |
+
# Scan a string prefix
|
| 298 |
+
while pos > 0 and rawtext[pos - 1] in "rRbBuU":
|
| 299 |
+
pos -= 1
|
| 300 |
+
last_identifier_pos = pos
|
| 301 |
+
break
|
| 302 |
+
|
| 303 |
+
else:
|
| 304 |
+
# We've found an operator or something.
|
| 305 |
+
break
|
| 306 |
+
|
| 307 |
+
return rawtext[last_identifier_pos:self.indexinrawtext]
|
| 308 |
+
|
| 309 |
+
|
| 310 |
+
if __name__ == '__main__':
|
| 311 |
+
from unittest import main
|
| 312 |
+
main('idlelib.idle_test.test_hyperparser', verbosity=2)
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/idle.bat
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
@echo off
|
| 2 |
+
rem Start IDLE using the appropriate Python interpreter
|
| 3 |
+
set CURRDIR=%~dp0
|
| 4 |
+
start "IDLE" "%CURRDIR%..\..\pythonw.exe" "%CURRDIR%idle.pyw" %1 %2 %3 %4 %5 %6 %7 %8 %9
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/idle.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os.path
|
| 2 |
+
import sys
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
# Enable running IDLE with idlelib in a non-standard location.
|
| 6 |
+
# This was once used to run development versions of IDLE.
|
| 7 |
+
# Because PEP 434 declared idle.py a public interface,
|
| 8 |
+
# removal should require deprecation.
|
| 9 |
+
idlelib_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
| 10 |
+
if idlelib_dir not in sys.path:
|
| 11 |
+
sys.path.insert(0, idlelib_dir)
|
| 12 |
+
|
| 13 |
+
from idlelib.pyshell import main # This is subject to change
|
| 14 |
+
main()
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/idle.pyw
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
try:
|
| 2 |
+
import idlelib.pyshell
|
| 3 |
+
except ImportError:
|
| 4 |
+
# IDLE is not installed, but maybe pyshell is on sys.path:
|
| 5 |
+
from . import pyshell
|
| 6 |
+
import os
|
| 7 |
+
idledir = os.path.dirname(os.path.abspath(pyshell.__file__))
|
| 8 |
+
if idledir != os.getcwd():
|
| 9 |
+
# We're not in the IDLE directory, help the subprocess find run.py
|
| 10 |
+
pypath = os.environ.get('PYTHONPATH', '')
|
| 11 |
+
if pypath:
|
| 12 |
+
os.environ['PYTHONPATH'] = pypath + ':' + idledir
|
| 13 |
+
else:
|
| 14 |
+
os.environ['PYTHONPATH'] = idledir
|
| 15 |
+
pyshell.main()
|
| 16 |
+
else:
|
| 17 |
+
idlelib.pyshell.main()
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/macosx.py
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
A number of functions that enhance IDLE on macOS.
|
| 3 |
+
"""
|
| 4 |
+
from os.path import expanduser
|
| 5 |
+
import plistlib
|
| 6 |
+
from sys import platform # Used in _init_tk_type, changed by test.
|
| 7 |
+
|
| 8 |
+
import tkinter
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
## Define functions that query the Mac graphics type.
|
| 12 |
+
## _tk_type and its initializer are private to this section.
|
| 13 |
+
|
| 14 |
+
_tk_type = None
|
| 15 |
+
|
| 16 |
+
def _init_tk_type():
|
| 17 |
+
"""
|
| 18 |
+
Initializes OS X Tk variant values for
|
| 19 |
+
isAquaTk(), isCarbonTk(), isCocoaTk(), and isXQuartz().
|
| 20 |
+
"""
|
| 21 |
+
global _tk_type
|
| 22 |
+
if platform == 'darwin':
|
| 23 |
+
root = tkinter.Tk()
|
| 24 |
+
ws = root.tk.call('tk', 'windowingsystem')
|
| 25 |
+
if 'x11' in ws:
|
| 26 |
+
_tk_type = "xquartz"
|
| 27 |
+
elif 'aqua' not in ws:
|
| 28 |
+
_tk_type = "other"
|
| 29 |
+
elif 'AppKit' in root.tk.call('winfo', 'server', '.'):
|
| 30 |
+
_tk_type = "cocoa"
|
| 31 |
+
else:
|
| 32 |
+
_tk_type = "carbon"
|
| 33 |
+
root.destroy()
|
| 34 |
+
else:
|
| 35 |
+
_tk_type = "other"
|
| 36 |
+
|
| 37 |
+
def isAquaTk():
|
| 38 |
+
"""
|
| 39 |
+
Returns True if IDLE is using a native OS X Tk (Cocoa or Carbon).
|
| 40 |
+
"""
|
| 41 |
+
if not _tk_type:
|
| 42 |
+
_init_tk_type()
|
| 43 |
+
return _tk_type == "cocoa" or _tk_type == "carbon"
|
| 44 |
+
|
| 45 |
+
def isCarbonTk():
|
| 46 |
+
"""
|
| 47 |
+
Returns True if IDLE is using a Carbon Aqua Tk (instead of the
|
| 48 |
+
newer Cocoa Aqua Tk).
|
| 49 |
+
"""
|
| 50 |
+
if not _tk_type:
|
| 51 |
+
_init_tk_type()
|
| 52 |
+
return _tk_type == "carbon"
|
| 53 |
+
|
| 54 |
+
def isCocoaTk():
|
| 55 |
+
"""
|
| 56 |
+
Returns True if IDLE is using a Cocoa Aqua Tk.
|
| 57 |
+
"""
|
| 58 |
+
if not _tk_type:
|
| 59 |
+
_init_tk_type()
|
| 60 |
+
return _tk_type == "cocoa"
|
| 61 |
+
|
| 62 |
+
def isXQuartz():
|
| 63 |
+
"""
|
| 64 |
+
Returns True if IDLE is using an OS X X11 Tk.
|
| 65 |
+
"""
|
| 66 |
+
if not _tk_type:
|
| 67 |
+
_init_tk_type()
|
| 68 |
+
return _tk_type == "xquartz"
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
def tkVersionWarning(root):
|
| 72 |
+
"""
|
| 73 |
+
Returns a string warning message if the Tk version in use appears to
|
| 74 |
+
be one known to cause problems with IDLE.
|
| 75 |
+
1. Apple Cocoa-based Tk 8.5.7 shipped with Mac OS X 10.6 is unusable.
|
| 76 |
+
2. Apple Cocoa-based Tk 8.5.9 in OS X 10.7 and 10.8 is better but
|
| 77 |
+
can still crash unexpectedly.
|
| 78 |
+
"""
|
| 79 |
+
|
| 80 |
+
if isCocoaTk():
|
| 81 |
+
patchlevel = root.tk.call('info', 'patchlevel')
|
| 82 |
+
if patchlevel not in ('8.5.7', '8.5.9'):
|
| 83 |
+
return False
|
| 84 |
+
return ("WARNING: The version of Tcl/Tk ({0}) in use may"
|
| 85 |
+
" be unstable.\n"
|
| 86 |
+
"Visit http://www.python.org/download/mac/tcltk/"
|
| 87 |
+
" for current information.".format(patchlevel))
|
| 88 |
+
else:
|
| 89 |
+
return False
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
def readSystemPreferences():
|
| 93 |
+
"""
|
| 94 |
+
Fetch the macOS system preferences.
|
| 95 |
+
"""
|
| 96 |
+
if platform != 'darwin':
|
| 97 |
+
return None
|
| 98 |
+
|
| 99 |
+
plist_path = expanduser('~/Library/Preferences/.GlobalPreferences.plist')
|
| 100 |
+
try:
|
| 101 |
+
with open(plist_path, 'rb') as plist_file:
|
| 102 |
+
return plistlib.load(plist_file)
|
| 103 |
+
except OSError:
|
| 104 |
+
return None
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
def preferTabsPreferenceWarning():
|
| 108 |
+
"""
|
| 109 |
+
Warn if "Prefer tabs when opening documents" is set to "Always".
|
| 110 |
+
"""
|
| 111 |
+
if platform != 'darwin':
|
| 112 |
+
return None
|
| 113 |
+
|
| 114 |
+
prefs = readSystemPreferences()
|
| 115 |
+
if prefs and prefs.get('AppleWindowTabbingMode') == 'always':
|
| 116 |
+
return (
|
| 117 |
+
'WARNING: The system preference "Prefer tabs when opening'
|
| 118 |
+
' documents" is set to "Always". This will cause various problems'
|
| 119 |
+
' with IDLE. For the best experience, change this setting when'
|
| 120 |
+
' running IDLE (via System Preferences -> Dock).'
|
| 121 |
+
)
|
| 122 |
+
return None
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
## Fix the menu and related functions.
|
| 126 |
+
|
| 127 |
+
def addOpenEventSupport(root, flist):
|
| 128 |
+
"""
|
| 129 |
+
This ensures that the application will respond to open AppleEvents, which
|
| 130 |
+
makes is feasible to use IDLE as the default application for python files.
|
| 131 |
+
"""
|
| 132 |
+
def doOpenFile(*args):
|
| 133 |
+
for fn in args:
|
| 134 |
+
flist.open(fn)
|
| 135 |
+
|
| 136 |
+
# The command below is a hook in aquatk that is called whenever the app
|
| 137 |
+
# receives a file open event. The callback can have multiple arguments,
|
| 138 |
+
# one for every file that should be opened.
|
| 139 |
+
root.createcommand("::tk::mac::OpenDocument", doOpenFile)
|
| 140 |
+
|
| 141 |
+
def hideTkConsole(root):
|
| 142 |
+
try:
|
| 143 |
+
root.tk.call('console', 'hide')
|
| 144 |
+
except tkinter.TclError:
|
| 145 |
+
# Some versions of the Tk framework don't have a console object
|
| 146 |
+
pass
|
| 147 |
+
|
| 148 |
+
def overrideRootMenu(root, flist):
|
| 149 |
+
"""
|
| 150 |
+
Replace the Tk root menu by something that is more appropriate for
|
| 151 |
+
IDLE with an Aqua Tk.
|
| 152 |
+
"""
|
| 153 |
+
# The menu that is attached to the Tk root (".") is also used by AquaTk for
|
| 154 |
+
# all windows that don't specify a menu of their own. The default menubar
|
| 155 |
+
# contains a number of menus, none of which are appropriate for IDLE. The
|
| 156 |
+
# Most annoying of those is an 'About Tck/Tk...' menu in the application
|
| 157 |
+
# menu.
|
| 158 |
+
#
|
| 159 |
+
# This function replaces the default menubar by a mostly empty one, it
|
| 160 |
+
# should only contain the correct application menu and the window menu.
|
| 161 |
+
#
|
| 162 |
+
# Due to a (mis-)feature of TkAqua the user will also see an empty Help
|
| 163 |
+
# menu.
|
| 164 |
+
from tkinter import Menu
|
| 165 |
+
from idlelib import mainmenu
|
| 166 |
+
from idlelib import window
|
| 167 |
+
|
| 168 |
+
closeItem = mainmenu.menudefs[0][1][-2]
|
| 169 |
+
|
| 170 |
+
# Remove the last 3 items of the file menu: a separator, close window and
|
| 171 |
+
# quit. Close window will be reinserted just above the save item, where
|
| 172 |
+
# it should be according to the HIG. Quit is in the application menu.
|
| 173 |
+
del mainmenu.menudefs[0][1][-3:]
|
| 174 |
+
mainmenu.menudefs[0][1].insert(6, closeItem)
|
| 175 |
+
|
| 176 |
+
# Remove the 'About' entry from the help menu, it is in the application
|
| 177 |
+
# menu
|
| 178 |
+
del mainmenu.menudefs[-1][1][0:2]
|
| 179 |
+
# Remove the 'Configure Idle' entry from the options menu, it is in the
|
| 180 |
+
# application menu as 'Preferences'
|
| 181 |
+
del mainmenu.menudefs[-3][1][0:2]
|
| 182 |
+
menubar = Menu(root)
|
| 183 |
+
root.configure(menu=menubar)
|
| 184 |
+
menudict = {}
|
| 185 |
+
|
| 186 |
+
menudict['window'] = menu = Menu(menubar, name='window', tearoff=0)
|
| 187 |
+
menubar.add_cascade(label='Window', menu=menu, underline=0)
|
| 188 |
+
|
| 189 |
+
def postwindowsmenu(menu=menu):
|
| 190 |
+
end = menu.index('end')
|
| 191 |
+
if end is None:
|
| 192 |
+
end = -1
|
| 193 |
+
|
| 194 |
+
if end > 0:
|
| 195 |
+
menu.delete(0, end)
|
| 196 |
+
window.add_windows_to_menu(menu)
|
| 197 |
+
window.register_callback(postwindowsmenu)
|
| 198 |
+
|
| 199 |
+
def about_dialog(event=None):
|
| 200 |
+
"Handle Help 'About IDLE' event."
|
| 201 |
+
# Synchronize with editor.EditorWindow.about_dialog.
|
| 202 |
+
from idlelib import help_about
|
| 203 |
+
help_about.AboutDialog(root)
|
| 204 |
+
|
| 205 |
+
def config_dialog(event=None):
|
| 206 |
+
"Handle Options 'Configure IDLE' event."
|
| 207 |
+
# Synchronize with editor.EditorWindow.config_dialog.
|
| 208 |
+
from idlelib import configdialog
|
| 209 |
+
|
| 210 |
+
# Ensure that the root object has an instance_dict attribute,
|
| 211 |
+
# mirrors code in EditorWindow (although that sets the attribute
|
| 212 |
+
# on an EditorWindow instance that is then passed as the first
|
| 213 |
+
# argument to ConfigDialog)
|
| 214 |
+
root.instance_dict = flist.inversedict
|
| 215 |
+
configdialog.ConfigDialog(root, 'Settings')
|
| 216 |
+
|
| 217 |
+
def help_dialog(event=None):
|
| 218 |
+
"Handle Help 'IDLE Help' event."
|
| 219 |
+
# Synchronize with editor.EditorWindow.help_dialog.
|
| 220 |
+
from idlelib import help
|
| 221 |
+
help.show_idlehelp(root)
|
| 222 |
+
|
| 223 |
+
root.bind('<<about-idle>>', about_dialog)
|
| 224 |
+
root.bind('<<open-config-dialog>>', config_dialog)
|
| 225 |
+
root.createcommand('::tk::mac::ShowPreferences', config_dialog)
|
| 226 |
+
if flist:
|
| 227 |
+
root.bind('<<close-all-windows>>', flist.close_all_callback)
|
| 228 |
+
|
| 229 |
+
# The binding above doesn't reliably work on all versions of Tk
|
| 230 |
+
# on macOS. Adding command definition below does seem to do the
|
| 231 |
+
# right thing for now.
|
| 232 |
+
root.createcommand('exit', flist.close_all_callback)
|
| 233 |
+
|
| 234 |
+
if isCarbonTk():
|
| 235 |
+
# for Carbon AquaTk, replace the default Tk apple menu
|
| 236 |
+
menudict['application'] = menu = Menu(menubar, name='apple',
|
| 237 |
+
tearoff=0)
|
| 238 |
+
menubar.add_cascade(label='IDLE', menu=menu)
|
| 239 |
+
mainmenu.menudefs.insert(0,
|
| 240 |
+
('application', [
|
| 241 |
+
('About IDLE', '<<about-idle>>'),
|
| 242 |
+
None,
|
| 243 |
+
]))
|
| 244 |
+
if isCocoaTk():
|
| 245 |
+
# replace default About dialog with About IDLE one
|
| 246 |
+
root.createcommand('tkAboutDialog', about_dialog)
|
| 247 |
+
# replace default "Help" item in Help menu
|
| 248 |
+
root.createcommand('::tk::mac::ShowHelp', help_dialog)
|
| 249 |
+
# remove redundant "IDLE Help" from menu
|
| 250 |
+
del mainmenu.menudefs[-1][1][0]
|
| 251 |
+
|
| 252 |
+
def fixb2context(root):
|
| 253 |
+
'''Removed bad AquaTk Button-2 (right) and Paste bindings.
|
| 254 |
+
|
| 255 |
+
They prevent context menu access and seem to be gone in AquaTk8.6.
|
| 256 |
+
See issue #24801.
|
| 257 |
+
'''
|
| 258 |
+
root.unbind_class('Text', '<B2>')
|
| 259 |
+
root.unbind_class('Text', '<B2-Motion>')
|
| 260 |
+
root.unbind_class('Text', '<<PasteSelection>>')
|
| 261 |
+
|
| 262 |
+
def setupApp(root, flist):
|
| 263 |
+
"""
|
| 264 |
+
Perform initial OS X customizations if needed.
|
| 265 |
+
Called from pyshell.main() after initial calls to Tk()
|
| 266 |
+
|
| 267 |
+
There are currently three major versions of Tk in use on OS X:
|
| 268 |
+
1. Aqua Cocoa Tk (native default since OS X 10.6)
|
| 269 |
+
2. Aqua Carbon Tk (original native, 32-bit only, deprecated)
|
| 270 |
+
3. X11 (supported by some third-party distributors, deprecated)
|
| 271 |
+
There are various differences among the three that affect IDLE
|
| 272 |
+
behavior, primarily with menus, mouse key events, and accelerators.
|
| 273 |
+
Some one-time customizations are performed here.
|
| 274 |
+
Others are dynamically tested throughout idlelib by calls to the
|
| 275 |
+
isAquaTk(), isCarbonTk(), isCocoaTk(), isXQuartz() functions which
|
| 276 |
+
are initialized here as well.
|
| 277 |
+
"""
|
| 278 |
+
if isAquaTk():
|
| 279 |
+
hideTkConsole(root)
|
| 280 |
+
overrideRootMenu(root, flist)
|
| 281 |
+
addOpenEventSupport(root, flist)
|
| 282 |
+
fixb2context(root)
|
| 283 |
+
|
| 284 |
+
|
| 285 |
+
if __name__ == '__main__':
|
| 286 |
+
from unittest import main
|
| 287 |
+
main('idlelib.idle_test.test_macosx', verbosity=2)
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/multicall.py
ADDED
|
@@ -0,0 +1,448 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
MultiCall - a class which inherits its methods from a Tkinter widget (Text, for
|
| 3 |
+
example), but enables multiple calls of functions per virtual event - all
|
| 4 |
+
matching events will be called, not only the most specific one. This is done
|
| 5 |
+
by wrapping the event functions - event_add, event_delete and event_info.
|
| 6 |
+
MultiCall recognizes only a subset of legal event sequences. Sequences which
|
| 7 |
+
are not recognized are treated by the original Tk handling mechanism. A
|
| 8 |
+
more-specific event will be called before a less-specific event.
|
| 9 |
+
|
| 10 |
+
The recognized sequences are complete one-event sequences (no emacs-style
|
| 11 |
+
Ctrl-X Ctrl-C, no shortcuts like <3>), for all types of events.
|
| 12 |
+
Key/Button Press/Release events can have modifiers.
|
| 13 |
+
The recognized modifiers are Shift, Control, Option and Command for Mac, and
|
| 14 |
+
Control, Alt, Shift, Meta/M for other platforms.
|
| 15 |
+
|
| 16 |
+
For all events which were handled by MultiCall, a new member is added to the
|
| 17 |
+
event instance passed to the binded functions - mc_type. This is one of the
|
| 18 |
+
event type constants defined in this module (such as MC_KEYPRESS).
|
| 19 |
+
For Key/Button events (which are handled by MultiCall and may receive
|
| 20 |
+
modifiers), another member is added - mc_state. This member gives the state
|
| 21 |
+
of the recognized modifiers, as a combination of the modifier constants
|
| 22 |
+
also defined in this module (for example, MC_SHIFT).
|
| 23 |
+
Using these members is absolutely portable.
|
| 24 |
+
|
| 25 |
+
The order by which events are called is defined by these rules:
|
| 26 |
+
1. A more-specific event will be called before a less-specific event.
|
| 27 |
+
2. A recently-binded event will be called before a previously-binded event,
|
| 28 |
+
unless this conflicts with the first rule.
|
| 29 |
+
Each function will be called at most once for each event.
|
| 30 |
+
"""
|
| 31 |
+
import re
|
| 32 |
+
import sys
|
| 33 |
+
|
| 34 |
+
import tkinter
|
| 35 |
+
|
| 36 |
+
# the event type constants, which define the meaning of mc_type
|
| 37 |
+
MC_KEYPRESS=0; MC_KEYRELEASE=1; MC_BUTTONPRESS=2; MC_BUTTONRELEASE=3;
|
| 38 |
+
MC_ACTIVATE=4; MC_CIRCULATE=5; MC_COLORMAP=6; MC_CONFIGURE=7;
|
| 39 |
+
MC_DEACTIVATE=8; MC_DESTROY=9; MC_ENTER=10; MC_EXPOSE=11; MC_FOCUSIN=12;
|
| 40 |
+
MC_FOCUSOUT=13; MC_GRAVITY=14; MC_LEAVE=15; MC_MAP=16; MC_MOTION=17;
|
| 41 |
+
MC_MOUSEWHEEL=18; MC_PROPERTY=19; MC_REPARENT=20; MC_UNMAP=21; MC_VISIBILITY=22;
|
| 42 |
+
# the modifier state constants, which define the meaning of mc_state
|
| 43 |
+
MC_SHIFT = 1<<0; MC_CONTROL = 1<<2; MC_ALT = 1<<3; MC_META = 1<<5
|
| 44 |
+
MC_OPTION = 1<<6; MC_COMMAND = 1<<7
|
| 45 |
+
|
| 46 |
+
# define the list of modifiers, to be used in complex event types.
|
| 47 |
+
if sys.platform == "darwin":
|
| 48 |
+
_modifiers = (("Shift",), ("Control",), ("Option",), ("Command",))
|
| 49 |
+
_modifier_masks = (MC_SHIFT, MC_CONTROL, MC_OPTION, MC_COMMAND)
|
| 50 |
+
else:
|
| 51 |
+
_modifiers = (("Control",), ("Alt",), ("Shift",), ("Meta", "M"))
|
| 52 |
+
_modifier_masks = (MC_CONTROL, MC_ALT, MC_SHIFT, MC_META)
|
| 53 |
+
|
| 54 |
+
# a dictionary to map a modifier name into its number
|
| 55 |
+
_modifier_names = dict([(name, number)
|
| 56 |
+
for number in range(len(_modifiers))
|
| 57 |
+
for name in _modifiers[number]])
|
| 58 |
+
|
| 59 |
+
# In 3.4, if no shell window is ever open, the underlying Tk widget is
|
| 60 |
+
# destroyed before .__del__ methods here are called. The following
|
| 61 |
+
# is used to selectively ignore shutdown exceptions to avoid
|
| 62 |
+
# 'Exception ignored' messages. See http://bugs.python.org/issue20167
|
| 63 |
+
APPLICATION_GONE = "application has been destroyed"
|
| 64 |
+
|
| 65 |
+
# A binder is a class which binds functions to one type of event. It has two
|
| 66 |
+
# methods: bind and unbind, which get a function and a parsed sequence, as
|
| 67 |
+
# returned by _parse_sequence(). There are two types of binders:
|
| 68 |
+
# _SimpleBinder handles event types with no modifiers and no detail.
|
| 69 |
+
# No Python functions are called when no events are binded.
|
| 70 |
+
# _ComplexBinder handles event types with modifiers and a detail.
|
| 71 |
+
# A Python function is called each time an event is generated.
|
| 72 |
+
|
| 73 |
+
class _SimpleBinder:
|
| 74 |
+
def __init__(self, type, widget, widgetinst):
|
| 75 |
+
self.type = type
|
| 76 |
+
self.sequence = '<'+_types[type][0]+'>'
|
| 77 |
+
self.widget = widget
|
| 78 |
+
self.widgetinst = widgetinst
|
| 79 |
+
self.bindedfuncs = []
|
| 80 |
+
self.handlerid = None
|
| 81 |
+
|
| 82 |
+
def bind(self, triplet, func):
|
| 83 |
+
if not self.handlerid:
|
| 84 |
+
def handler(event, l = self.bindedfuncs, mc_type = self.type):
|
| 85 |
+
event.mc_type = mc_type
|
| 86 |
+
wascalled = {}
|
| 87 |
+
for i in range(len(l)-1, -1, -1):
|
| 88 |
+
func = l[i]
|
| 89 |
+
if func not in wascalled:
|
| 90 |
+
wascalled[func] = True
|
| 91 |
+
r = func(event)
|
| 92 |
+
if r:
|
| 93 |
+
return r
|
| 94 |
+
self.handlerid = self.widget.bind(self.widgetinst,
|
| 95 |
+
self.sequence, handler)
|
| 96 |
+
self.bindedfuncs.append(func)
|
| 97 |
+
|
| 98 |
+
def unbind(self, triplet, func):
|
| 99 |
+
self.bindedfuncs.remove(func)
|
| 100 |
+
if not self.bindedfuncs:
|
| 101 |
+
self.widget.unbind(self.widgetinst, self.sequence, self.handlerid)
|
| 102 |
+
self.handlerid = None
|
| 103 |
+
|
| 104 |
+
def __del__(self):
|
| 105 |
+
if self.handlerid:
|
| 106 |
+
try:
|
| 107 |
+
self.widget.unbind(self.widgetinst, self.sequence,
|
| 108 |
+
self.handlerid)
|
| 109 |
+
except tkinter.TclError as e:
|
| 110 |
+
if not APPLICATION_GONE in e.args[0]:
|
| 111 |
+
raise
|
| 112 |
+
|
| 113 |
+
# An int in range(1 << len(_modifiers)) represents a combination of modifiers
|
| 114 |
+
# (if the least significant bit is on, _modifiers[0] is on, and so on).
|
| 115 |
+
# _state_subsets gives for each combination of modifiers, or *state*,
|
| 116 |
+
# a list of the states which are a subset of it. This list is ordered by the
|
| 117 |
+
# number of modifiers is the state - the most specific state comes first.
|
| 118 |
+
_states = range(1 << len(_modifiers))
|
| 119 |
+
_state_names = [''.join(m[0]+'-'
|
| 120 |
+
for i, m in enumerate(_modifiers)
|
| 121 |
+
if (1 << i) & s)
|
| 122 |
+
for s in _states]
|
| 123 |
+
|
| 124 |
+
def expand_substates(states):
|
| 125 |
+
'''For each item of states return a list containing all combinations of
|
| 126 |
+
that item with individual bits reset, sorted by the number of set bits.
|
| 127 |
+
'''
|
| 128 |
+
def nbits(n):
|
| 129 |
+
"number of bits set in n base 2"
|
| 130 |
+
nb = 0
|
| 131 |
+
while n:
|
| 132 |
+
n, rem = divmod(n, 2)
|
| 133 |
+
nb += rem
|
| 134 |
+
return nb
|
| 135 |
+
statelist = []
|
| 136 |
+
for state in states:
|
| 137 |
+
substates = list(set(state & x for x in states))
|
| 138 |
+
substates.sort(key=nbits, reverse=True)
|
| 139 |
+
statelist.append(substates)
|
| 140 |
+
return statelist
|
| 141 |
+
|
| 142 |
+
_state_subsets = expand_substates(_states)
|
| 143 |
+
|
| 144 |
+
# _state_codes gives for each state, the portable code to be passed as mc_state
|
| 145 |
+
_state_codes = []
|
| 146 |
+
for s in _states:
|
| 147 |
+
r = 0
|
| 148 |
+
for i in range(len(_modifiers)):
|
| 149 |
+
if (1 << i) & s:
|
| 150 |
+
r |= _modifier_masks[i]
|
| 151 |
+
_state_codes.append(r)
|
| 152 |
+
|
| 153 |
+
class _ComplexBinder:
|
| 154 |
+
# This class binds many functions, and only unbinds them when it is deleted.
|
| 155 |
+
# self.handlerids is the list of seqs and ids of binded handler functions.
|
| 156 |
+
# The binded functions sit in a dictionary of lists of lists, which maps
|
| 157 |
+
# a detail (or None) and a state into a list of functions.
|
| 158 |
+
# When a new detail is discovered, handlers for all the possible states
|
| 159 |
+
# are binded.
|
| 160 |
+
|
| 161 |
+
def __create_handler(self, lists, mc_type, mc_state):
|
| 162 |
+
def handler(event, lists = lists,
|
| 163 |
+
mc_type = mc_type, mc_state = mc_state,
|
| 164 |
+
ishandlerrunning = self.ishandlerrunning,
|
| 165 |
+
doafterhandler = self.doafterhandler):
|
| 166 |
+
ishandlerrunning[:] = [True]
|
| 167 |
+
event.mc_type = mc_type
|
| 168 |
+
event.mc_state = mc_state
|
| 169 |
+
wascalled = {}
|
| 170 |
+
r = None
|
| 171 |
+
for l in lists:
|
| 172 |
+
for i in range(len(l)-1, -1, -1):
|
| 173 |
+
func = l[i]
|
| 174 |
+
if func not in wascalled:
|
| 175 |
+
wascalled[func] = True
|
| 176 |
+
r = l[i](event)
|
| 177 |
+
if r:
|
| 178 |
+
break
|
| 179 |
+
if r:
|
| 180 |
+
break
|
| 181 |
+
ishandlerrunning[:] = []
|
| 182 |
+
# Call all functions in doafterhandler and remove them from list
|
| 183 |
+
for f in doafterhandler:
|
| 184 |
+
f()
|
| 185 |
+
doafterhandler[:] = []
|
| 186 |
+
if r:
|
| 187 |
+
return r
|
| 188 |
+
return handler
|
| 189 |
+
|
| 190 |
+
def __init__(self, type, widget, widgetinst):
|
| 191 |
+
self.type = type
|
| 192 |
+
self.typename = _types[type][0]
|
| 193 |
+
self.widget = widget
|
| 194 |
+
self.widgetinst = widgetinst
|
| 195 |
+
self.bindedfuncs = {None: [[] for s in _states]}
|
| 196 |
+
self.handlerids = []
|
| 197 |
+
# we don't want to change the lists of functions while a handler is
|
| 198 |
+
# running - it will mess up the loop and anyway, we usually want the
|
| 199 |
+
# change to happen from the next event. So we have a list of functions
|
| 200 |
+
# for the handler to run after it finishes calling the binded functions.
|
| 201 |
+
# It calls them only once.
|
| 202 |
+
# ishandlerrunning is a list. An empty one means no, otherwise - yes.
|
| 203 |
+
# this is done so that it would be mutable.
|
| 204 |
+
self.ishandlerrunning = []
|
| 205 |
+
self.doafterhandler = []
|
| 206 |
+
for s in _states:
|
| 207 |
+
lists = [self.bindedfuncs[None][i] for i in _state_subsets[s]]
|
| 208 |
+
handler = self.__create_handler(lists, type, _state_codes[s])
|
| 209 |
+
seq = '<'+_state_names[s]+self.typename+'>'
|
| 210 |
+
self.handlerids.append((seq, self.widget.bind(self.widgetinst,
|
| 211 |
+
seq, handler)))
|
| 212 |
+
|
| 213 |
+
def bind(self, triplet, func):
|
| 214 |
+
if triplet[2] not in self.bindedfuncs:
|
| 215 |
+
self.bindedfuncs[triplet[2]] = [[] for s in _states]
|
| 216 |
+
for s in _states:
|
| 217 |
+
lists = [ self.bindedfuncs[detail][i]
|
| 218 |
+
for detail in (triplet[2], None)
|
| 219 |
+
for i in _state_subsets[s] ]
|
| 220 |
+
handler = self.__create_handler(lists, self.type,
|
| 221 |
+
_state_codes[s])
|
| 222 |
+
seq = "<%s%s-%s>"% (_state_names[s], self.typename, triplet[2])
|
| 223 |
+
self.handlerids.append((seq, self.widget.bind(self.widgetinst,
|
| 224 |
+
seq, handler)))
|
| 225 |
+
doit = lambda: self.bindedfuncs[triplet[2]][triplet[0]].append(func)
|
| 226 |
+
if not self.ishandlerrunning:
|
| 227 |
+
doit()
|
| 228 |
+
else:
|
| 229 |
+
self.doafterhandler.append(doit)
|
| 230 |
+
|
| 231 |
+
def unbind(self, triplet, func):
|
| 232 |
+
doit = lambda: self.bindedfuncs[triplet[2]][triplet[0]].remove(func)
|
| 233 |
+
if not self.ishandlerrunning:
|
| 234 |
+
doit()
|
| 235 |
+
else:
|
| 236 |
+
self.doafterhandler.append(doit)
|
| 237 |
+
|
| 238 |
+
def __del__(self):
|
| 239 |
+
for seq, id in self.handlerids:
|
| 240 |
+
try:
|
| 241 |
+
self.widget.unbind(self.widgetinst, seq, id)
|
| 242 |
+
except tkinter.TclError as e:
|
| 243 |
+
if not APPLICATION_GONE in e.args[0]:
|
| 244 |
+
raise
|
| 245 |
+
|
| 246 |
+
# define the list of event types to be handled by MultiEvent. the order is
|
| 247 |
+
# compatible with the definition of event type constants.
|
| 248 |
+
_types = (
|
| 249 |
+
("KeyPress", "Key"), ("KeyRelease",), ("ButtonPress", "Button"),
|
| 250 |
+
("ButtonRelease",), ("Activate",), ("Circulate",), ("Colormap",),
|
| 251 |
+
("Configure",), ("Deactivate",), ("Destroy",), ("Enter",), ("Expose",),
|
| 252 |
+
("FocusIn",), ("FocusOut",), ("Gravity",), ("Leave",), ("Map",),
|
| 253 |
+
("Motion",), ("MouseWheel",), ("Property",), ("Reparent",), ("Unmap",),
|
| 254 |
+
("Visibility",),
|
| 255 |
+
)
|
| 256 |
+
|
| 257 |
+
# which binder should be used for every event type?
|
| 258 |
+
_binder_classes = (_ComplexBinder,) * 4 + (_SimpleBinder,) * (len(_types)-4)
|
| 259 |
+
|
| 260 |
+
# A dictionary to map a type name into its number
|
| 261 |
+
_type_names = dict([(name, number)
|
| 262 |
+
for number in range(len(_types))
|
| 263 |
+
for name in _types[number]])
|
| 264 |
+
|
| 265 |
+
_keysym_re = re.compile(r"^\w+$")
|
| 266 |
+
_button_re = re.compile(r"^[1-5]$")
|
| 267 |
+
def _parse_sequence(sequence):
|
| 268 |
+
"""Get a string which should describe an event sequence. If it is
|
| 269 |
+
successfully parsed as one, return a tuple containing the state (as an int),
|
| 270 |
+
the event type (as an index of _types), and the detail - None if none, or a
|
| 271 |
+
string if there is one. If the parsing is unsuccessful, return None.
|
| 272 |
+
"""
|
| 273 |
+
if not sequence or sequence[0] != '<' or sequence[-1] != '>':
|
| 274 |
+
return None
|
| 275 |
+
words = sequence[1:-1].split('-')
|
| 276 |
+
modifiers = 0
|
| 277 |
+
while words and words[0] in _modifier_names:
|
| 278 |
+
modifiers |= 1 << _modifier_names[words[0]]
|
| 279 |
+
del words[0]
|
| 280 |
+
if words and words[0] in _type_names:
|
| 281 |
+
type = _type_names[words[0]]
|
| 282 |
+
del words[0]
|
| 283 |
+
else:
|
| 284 |
+
return None
|
| 285 |
+
if _binder_classes[type] is _SimpleBinder:
|
| 286 |
+
if modifiers or words:
|
| 287 |
+
return None
|
| 288 |
+
else:
|
| 289 |
+
detail = None
|
| 290 |
+
else:
|
| 291 |
+
# _ComplexBinder
|
| 292 |
+
if type in [_type_names[s] for s in ("KeyPress", "KeyRelease")]:
|
| 293 |
+
type_re = _keysym_re
|
| 294 |
+
else:
|
| 295 |
+
type_re = _button_re
|
| 296 |
+
|
| 297 |
+
if not words:
|
| 298 |
+
detail = None
|
| 299 |
+
elif len(words) == 1 and type_re.match(words[0]):
|
| 300 |
+
detail = words[0]
|
| 301 |
+
else:
|
| 302 |
+
return None
|
| 303 |
+
|
| 304 |
+
return modifiers, type, detail
|
| 305 |
+
|
| 306 |
+
def _triplet_to_sequence(triplet):
|
| 307 |
+
if triplet[2]:
|
| 308 |
+
return '<'+_state_names[triplet[0]]+_types[triplet[1]][0]+'-'+ \
|
| 309 |
+
triplet[2]+'>'
|
| 310 |
+
else:
|
| 311 |
+
return '<'+_state_names[triplet[0]]+_types[triplet[1]][0]+'>'
|
| 312 |
+
|
| 313 |
+
_multicall_dict = {}
|
| 314 |
+
def MultiCallCreator(widget):
|
| 315 |
+
"""Return a MultiCall class which inherits its methods from the
|
| 316 |
+
given widget class (for example, Tkinter.Text). This is used
|
| 317 |
+
instead of a templating mechanism.
|
| 318 |
+
"""
|
| 319 |
+
if widget in _multicall_dict:
|
| 320 |
+
return _multicall_dict[widget]
|
| 321 |
+
|
| 322 |
+
class MultiCall (widget):
|
| 323 |
+
assert issubclass(widget, tkinter.Misc)
|
| 324 |
+
|
| 325 |
+
def __init__(self, *args, **kwargs):
|
| 326 |
+
widget.__init__(self, *args, **kwargs)
|
| 327 |
+
# a dictionary which maps a virtual event to a tuple with:
|
| 328 |
+
# 0. the function binded
|
| 329 |
+
# 1. a list of triplets - the sequences it is binded to
|
| 330 |
+
self.__eventinfo = {}
|
| 331 |
+
self.__binders = [_binder_classes[i](i, widget, self)
|
| 332 |
+
for i in range(len(_types))]
|
| 333 |
+
|
| 334 |
+
def bind(self, sequence=None, func=None, add=None):
|
| 335 |
+
#print("bind(%s, %s, %s)" % (sequence, func, add),
|
| 336 |
+
# file=sys.__stderr__)
|
| 337 |
+
if type(sequence) is str and len(sequence) > 2 and \
|
| 338 |
+
sequence[:2] == "<<" and sequence[-2:] == ">>":
|
| 339 |
+
if sequence in self.__eventinfo:
|
| 340 |
+
ei = self.__eventinfo[sequence]
|
| 341 |
+
if ei[0] is not None:
|
| 342 |
+
for triplet in ei[1]:
|
| 343 |
+
self.__binders[triplet[1]].unbind(triplet, ei[0])
|
| 344 |
+
ei[0] = func
|
| 345 |
+
if ei[0] is not None:
|
| 346 |
+
for triplet in ei[1]:
|
| 347 |
+
self.__binders[triplet[1]].bind(triplet, func)
|
| 348 |
+
else:
|
| 349 |
+
self.__eventinfo[sequence] = [func, []]
|
| 350 |
+
return widget.bind(self, sequence, func, add)
|
| 351 |
+
|
| 352 |
+
def unbind(self, sequence, funcid=None):
|
| 353 |
+
if type(sequence) is str and len(sequence) > 2 and \
|
| 354 |
+
sequence[:2] == "<<" and sequence[-2:] == ">>" and \
|
| 355 |
+
sequence in self.__eventinfo:
|
| 356 |
+
func, triplets = self.__eventinfo[sequence]
|
| 357 |
+
if func is not None:
|
| 358 |
+
for triplet in triplets:
|
| 359 |
+
self.__binders[triplet[1]].unbind(triplet, func)
|
| 360 |
+
self.__eventinfo[sequence][0] = None
|
| 361 |
+
return widget.unbind(self, sequence, funcid)
|
| 362 |
+
|
| 363 |
+
def event_add(self, virtual, *sequences):
|
| 364 |
+
#print("event_add(%s, %s)" % (repr(virtual), repr(sequences)),
|
| 365 |
+
# file=sys.__stderr__)
|
| 366 |
+
if virtual not in self.__eventinfo:
|
| 367 |
+
self.__eventinfo[virtual] = [None, []]
|
| 368 |
+
|
| 369 |
+
func, triplets = self.__eventinfo[virtual]
|
| 370 |
+
for seq in sequences:
|
| 371 |
+
triplet = _parse_sequence(seq)
|
| 372 |
+
if triplet is None:
|
| 373 |
+
#print("Tkinter event_add(%s)" % seq, file=sys.__stderr__)
|
| 374 |
+
widget.event_add(self, virtual, seq)
|
| 375 |
+
else:
|
| 376 |
+
if func is not None:
|
| 377 |
+
self.__binders[triplet[1]].bind(triplet, func)
|
| 378 |
+
triplets.append(triplet)
|
| 379 |
+
|
| 380 |
+
def event_delete(self, virtual, *sequences):
|
| 381 |
+
if virtual not in self.__eventinfo:
|
| 382 |
+
return
|
| 383 |
+
func, triplets = self.__eventinfo[virtual]
|
| 384 |
+
for seq in sequences:
|
| 385 |
+
triplet = _parse_sequence(seq)
|
| 386 |
+
if triplet is None:
|
| 387 |
+
#print("Tkinter event_delete: %s" % seq, file=sys.__stderr__)
|
| 388 |
+
widget.event_delete(self, virtual, seq)
|
| 389 |
+
else:
|
| 390 |
+
if func is not None:
|
| 391 |
+
self.__binders[triplet[1]].unbind(triplet, func)
|
| 392 |
+
triplets.remove(triplet)
|
| 393 |
+
|
| 394 |
+
def event_info(self, virtual=None):
|
| 395 |
+
if virtual is None or virtual not in self.__eventinfo:
|
| 396 |
+
return widget.event_info(self, virtual)
|
| 397 |
+
else:
|
| 398 |
+
return tuple(map(_triplet_to_sequence,
|
| 399 |
+
self.__eventinfo[virtual][1])) + \
|
| 400 |
+
widget.event_info(self, virtual)
|
| 401 |
+
|
| 402 |
+
def __del__(self):
|
| 403 |
+
for virtual in self.__eventinfo:
|
| 404 |
+
func, triplets = self.__eventinfo[virtual]
|
| 405 |
+
if func:
|
| 406 |
+
for triplet in triplets:
|
| 407 |
+
try:
|
| 408 |
+
self.__binders[triplet[1]].unbind(triplet, func)
|
| 409 |
+
except tkinter.TclError as e:
|
| 410 |
+
if not APPLICATION_GONE in e.args[0]:
|
| 411 |
+
raise
|
| 412 |
+
|
| 413 |
+
_multicall_dict[widget] = MultiCall
|
| 414 |
+
return MultiCall
|
| 415 |
+
|
| 416 |
+
|
| 417 |
+
def _multi_call(parent): # htest #
|
| 418 |
+
top = tkinter.Toplevel(parent)
|
| 419 |
+
top.title("Test MultiCall")
|
| 420 |
+
x, y = map(int, parent.geometry().split('+')[1:])
|
| 421 |
+
top.geometry("+%d+%d" % (x, y + 175))
|
| 422 |
+
text = MultiCallCreator(tkinter.Text)(top)
|
| 423 |
+
text.pack()
|
| 424 |
+
def bindseq(seq, n=[0]):
|
| 425 |
+
def handler(event):
|
| 426 |
+
print(seq)
|
| 427 |
+
text.bind("<<handler%d>>"%n[0], handler)
|
| 428 |
+
text.event_add("<<handler%d>>"%n[0], seq)
|
| 429 |
+
n[0] += 1
|
| 430 |
+
bindseq("<Key>")
|
| 431 |
+
bindseq("<Control-Key>")
|
| 432 |
+
bindseq("<Alt-Key-a>")
|
| 433 |
+
bindseq("<Control-Key-a>")
|
| 434 |
+
bindseq("<Alt-Control-Key-a>")
|
| 435 |
+
bindseq("<Key-b>")
|
| 436 |
+
bindseq("<Control-Button-1>")
|
| 437 |
+
bindseq("<Button-2>")
|
| 438 |
+
bindseq("<Alt-Button-1>")
|
| 439 |
+
bindseq("<FocusOut>")
|
| 440 |
+
bindseq("<Enter>")
|
| 441 |
+
bindseq("<Leave>")
|
| 442 |
+
|
| 443 |
+
if __name__ == "__main__":
|
| 444 |
+
from unittest import main
|
| 445 |
+
main('idlelib.idle_test.test_mainmenu', verbosity=2, exit=False)
|
| 446 |
+
|
| 447 |
+
from idlelib.idle_test.htest import run
|
| 448 |
+
run(_multi_call)
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/outwin.py
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Editor window that can serve as an output file.
|
| 2 |
+
"""
|
| 3 |
+
|
| 4 |
+
import re
|
| 5 |
+
|
| 6 |
+
from tkinter import messagebox
|
| 7 |
+
|
| 8 |
+
from idlelib.editor import EditorWindow
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
file_line_pats = [
|
| 12 |
+
# order of patterns matters
|
| 13 |
+
r'file "([^"]*)", line (\d+)',
|
| 14 |
+
r'([^\s]+)\((\d+)\)',
|
| 15 |
+
r'^(\s*\S.*?):\s*(\d+):', # Win filename, maybe starting with spaces
|
| 16 |
+
r'([^\s]+):\s*(\d+):', # filename or path, ltrim
|
| 17 |
+
r'^\s*(\S.*?):\s*(\d+):', # Win abs path with embedded spaces, ltrim
|
| 18 |
+
]
|
| 19 |
+
|
| 20 |
+
file_line_progs = None
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def compile_progs():
|
| 24 |
+
"Compile the patterns for matching to file name and line number."
|
| 25 |
+
global file_line_progs
|
| 26 |
+
file_line_progs = [re.compile(pat, re.IGNORECASE)
|
| 27 |
+
for pat in file_line_pats]
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
def file_line_helper(line):
|
| 31 |
+
"""Extract file name and line number from line of text.
|
| 32 |
+
|
| 33 |
+
Check if line of text contains one of the file/line patterns.
|
| 34 |
+
If it does and if the file and line are valid, return
|
| 35 |
+
a tuple of the file name and line number. If it doesn't match
|
| 36 |
+
or if the file or line is invalid, return None.
|
| 37 |
+
"""
|
| 38 |
+
if not file_line_progs:
|
| 39 |
+
compile_progs()
|
| 40 |
+
for prog in file_line_progs:
|
| 41 |
+
match = prog.search(line)
|
| 42 |
+
if match:
|
| 43 |
+
filename, lineno = match.group(1, 2)
|
| 44 |
+
try:
|
| 45 |
+
f = open(filename, "r")
|
| 46 |
+
f.close()
|
| 47 |
+
break
|
| 48 |
+
except OSError:
|
| 49 |
+
continue
|
| 50 |
+
else:
|
| 51 |
+
return None
|
| 52 |
+
try:
|
| 53 |
+
return filename, int(lineno)
|
| 54 |
+
except TypeError:
|
| 55 |
+
return None
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
class OutputWindow(EditorWindow):
|
| 59 |
+
"""An editor window that can serve as an output file.
|
| 60 |
+
|
| 61 |
+
Also the future base class for the Python shell window.
|
| 62 |
+
This class has no input facilities.
|
| 63 |
+
|
| 64 |
+
Adds binding to open a file at a line to the text widget.
|
| 65 |
+
"""
|
| 66 |
+
|
| 67 |
+
# Our own right-button menu
|
| 68 |
+
rmenu_specs = [
|
| 69 |
+
("Cut", "<<cut>>", "rmenu_check_cut"),
|
| 70 |
+
("Copy", "<<copy>>", "rmenu_check_copy"),
|
| 71 |
+
("Paste", "<<paste>>", "rmenu_check_paste"),
|
| 72 |
+
(None, None, None),
|
| 73 |
+
("Go to file/line", "<<goto-file-line>>", None),
|
| 74 |
+
]
|
| 75 |
+
|
| 76 |
+
allow_code_context = False
|
| 77 |
+
|
| 78 |
+
def __init__(self, *args):
|
| 79 |
+
EditorWindow.__init__(self, *args)
|
| 80 |
+
self.text.bind("<<goto-file-line>>", self.goto_file_line)
|
| 81 |
+
|
| 82 |
+
# Customize EditorWindow
|
| 83 |
+
def ispythonsource(self, filename):
|
| 84 |
+
"Python source is only part of output: do not colorize."
|
| 85 |
+
return False
|
| 86 |
+
|
| 87 |
+
def short_title(self):
|
| 88 |
+
"Customize EditorWindow title."
|
| 89 |
+
return "Output"
|
| 90 |
+
|
| 91 |
+
def maybesave(self):
|
| 92 |
+
"Customize EditorWindow to not display save file messagebox."
|
| 93 |
+
return 'yes' if self.get_saved() else 'no'
|
| 94 |
+
|
| 95 |
+
# Act as output file
|
| 96 |
+
def write(self, s, tags=(), mark="insert"):
|
| 97 |
+
"""Write text to text widget.
|
| 98 |
+
|
| 99 |
+
The text is inserted at the given index with the provided
|
| 100 |
+
tags. The text widget is then scrolled to make it visible
|
| 101 |
+
and updated to display it, giving the effect of seeing each
|
| 102 |
+
line as it is added.
|
| 103 |
+
|
| 104 |
+
Args:
|
| 105 |
+
s: Text to insert into text widget.
|
| 106 |
+
tags: Tuple of tag strings to apply on the insert.
|
| 107 |
+
mark: Index for the insert.
|
| 108 |
+
|
| 109 |
+
Return:
|
| 110 |
+
Length of text inserted.
|
| 111 |
+
"""
|
| 112 |
+
assert isinstance(s, str)
|
| 113 |
+
self.text.insert(mark, s, tags)
|
| 114 |
+
self.text.see(mark)
|
| 115 |
+
self.text.update()
|
| 116 |
+
return len(s)
|
| 117 |
+
|
| 118 |
+
def writelines(self, lines):
|
| 119 |
+
"Write each item in lines iterable."
|
| 120 |
+
for line in lines:
|
| 121 |
+
self.write(line)
|
| 122 |
+
|
| 123 |
+
def flush(self):
|
| 124 |
+
"No flushing needed as write() directly writes to widget."
|
| 125 |
+
pass
|
| 126 |
+
|
| 127 |
+
def showerror(self, *args, **kwargs):
|
| 128 |
+
messagebox.showerror(*args, **kwargs)
|
| 129 |
+
|
| 130 |
+
def goto_file_line(self, event=None):
|
| 131 |
+
"""Handle request to open file/line.
|
| 132 |
+
|
| 133 |
+
If the selected or previous line in the output window
|
| 134 |
+
contains a file name and line number, then open that file
|
| 135 |
+
name in a new window and position on the line number.
|
| 136 |
+
|
| 137 |
+
Otherwise, display an error messagebox.
|
| 138 |
+
"""
|
| 139 |
+
line = self.text.get("insert linestart", "insert lineend")
|
| 140 |
+
result = file_line_helper(line)
|
| 141 |
+
if not result:
|
| 142 |
+
# Try the previous line. This is handy e.g. in tracebacks,
|
| 143 |
+
# where you tend to right-click on the displayed source line
|
| 144 |
+
line = self.text.get("insert -1line linestart",
|
| 145 |
+
"insert -1line lineend")
|
| 146 |
+
result = file_line_helper(line)
|
| 147 |
+
if not result:
|
| 148 |
+
self.showerror(
|
| 149 |
+
"No special line",
|
| 150 |
+
"The line you point at doesn't look like "
|
| 151 |
+
"a valid file name followed by a line number.",
|
| 152 |
+
parent=self.text)
|
| 153 |
+
return
|
| 154 |
+
filename, lineno = result
|
| 155 |
+
self.flist.gotofileline(filename, lineno)
|
| 156 |
+
|
| 157 |
+
|
| 158 |
+
# These classes are currently not used but might come in handy
|
| 159 |
+
class OnDemandOutputWindow:
|
| 160 |
+
|
| 161 |
+
tagdefs = {
|
| 162 |
+
# XXX Should use IdlePrefs.ColorPrefs
|
| 163 |
+
"stdout": {"foreground": "blue"},
|
| 164 |
+
"stderr": {"foreground": "#007700"},
|
| 165 |
+
}
|
| 166 |
+
|
| 167 |
+
def __init__(self, flist):
|
| 168 |
+
self.flist = flist
|
| 169 |
+
self.owin = None
|
| 170 |
+
|
| 171 |
+
def write(self, s, tags, mark):
|
| 172 |
+
if not self.owin:
|
| 173 |
+
self.setup()
|
| 174 |
+
self.owin.write(s, tags, mark)
|
| 175 |
+
|
| 176 |
+
def setup(self):
|
| 177 |
+
self.owin = owin = OutputWindow(self.flist)
|
| 178 |
+
text = owin.text
|
| 179 |
+
for tag, cnf in self.tagdefs.items():
|
| 180 |
+
if cnf:
|
| 181 |
+
text.tag_configure(tag, **cnf)
|
| 182 |
+
text.tag_raise('sel')
|
| 183 |
+
self.write = self.owin.write
|
| 184 |
+
|
| 185 |
+
if __name__ == '__main__':
|
| 186 |
+
from unittest import main
|
| 187 |
+
main('idlelib.idle_test.test_outwin', verbosity=2, exit=False)
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/parenmatch.py
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""ParenMatch -- for parenthesis matching.
|
| 2 |
+
|
| 3 |
+
When you hit a right paren, the cursor should move briefly to the left
|
| 4 |
+
paren. Paren here is used generically; the matching applies to
|
| 5 |
+
parentheses, square brackets, and curly braces.
|
| 6 |
+
"""
|
| 7 |
+
from idlelib.hyperparser import HyperParser
|
| 8 |
+
from idlelib.config import idleConf
|
| 9 |
+
|
| 10 |
+
_openers = {')':'(',']':'[','}':'{'}
|
| 11 |
+
CHECK_DELAY = 100 # milliseconds
|
| 12 |
+
|
| 13 |
+
class ParenMatch:
|
| 14 |
+
"""Highlight matching openers and closers, (), [], and {}.
|
| 15 |
+
|
| 16 |
+
There are three supported styles of paren matching. When a right
|
| 17 |
+
paren (opener) is typed:
|
| 18 |
+
|
| 19 |
+
opener -- highlight the matching left paren (closer);
|
| 20 |
+
parens -- highlight the left and right parens (opener and closer);
|
| 21 |
+
expression -- highlight the entire expression from opener to closer.
|
| 22 |
+
(For back compatibility, 'default' is a synonym for 'opener').
|
| 23 |
+
|
| 24 |
+
Flash-delay is the maximum milliseconds the highlighting remains.
|
| 25 |
+
Any cursor movement (key press or click) before that removes the
|
| 26 |
+
highlight. If flash-delay is 0, there is no maximum.
|
| 27 |
+
|
| 28 |
+
TODO:
|
| 29 |
+
- Augment bell() with mismatch warning in status window.
|
| 30 |
+
- Highlight when cursor is moved to the right of a closer.
|
| 31 |
+
This might be too expensive to check.
|
| 32 |
+
"""
|
| 33 |
+
|
| 34 |
+
RESTORE_VIRTUAL_EVENT_NAME = "<<parenmatch-check-restore>>"
|
| 35 |
+
# We want the restore event be called before the usual return and
|
| 36 |
+
# backspace events.
|
| 37 |
+
RESTORE_SEQUENCES = ("<KeyPress>", "<ButtonPress>",
|
| 38 |
+
"<Key-Return>", "<Key-BackSpace>")
|
| 39 |
+
|
| 40 |
+
def __init__(self, editwin):
|
| 41 |
+
self.editwin = editwin
|
| 42 |
+
self.text = editwin.text
|
| 43 |
+
# Bind the check-restore event to the function restore_event,
|
| 44 |
+
# so that we can then use activate_restore (which calls event_add)
|
| 45 |
+
# and deactivate_restore (which calls event_delete).
|
| 46 |
+
editwin.text.bind(self.RESTORE_VIRTUAL_EVENT_NAME,
|
| 47 |
+
self.restore_event)
|
| 48 |
+
self.counter = 0
|
| 49 |
+
self.is_restore_active = 0
|
| 50 |
+
|
| 51 |
+
@classmethod
|
| 52 |
+
def reload(cls):
|
| 53 |
+
cls.STYLE = idleConf.GetOption(
|
| 54 |
+
'extensions','ParenMatch','style', default='opener')
|
| 55 |
+
cls.FLASH_DELAY = idleConf.GetOption(
|
| 56 |
+
'extensions','ParenMatch','flash-delay', type='int',default=500)
|
| 57 |
+
cls.BELL = idleConf.GetOption(
|
| 58 |
+
'extensions','ParenMatch','bell', type='bool', default=1)
|
| 59 |
+
cls.HILITE_CONFIG = idleConf.GetHighlight(idleConf.CurrentTheme(),
|
| 60 |
+
'hilite')
|
| 61 |
+
|
| 62 |
+
def activate_restore(self):
|
| 63 |
+
"Activate mechanism to restore text from highlighting."
|
| 64 |
+
if not self.is_restore_active:
|
| 65 |
+
for seq in self.RESTORE_SEQUENCES:
|
| 66 |
+
self.text.event_add(self.RESTORE_VIRTUAL_EVENT_NAME, seq)
|
| 67 |
+
self.is_restore_active = True
|
| 68 |
+
|
| 69 |
+
def deactivate_restore(self):
|
| 70 |
+
"Remove restore event bindings."
|
| 71 |
+
if self.is_restore_active:
|
| 72 |
+
for seq in self.RESTORE_SEQUENCES:
|
| 73 |
+
self.text.event_delete(self.RESTORE_VIRTUAL_EVENT_NAME, seq)
|
| 74 |
+
self.is_restore_active = False
|
| 75 |
+
|
| 76 |
+
def flash_paren_event(self, event):
|
| 77 |
+
"Handle editor 'show surrounding parens' event (menu or shortcut)."
|
| 78 |
+
indices = (HyperParser(self.editwin, "insert")
|
| 79 |
+
.get_surrounding_brackets())
|
| 80 |
+
self.finish_paren_event(indices)
|
| 81 |
+
return "break"
|
| 82 |
+
|
| 83 |
+
def paren_closed_event(self, event):
|
| 84 |
+
"Handle user input of closer."
|
| 85 |
+
# If user bound non-closer to <<paren-closed>>, quit.
|
| 86 |
+
closer = self.text.get("insert-1c")
|
| 87 |
+
if closer not in _openers:
|
| 88 |
+
return
|
| 89 |
+
hp = HyperParser(self.editwin, "insert-1c")
|
| 90 |
+
if not hp.is_in_code():
|
| 91 |
+
return
|
| 92 |
+
indices = hp.get_surrounding_brackets(_openers[closer], True)
|
| 93 |
+
self.finish_paren_event(indices)
|
| 94 |
+
return # Allow calltips to see ')'
|
| 95 |
+
|
| 96 |
+
def finish_paren_event(self, indices):
|
| 97 |
+
if indices is None and self.BELL:
|
| 98 |
+
self.text.bell()
|
| 99 |
+
return
|
| 100 |
+
self.activate_restore()
|
| 101 |
+
# self.create_tag(indices)
|
| 102 |
+
self.tagfuncs.get(self.STYLE, self.create_tag_expression)(self, indices)
|
| 103 |
+
# self.set_timeout()
|
| 104 |
+
(self.set_timeout_last if self.FLASH_DELAY else
|
| 105 |
+
self.set_timeout_none)()
|
| 106 |
+
|
| 107 |
+
def restore_event(self, event=None):
|
| 108 |
+
"Remove effect of doing match."
|
| 109 |
+
self.text.tag_delete("paren")
|
| 110 |
+
self.deactivate_restore()
|
| 111 |
+
self.counter += 1 # disable the last timer, if there is one.
|
| 112 |
+
|
| 113 |
+
def handle_restore_timer(self, timer_count):
|
| 114 |
+
if timer_count == self.counter:
|
| 115 |
+
self.restore_event()
|
| 116 |
+
|
| 117 |
+
# any one of the create_tag_XXX methods can be used depending on
|
| 118 |
+
# the style
|
| 119 |
+
|
| 120 |
+
def create_tag_opener(self, indices):
|
| 121 |
+
"""Highlight the single paren that matches"""
|
| 122 |
+
self.text.tag_add("paren", indices[0])
|
| 123 |
+
self.text.tag_config("paren", self.HILITE_CONFIG)
|
| 124 |
+
|
| 125 |
+
def create_tag_parens(self, indices):
|
| 126 |
+
"""Highlight the left and right parens"""
|
| 127 |
+
if self.text.get(indices[1]) in (')', ']', '}'):
|
| 128 |
+
rightindex = indices[1]+"+1c"
|
| 129 |
+
else:
|
| 130 |
+
rightindex = indices[1]
|
| 131 |
+
self.text.tag_add("paren", indices[0], indices[0]+"+1c", rightindex+"-1c", rightindex)
|
| 132 |
+
self.text.tag_config("paren", self.HILITE_CONFIG)
|
| 133 |
+
|
| 134 |
+
def create_tag_expression(self, indices):
|
| 135 |
+
"""Highlight the entire expression"""
|
| 136 |
+
if self.text.get(indices[1]) in (')', ']', '}'):
|
| 137 |
+
rightindex = indices[1]+"+1c"
|
| 138 |
+
else:
|
| 139 |
+
rightindex = indices[1]
|
| 140 |
+
self.text.tag_add("paren", indices[0], rightindex)
|
| 141 |
+
self.text.tag_config("paren", self.HILITE_CONFIG)
|
| 142 |
+
|
| 143 |
+
tagfuncs = {
|
| 144 |
+
'opener': create_tag_opener,
|
| 145 |
+
'default': create_tag_opener,
|
| 146 |
+
'parens': create_tag_parens,
|
| 147 |
+
'expression': create_tag_expression,
|
| 148 |
+
}
|
| 149 |
+
|
| 150 |
+
# any one of the set_timeout_XXX methods can be used depending on
|
| 151 |
+
# the style
|
| 152 |
+
|
| 153 |
+
def set_timeout_none(self):
|
| 154 |
+
"""Highlight will remain until user input turns it off
|
| 155 |
+
or the insert has moved"""
|
| 156 |
+
# After CHECK_DELAY, call a function which disables the "paren" tag
|
| 157 |
+
# if the event is for the most recent timer and the insert has changed,
|
| 158 |
+
# or schedules another call for itself.
|
| 159 |
+
self.counter += 1
|
| 160 |
+
def callme(callme, self=self, c=self.counter,
|
| 161 |
+
index=self.text.index("insert")):
|
| 162 |
+
if index != self.text.index("insert"):
|
| 163 |
+
self.handle_restore_timer(c)
|
| 164 |
+
else:
|
| 165 |
+
self.editwin.text_frame.after(CHECK_DELAY, callme, callme)
|
| 166 |
+
self.editwin.text_frame.after(CHECK_DELAY, callme, callme)
|
| 167 |
+
|
| 168 |
+
def set_timeout_last(self):
|
| 169 |
+
"""The last highlight created will be removed after FLASH_DELAY millisecs"""
|
| 170 |
+
# associate a counter with an event; only disable the "paren"
|
| 171 |
+
# tag if the event is for the most recent timer.
|
| 172 |
+
self.counter += 1
|
| 173 |
+
self.editwin.text_frame.after(
|
| 174 |
+
self.FLASH_DELAY,
|
| 175 |
+
lambda self=self, c=self.counter: self.handle_restore_timer(c))
|
| 176 |
+
|
| 177 |
+
|
| 178 |
+
ParenMatch.reload()
|
| 179 |
+
|
| 180 |
+
|
| 181 |
+
if __name__ == '__main__':
|
| 182 |
+
from unittest import main
|
| 183 |
+
main('idlelib.idle_test.test_parenmatch', verbosity=2)
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/pathbrowser.py
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import importlib.machinery
|
| 2 |
+
import os
|
| 3 |
+
import sys
|
| 4 |
+
|
| 5 |
+
from idlelib.browser import ModuleBrowser, ModuleBrowserTreeItem
|
| 6 |
+
from idlelib.tree import TreeItem
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class PathBrowser(ModuleBrowser):
|
| 10 |
+
|
| 11 |
+
def __init__(self, master, *, _htest=False, _utest=False):
|
| 12 |
+
"""
|
| 13 |
+
_htest - bool, change box location when running htest
|
| 14 |
+
"""
|
| 15 |
+
self.master = master
|
| 16 |
+
self._htest = _htest
|
| 17 |
+
self._utest = _utest
|
| 18 |
+
self.init()
|
| 19 |
+
|
| 20 |
+
def settitle(self):
|
| 21 |
+
"Set window titles."
|
| 22 |
+
self.top.wm_title("Path Browser")
|
| 23 |
+
self.top.wm_iconname("Path Browser")
|
| 24 |
+
|
| 25 |
+
def rootnode(self):
|
| 26 |
+
return PathBrowserTreeItem()
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
class PathBrowserTreeItem(TreeItem):
|
| 30 |
+
|
| 31 |
+
def GetText(self):
|
| 32 |
+
return "sys.path"
|
| 33 |
+
|
| 34 |
+
def GetSubList(self):
|
| 35 |
+
sublist = []
|
| 36 |
+
for dir in sys.path:
|
| 37 |
+
item = DirBrowserTreeItem(dir)
|
| 38 |
+
sublist.append(item)
|
| 39 |
+
return sublist
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
class DirBrowserTreeItem(TreeItem):
|
| 43 |
+
|
| 44 |
+
def __init__(self, dir, packages=[]):
|
| 45 |
+
self.dir = dir
|
| 46 |
+
self.packages = packages
|
| 47 |
+
|
| 48 |
+
def GetText(self):
|
| 49 |
+
if not self.packages:
|
| 50 |
+
return self.dir
|
| 51 |
+
else:
|
| 52 |
+
return self.packages[-1] + ": package"
|
| 53 |
+
|
| 54 |
+
def GetSubList(self):
|
| 55 |
+
try:
|
| 56 |
+
names = os.listdir(self.dir or os.curdir)
|
| 57 |
+
except OSError:
|
| 58 |
+
return []
|
| 59 |
+
packages = []
|
| 60 |
+
for name in names:
|
| 61 |
+
file = os.path.join(self.dir, name)
|
| 62 |
+
if self.ispackagedir(file):
|
| 63 |
+
nn = os.path.normcase(name)
|
| 64 |
+
packages.append((nn, name, file))
|
| 65 |
+
packages.sort()
|
| 66 |
+
sublist = []
|
| 67 |
+
for nn, name, file in packages:
|
| 68 |
+
item = DirBrowserTreeItem(file, self.packages + [name])
|
| 69 |
+
sublist.append(item)
|
| 70 |
+
for nn, name in self.listmodules(names):
|
| 71 |
+
item = ModuleBrowserTreeItem(os.path.join(self.dir, name))
|
| 72 |
+
sublist.append(item)
|
| 73 |
+
return sublist
|
| 74 |
+
|
| 75 |
+
def ispackagedir(self, file):
|
| 76 |
+
" Return true for directories that are packages."
|
| 77 |
+
if not os.path.isdir(file):
|
| 78 |
+
return False
|
| 79 |
+
init = os.path.join(file, "__init__.py")
|
| 80 |
+
return os.path.exists(init)
|
| 81 |
+
|
| 82 |
+
def listmodules(self, allnames):
|
| 83 |
+
modules = {}
|
| 84 |
+
suffixes = importlib.machinery.EXTENSION_SUFFIXES[:]
|
| 85 |
+
suffixes += importlib.machinery.SOURCE_SUFFIXES
|
| 86 |
+
suffixes += importlib.machinery.BYTECODE_SUFFIXES
|
| 87 |
+
sorted = []
|
| 88 |
+
for suff in suffixes:
|
| 89 |
+
i = -len(suff)
|
| 90 |
+
for name in allnames[:]:
|
| 91 |
+
normed_name = os.path.normcase(name)
|
| 92 |
+
if normed_name[i:] == suff:
|
| 93 |
+
mod_name = name[:i]
|
| 94 |
+
if mod_name not in modules:
|
| 95 |
+
modules[mod_name] = None
|
| 96 |
+
sorted.append((normed_name, name))
|
| 97 |
+
allnames.remove(name)
|
| 98 |
+
sorted.sort()
|
| 99 |
+
return sorted
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
def _path_browser(parent): # htest #
|
| 103 |
+
PathBrowser(parent, _htest=True)
|
| 104 |
+
parent.mainloop()
|
| 105 |
+
|
| 106 |
+
if __name__ == "__main__":
|
| 107 |
+
from unittest import main
|
| 108 |
+
main('idlelib.idle_test.test_pathbrowser', verbosity=2, exit=False)
|
| 109 |
+
|
| 110 |
+
from idlelib.idle_test.htest import run
|
| 111 |
+
run(_path_browser)
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/pyparse.py
ADDED
|
@@ -0,0 +1,593 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Define partial Python code Parser used by editor and hyperparser.
|
| 2 |
+
|
| 3 |
+
Instances of ParseMap are used with str.translate.
|
| 4 |
+
|
| 5 |
+
The following bound search and match functions are defined:
|
| 6 |
+
_synchre - start of popular statement;
|
| 7 |
+
_junkre - whitespace or comment line;
|
| 8 |
+
_match_stringre: string, possibly without closer;
|
| 9 |
+
_itemre - line that may have bracket structure start;
|
| 10 |
+
_closere - line that must be followed by dedent.
|
| 11 |
+
_chew_ordinaryre - non-special characters.
|
| 12 |
+
"""
|
| 13 |
+
import re
|
| 14 |
+
|
| 15 |
+
# Reason last statement is continued (or C_NONE if it's not).
|
| 16 |
+
(C_NONE, C_BACKSLASH, C_STRING_FIRST_LINE,
|
| 17 |
+
C_STRING_NEXT_LINES, C_BRACKET) = range(5)
|
| 18 |
+
|
| 19 |
+
# Find what looks like the start of a popular statement.
|
| 20 |
+
|
| 21 |
+
_synchre = re.compile(r"""
|
| 22 |
+
^
|
| 23 |
+
[ \t]*
|
| 24 |
+
(?: while
|
| 25 |
+
| else
|
| 26 |
+
| def
|
| 27 |
+
| return
|
| 28 |
+
| assert
|
| 29 |
+
| break
|
| 30 |
+
| class
|
| 31 |
+
| continue
|
| 32 |
+
| elif
|
| 33 |
+
| try
|
| 34 |
+
| except
|
| 35 |
+
| raise
|
| 36 |
+
| import
|
| 37 |
+
| yield
|
| 38 |
+
)
|
| 39 |
+
\b
|
| 40 |
+
""", re.VERBOSE | re.MULTILINE).search
|
| 41 |
+
|
| 42 |
+
# Match blank line or non-indenting comment line.
|
| 43 |
+
|
| 44 |
+
_junkre = re.compile(r"""
|
| 45 |
+
[ \t]*
|
| 46 |
+
(?: \# \S .* )?
|
| 47 |
+
\n
|
| 48 |
+
""", re.VERBOSE).match
|
| 49 |
+
|
| 50 |
+
# Match any flavor of string; the terminating quote is optional
|
| 51 |
+
# so that we're robust in the face of incomplete program text.
|
| 52 |
+
|
| 53 |
+
_match_stringre = re.compile(r"""
|
| 54 |
+
\""" [^"\\]* (?:
|
| 55 |
+
(?: \\. | "(?!"") )
|
| 56 |
+
[^"\\]*
|
| 57 |
+
)*
|
| 58 |
+
(?: \""" )?
|
| 59 |
+
|
| 60 |
+
| " [^"\\\n]* (?: \\. [^"\\\n]* )* "?
|
| 61 |
+
|
| 62 |
+
| ''' [^'\\]* (?:
|
| 63 |
+
(?: \\. | '(?!'') )
|
| 64 |
+
[^'\\]*
|
| 65 |
+
)*
|
| 66 |
+
(?: ''' )?
|
| 67 |
+
|
| 68 |
+
| ' [^'\\\n]* (?: \\. [^'\\\n]* )* '?
|
| 69 |
+
""", re.VERBOSE | re.DOTALL).match
|
| 70 |
+
|
| 71 |
+
# Match a line that starts with something interesting;
|
| 72 |
+
# used to find the first item of a bracket structure.
|
| 73 |
+
|
| 74 |
+
_itemre = re.compile(r"""
|
| 75 |
+
[ \t]*
|
| 76 |
+
[^\s#\\] # if we match, m.end()-1 is the interesting char
|
| 77 |
+
""", re.VERBOSE).match
|
| 78 |
+
|
| 79 |
+
# Match start of statements that should be followed by a dedent.
|
| 80 |
+
|
| 81 |
+
_closere = re.compile(r"""
|
| 82 |
+
\s*
|
| 83 |
+
(?: return
|
| 84 |
+
| break
|
| 85 |
+
| continue
|
| 86 |
+
| raise
|
| 87 |
+
| pass
|
| 88 |
+
)
|
| 89 |
+
\b
|
| 90 |
+
""", re.VERBOSE).match
|
| 91 |
+
|
| 92 |
+
# Chew up non-special chars as quickly as possible. If match is
|
| 93 |
+
# successful, m.end() less 1 is the index of the last boring char
|
| 94 |
+
# matched. If match is unsuccessful, the string starts with an
|
| 95 |
+
# interesting char.
|
| 96 |
+
|
| 97 |
+
_chew_ordinaryre = re.compile(r"""
|
| 98 |
+
[^[\](){}#'"\\]+
|
| 99 |
+
""", re.VERBOSE).match
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
class ParseMap(dict):
|
| 103 |
+
r"""Dict subclass that maps anything not in dict to 'x'.
|
| 104 |
+
|
| 105 |
+
This is designed to be used with str.translate in study1.
|
| 106 |
+
Anything not specifically mapped otherwise becomes 'x'.
|
| 107 |
+
Example: replace everything except whitespace with 'x'.
|
| 108 |
+
|
| 109 |
+
>>> keepwhite = ParseMap((ord(c), ord(c)) for c in ' \t\n\r')
|
| 110 |
+
>>> "a + b\tc\nd".translate(keepwhite)
|
| 111 |
+
'x x x\tx\nx'
|
| 112 |
+
"""
|
| 113 |
+
# Calling this triples access time; see bpo-32940
|
| 114 |
+
def __missing__(self, key):
|
| 115 |
+
return 120 # ord('x')
|
| 116 |
+
|
| 117 |
+
|
| 118 |
+
# Map all ascii to 120 to avoid __missing__ call, then replace some.
|
| 119 |
+
trans = ParseMap.fromkeys(range(128), 120)
|
| 120 |
+
trans.update((ord(c), ord('(')) for c in "({[") # open brackets => '(';
|
| 121 |
+
trans.update((ord(c), ord(')')) for c in ")}]") # close brackets => ')'.
|
| 122 |
+
trans.update((ord(c), ord(c)) for c in "\"'\\\n#") # Keep these.
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
class Parser:
|
| 126 |
+
|
| 127 |
+
def __init__(self, indentwidth, tabwidth):
|
| 128 |
+
self.indentwidth = indentwidth
|
| 129 |
+
self.tabwidth = tabwidth
|
| 130 |
+
|
| 131 |
+
def set_code(self, s):
|
| 132 |
+
assert len(s) == 0 or s[-1] == '\n'
|
| 133 |
+
self.code = s
|
| 134 |
+
self.study_level = 0
|
| 135 |
+
|
| 136 |
+
def find_good_parse_start(self, is_char_in_string):
|
| 137 |
+
"""
|
| 138 |
+
Return index of a good place to begin parsing, as close to the
|
| 139 |
+
end of the string as possible. This will be the start of some
|
| 140 |
+
popular stmt like "if" or "def". Return None if none found:
|
| 141 |
+
the caller should pass more prior context then, if possible, or
|
| 142 |
+
if not (the entire program text up until the point of interest
|
| 143 |
+
has already been tried) pass 0 to set_lo().
|
| 144 |
+
|
| 145 |
+
This will be reliable iff given a reliable is_char_in_string()
|
| 146 |
+
function, meaning that when it says "no", it's absolutely
|
| 147 |
+
guaranteed that the char is not in a string.
|
| 148 |
+
"""
|
| 149 |
+
code, pos = self.code, None
|
| 150 |
+
|
| 151 |
+
# Peek back from the end for a good place to start,
|
| 152 |
+
# but don't try too often; pos will be left None, or
|
| 153 |
+
# bumped to a legitimate synch point.
|
| 154 |
+
limit = len(code)
|
| 155 |
+
for tries in range(5):
|
| 156 |
+
i = code.rfind(":\n", 0, limit)
|
| 157 |
+
if i < 0:
|
| 158 |
+
break
|
| 159 |
+
i = code.rfind('\n', 0, i) + 1 # start of colon line (-1+1=0)
|
| 160 |
+
m = _synchre(code, i, limit)
|
| 161 |
+
if m and not is_char_in_string(m.start()):
|
| 162 |
+
pos = m.start()
|
| 163 |
+
break
|
| 164 |
+
limit = i
|
| 165 |
+
if pos is None:
|
| 166 |
+
# Nothing looks like a block-opener, or stuff does
|
| 167 |
+
# but is_char_in_string keeps returning true; most likely
|
| 168 |
+
# we're in or near a giant string, the colorizer hasn't
|
| 169 |
+
# caught up enough to be helpful, or there simply *aren't*
|
| 170 |
+
# any interesting stmts. In any of these cases we're
|
| 171 |
+
# going to have to parse the whole thing to be sure, so
|
| 172 |
+
# give it one last try from the start, but stop wasting
|
| 173 |
+
# time here regardless of the outcome.
|
| 174 |
+
m = _synchre(code)
|
| 175 |
+
if m and not is_char_in_string(m.start()):
|
| 176 |
+
pos = m.start()
|
| 177 |
+
return pos
|
| 178 |
+
|
| 179 |
+
# Peeking back worked; look forward until _synchre no longer
|
| 180 |
+
# matches.
|
| 181 |
+
i = pos + 1
|
| 182 |
+
while 1:
|
| 183 |
+
m = _synchre(code, i)
|
| 184 |
+
if m:
|
| 185 |
+
s, i = m.span()
|
| 186 |
+
if not is_char_in_string(s):
|
| 187 |
+
pos = s
|
| 188 |
+
else:
|
| 189 |
+
break
|
| 190 |
+
return pos
|
| 191 |
+
|
| 192 |
+
def set_lo(self, lo):
|
| 193 |
+
""" Throw away the start of the string.
|
| 194 |
+
|
| 195 |
+
Intended to be called with the result of find_good_parse_start().
|
| 196 |
+
"""
|
| 197 |
+
assert lo == 0 or self.code[lo-1] == '\n'
|
| 198 |
+
if lo > 0:
|
| 199 |
+
self.code = self.code[lo:]
|
| 200 |
+
|
| 201 |
+
def _study1(self):
|
| 202 |
+
"""Find the line numbers of non-continuation lines.
|
| 203 |
+
|
| 204 |
+
As quickly as humanly possible <wink>, find the line numbers (0-
|
| 205 |
+
based) of the non-continuation lines.
|
| 206 |
+
Creates self.{goodlines, continuation}.
|
| 207 |
+
"""
|
| 208 |
+
if self.study_level >= 1:
|
| 209 |
+
return
|
| 210 |
+
self.study_level = 1
|
| 211 |
+
|
| 212 |
+
# Map all uninteresting characters to "x", all open brackets
|
| 213 |
+
# to "(", all close brackets to ")", then collapse runs of
|
| 214 |
+
# uninteresting characters. This can cut the number of chars
|
| 215 |
+
# by a factor of 10-40, and so greatly speed the following loop.
|
| 216 |
+
code = self.code
|
| 217 |
+
code = code.translate(trans)
|
| 218 |
+
code = code.replace('xxxxxxxx', 'x')
|
| 219 |
+
code = code.replace('xxxx', 'x')
|
| 220 |
+
code = code.replace('xx', 'x')
|
| 221 |
+
code = code.replace('xx', 'x')
|
| 222 |
+
code = code.replace('\nx', '\n')
|
| 223 |
+
# Replacing x\n with \n would be incorrect because
|
| 224 |
+
# x may be preceded by a backslash.
|
| 225 |
+
|
| 226 |
+
# March over the squashed version of the program, accumulating
|
| 227 |
+
# the line numbers of non-continued stmts, and determining
|
| 228 |
+
# whether & why the last stmt is a continuation.
|
| 229 |
+
continuation = C_NONE
|
| 230 |
+
level = lno = 0 # level is nesting level; lno is line number
|
| 231 |
+
self.goodlines = goodlines = [0]
|
| 232 |
+
push_good = goodlines.append
|
| 233 |
+
i, n = 0, len(code)
|
| 234 |
+
while i < n:
|
| 235 |
+
ch = code[i]
|
| 236 |
+
i = i+1
|
| 237 |
+
|
| 238 |
+
# cases are checked in decreasing order of frequency
|
| 239 |
+
if ch == 'x':
|
| 240 |
+
continue
|
| 241 |
+
|
| 242 |
+
if ch == '\n':
|
| 243 |
+
lno = lno + 1
|
| 244 |
+
if level == 0:
|
| 245 |
+
push_good(lno)
|
| 246 |
+
# else we're in an unclosed bracket structure
|
| 247 |
+
continue
|
| 248 |
+
|
| 249 |
+
if ch == '(':
|
| 250 |
+
level = level + 1
|
| 251 |
+
continue
|
| 252 |
+
|
| 253 |
+
if ch == ')':
|
| 254 |
+
if level:
|
| 255 |
+
level = level - 1
|
| 256 |
+
# else the program is invalid, but we can't complain
|
| 257 |
+
continue
|
| 258 |
+
|
| 259 |
+
if ch == '"' or ch == "'":
|
| 260 |
+
# consume the string
|
| 261 |
+
quote = ch
|
| 262 |
+
if code[i-1:i+2] == quote * 3:
|
| 263 |
+
quote = quote * 3
|
| 264 |
+
firstlno = lno
|
| 265 |
+
w = len(quote) - 1
|
| 266 |
+
i = i+w
|
| 267 |
+
while i < n:
|
| 268 |
+
ch = code[i]
|
| 269 |
+
i = i+1
|
| 270 |
+
|
| 271 |
+
if ch == 'x':
|
| 272 |
+
continue
|
| 273 |
+
|
| 274 |
+
if code[i-1:i+w] == quote:
|
| 275 |
+
i = i+w
|
| 276 |
+
break
|
| 277 |
+
|
| 278 |
+
if ch == '\n':
|
| 279 |
+
lno = lno + 1
|
| 280 |
+
if w == 0:
|
| 281 |
+
# unterminated single-quoted string
|
| 282 |
+
if level == 0:
|
| 283 |
+
push_good(lno)
|
| 284 |
+
break
|
| 285 |
+
continue
|
| 286 |
+
|
| 287 |
+
if ch == '\\':
|
| 288 |
+
assert i < n
|
| 289 |
+
if code[i] == '\n':
|
| 290 |
+
lno = lno + 1
|
| 291 |
+
i = i+1
|
| 292 |
+
continue
|
| 293 |
+
|
| 294 |
+
# else comment char or paren inside string
|
| 295 |
+
|
| 296 |
+
else:
|
| 297 |
+
# didn't break out of the loop, so we're still
|
| 298 |
+
# inside a string
|
| 299 |
+
if (lno - 1) == firstlno:
|
| 300 |
+
# before the previous \n in code, we were in the first
|
| 301 |
+
# line of the string
|
| 302 |
+
continuation = C_STRING_FIRST_LINE
|
| 303 |
+
else:
|
| 304 |
+
continuation = C_STRING_NEXT_LINES
|
| 305 |
+
continue # with outer loop
|
| 306 |
+
|
| 307 |
+
if ch == '#':
|
| 308 |
+
# consume the comment
|
| 309 |
+
i = code.find('\n', i)
|
| 310 |
+
assert i >= 0
|
| 311 |
+
continue
|
| 312 |
+
|
| 313 |
+
assert ch == '\\'
|
| 314 |
+
assert i < n
|
| 315 |
+
if code[i] == '\n':
|
| 316 |
+
lno = lno + 1
|
| 317 |
+
if i+1 == n:
|
| 318 |
+
continuation = C_BACKSLASH
|
| 319 |
+
i = i+1
|
| 320 |
+
|
| 321 |
+
# The last stmt may be continued for all 3 reasons.
|
| 322 |
+
# String continuation takes precedence over bracket
|
| 323 |
+
# continuation, which beats backslash continuation.
|
| 324 |
+
if (continuation != C_STRING_FIRST_LINE
|
| 325 |
+
and continuation != C_STRING_NEXT_LINES and level > 0):
|
| 326 |
+
continuation = C_BRACKET
|
| 327 |
+
self.continuation = continuation
|
| 328 |
+
|
| 329 |
+
# Push the final line number as a sentinel value, regardless of
|
| 330 |
+
# whether it's continued.
|
| 331 |
+
assert (continuation == C_NONE) == (goodlines[-1] == lno)
|
| 332 |
+
if goodlines[-1] != lno:
|
| 333 |
+
push_good(lno)
|
| 334 |
+
|
| 335 |
+
def get_continuation_type(self):
|
| 336 |
+
self._study1()
|
| 337 |
+
return self.continuation
|
| 338 |
+
|
| 339 |
+
def _study2(self):
|
| 340 |
+
"""
|
| 341 |
+
study1 was sufficient to determine the continuation status,
|
| 342 |
+
but doing more requires looking at every character. study2
|
| 343 |
+
does this for the last interesting statement in the block.
|
| 344 |
+
Creates:
|
| 345 |
+
self.stmt_start, stmt_end
|
| 346 |
+
slice indices of last interesting stmt
|
| 347 |
+
self.stmt_bracketing
|
| 348 |
+
the bracketing structure of the last interesting stmt; for
|
| 349 |
+
example, for the statement "say(boo) or die",
|
| 350 |
+
stmt_bracketing will be ((0, 0), (0, 1), (2, 0), (2, 1),
|
| 351 |
+
(4, 0)). Strings and comments are treated as brackets, for
|
| 352 |
+
the matter.
|
| 353 |
+
self.lastch
|
| 354 |
+
last interesting character before optional trailing comment
|
| 355 |
+
self.lastopenbracketpos
|
| 356 |
+
if continuation is C_BRACKET, index of last open bracket
|
| 357 |
+
"""
|
| 358 |
+
if self.study_level >= 2:
|
| 359 |
+
return
|
| 360 |
+
self._study1()
|
| 361 |
+
self.study_level = 2
|
| 362 |
+
|
| 363 |
+
# Set p and q to slice indices of last interesting stmt.
|
| 364 |
+
code, goodlines = self.code, self.goodlines
|
| 365 |
+
i = len(goodlines) - 1 # Index of newest line.
|
| 366 |
+
p = len(code) # End of goodlines[i]
|
| 367 |
+
while i:
|
| 368 |
+
assert p
|
| 369 |
+
# Make p be the index of the stmt at line number goodlines[i].
|
| 370 |
+
# Move p back to the stmt at line number goodlines[i-1].
|
| 371 |
+
q = p
|
| 372 |
+
for nothing in range(goodlines[i-1], goodlines[i]):
|
| 373 |
+
# tricky: sets p to 0 if no preceding newline
|
| 374 |
+
p = code.rfind('\n', 0, p-1) + 1
|
| 375 |
+
# The stmt code[p:q] isn't a continuation, but may be blank
|
| 376 |
+
# or a non-indenting comment line.
|
| 377 |
+
if _junkre(code, p):
|
| 378 |
+
i = i-1
|
| 379 |
+
else:
|
| 380 |
+
break
|
| 381 |
+
if i == 0:
|
| 382 |
+
# nothing but junk!
|
| 383 |
+
assert p == 0
|
| 384 |
+
q = p
|
| 385 |
+
self.stmt_start, self.stmt_end = p, q
|
| 386 |
+
|
| 387 |
+
# Analyze this stmt, to find the last open bracket (if any)
|
| 388 |
+
# and last interesting character (if any).
|
| 389 |
+
lastch = ""
|
| 390 |
+
stack = [] # stack of open bracket indices
|
| 391 |
+
push_stack = stack.append
|
| 392 |
+
bracketing = [(p, 0)]
|
| 393 |
+
while p < q:
|
| 394 |
+
# suck up all except ()[]{}'"#\\
|
| 395 |
+
m = _chew_ordinaryre(code, p, q)
|
| 396 |
+
if m:
|
| 397 |
+
# we skipped at least one boring char
|
| 398 |
+
newp = m.end()
|
| 399 |
+
# back up over totally boring whitespace
|
| 400 |
+
i = newp - 1 # index of last boring char
|
| 401 |
+
while i >= p and code[i] in " \t\n":
|
| 402 |
+
i = i-1
|
| 403 |
+
if i >= p:
|
| 404 |
+
lastch = code[i]
|
| 405 |
+
p = newp
|
| 406 |
+
if p >= q:
|
| 407 |
+
break
|
| 408 |
+
|
| 409 |
+
ch = code[p]
|
| 410 |
+
|
| 411 |
+
if ch in "([{":
|
| 412 |
+
push_stack(p)
|
| 413 |
+
bracketing.append((p, len(stack)))
|
| 414 |
+
lastch = ch
|
| 415 |
+
p = p+1
|
| 416 |
+
continue
|
| 417 |
+
|
| 418 |
+
if ch in ")]}":
|
| 419 |
+
if stack:
|
| 420 |
+
del stack[-1]
|
| 421 |
+
lastch = ch
|
| 422 |
+
p = p+1
|
| 423 |
+
bracketing.append((p, len(stack)))
|
| 424 |
+
continue
|
| 425 |
+
|
| 426 |
+
if ch == '"' or ch == "'":
|
| 427 |
+
# consume string
|
| 428 |
+
# Note that study1 did this with a Python loop, but
|
| 429 |
+
# we use a regexp here; the reason is speed in both
|
| 430 |
+
# cases; the string may be huge, but study1 pre-squashed
|
| 431 |
+
# strings to a couple of characters per line. study1
|
| 432 |
+
# also needed to keep track of newlines, and we don't
|
| 433 |
+
# have to.
|
| 434 |
+
bracketing.append((p, len(stack)+1))
|
| 435 |
+
lastch = ch
|
| 436 |
+
p = _match_stringre(code, p, q).end()
|
| 437 |
+
bracketing.append((p, len(stack)))
|
| 438 |
+
continue
|
| 439 |
+
|
| 440 |
+
if ch == '#':
|
| 441 |
+
# consume comment and trailing newline
|
| 442 |
+
bracketing.append((p, len(stack)+1))
|
| 443 |
+
p = code.find('\n', p, q) + 1
|
| 444 |
+
assert p > 0
|
| 445 |
+
bracketing.append((p, len(stack)))
|
| 446 |
+
continue
|
| 447 |
+
|
| 448 |
+
assert ch == '\\'
|
| 449 |
+
p = p+1 # beyond backslash
|
| 450 |
+
assert p < q
|
| 451 |
+
if code[p] != '\n':
|
| 452 |
+
# the program is invalid, but can't complain
|
| 453 |
+
lastch = ch + code[p]
|
| 454 |
+
p = p+1 # beyond escaped char
|
| 455 |
+
|
| 456 |
+
# end while p < q:
|
| 457 |
+
|
| 458 |
+
self.lastch = lastch
|
| 459 |
+
self.lastopenbracketpos = stack[-1] if stack else None
|
| 460 |
+
self.stmt_bracketing = tuple(bracketing)
|
| 461 |
+
|
| 462 |
+
def compute_bracket_indent(self):
|
| 463 |
+
"""Return number of spaces the next line should be indented.
|
| 464 |
+
|
| 465 |
+
Line continuation must be C_BRACKET.
|
| 466 |
+
"""
|
| 467 |
+
self._study2()
|
| 468 |
+
assert self.continuation == C_BRACKET
|
| 469 |
+
j = self.lastopenbracketpos
|
| 470 |
+
code = self.code
|
| 471 |
+
n = len(code)
|
| 472 |
+
origi = i = code.rfind('\n', 0, j) + 1
|
| 473 |
+
j = j+1 # one beyond open bracket
|
| 474 |
+
# find first list item; set i to start of its line
|
| 475 |
+
while j < n:
|
| 476 |
+
m = _itemre(code, j)
|
| 477 |
+
if m:
|
| 478 |
+
j = m.end() - 1 # index of first interesting char
|
| 479 |
+
extra = 0
|
| 480 |
+
break
|
| 481 |
+
else:
|
| 482 |
+
# this line is junk; advance to next line
|
| 483 |
+
i = j = code.find('\n', j) + 1
|
| 484 |
+
else:
|
| 485 |
+
# nothing interesting follows the bracket;
|
| 486 |
+
# reproduce the bracket line's indentation + a level
|
| 487 |
+
j = i = origi
|
| 488 |
+
while code[j] in " \t":
|
| 489 |
+
j = j+1
|
| 490 |
+
extra = self.indentwidth
|
| 491 |
+
return len(code[i:j].expandtabs(self.tabwidth)) + extra
|
| 492 |
+
|
| 493 |
+
def get_num_lines_in_stmt(self):
|
| 494 |
+
"""Return number of physical lines in last stmt.
|
| 495 |
+
|
| 496 |
+
The statement doesn't have to be an interesting statement. This is
|
| 497 |
+
intended to be called when continuation is C_BACKSLASH.
|
| 498 |
+
"""
|
| 499 |
+
self._study1()
|
| 500 |
+
goodlines = self.goodlines
|
| 501 |
+
return goodlines[-1] - goodlines[-2]
|
| 502 |
+
|
| 503 |
+
def compute_backslash_indent(self):
|
| 504 |
+
"""Return number of spaces the next line should be indented.
|
| 505 |
+
|
| 506 |
+
Line continuation must be C_BACKSLASH. Also assume that the new
|
| 507 |
+
line is the first one following the initial line of the stmt.
|
| 508 |
+
"""
|
| 509 |
+
self._study2()
|
| 510 |
+
assert self.continuation == C_BACKSLASH
|
| 511 |
+
code = self.code
|
| 512 |
+
i = self.stmt_start
|
| 513 |
+
while code[i] in " \t":
|
| 514 |
+
i = i+1
|
| 515 |
+
startpos = i
|
| 516 |
+
|
| 517 |
+
# See whether the initial line starts an assignment stmt; i.e.,
|
| 518 |
+
# look for an = operator
|
| 519 |
+
endpos = code.find('\n', startpos) + 1
|
| 520 |
+
found = level = 0
|
| 521 |
+
while i < endpos:
|
| 522 |
+
ch = code[i]
|
| 523 |
+
if ch in "([{":
|
| 524 |
+
level = level + 1
|
| 525 |
+
i = i+1
|
| 526 |
+
elif ch in ")]}":
|
| 527 |
+
if level:
|
| 528 |
+
level = level - 1
|
| 529 |
+
i = i+1
|
| 530 |
+
elif ch == '"' or ch == "'":
|
| 531 |
+
i = _match_stringre(code, i, endpos).end()
|
| 532 |
+
elif ch == '#':
|
| 533 |
+
# This line is unreachable because the # makes a comment of
|
| 534 |
+
# everything after it.
|
| 535 |
+
break
|
| 536 |
+
elif level == 0 and ch == '=' and \
|
| 537 |
+
(i == 0 or code[i-1] not in "=<>!") and \
|
| 538 |
+
code[i+1] != '=':
|
| 539 |
+
found = 1
|
| 540 |
+
break
|
| 541 |
+
else:
|
| 542 |
+
i = i+1
|
| 543 |
+
|
| 544 |
+
if found:
|
| 545 |
+
# found a legit =, but it may be the last interesting
|
| 546 |
+
# thing on the line
|
| 547 |
+
i = i+1 # move beyond the =
|
| 548 |
+
found = re.match(r"\s*\\", code[i:endpos]) is None
|
| 549 |
+
|
| 550 |
+
if not found:
|
| 551 |
+
# oh well ... settle for moving beyond the first chunk
|
| 552 |
+
# of non-whitespace chars
|
| 553 |
+
i = startpos
|
| 554 |
+
while code[i] not in " \t\n":
|
| 555 |
+
i = i+1
|
| 556 |
+
|
| 557 |
+
return len(code[self.stmt_start:i].expandtabs(\
|
| 558 |
+
self.tabwidth)) + 1
|
| 559 |
+
|
| 560 |
+
def get_base_indent_string(self):
|
| 561 |
+
"""Return the leading whitespace on the initial line of the last
|
| 562 |
+
interesting stmt.
|
| 563 |
+
"""
|
| 564 |
+
self._study2()
|
| 565 |
+
i, n = self.stmt_start, self.stmt_end
|
| 566 |
+
j = i
|
| 567 |
+
code = self.code
|
| 568 |
+
while j < n and code[j] in " \t":
|
| 569 |
+
j = j + 1
|
| 570 |
+
return code[i:j]
|
| 571 |
+
|
| 572 |
+
def is_block_opener(self):
|
| 573 |
+
"Return True if the last interesting statement opens a block."
|
| 574 |
+
self._study2()
|
| 575 |
+
return self.lastch == ':'
|
| 576 |
+
|
| 577 |
+
def is_block_closer(self):
|
| 578 |
+
"Return True if the last interesting statement closes a block."
|
| 579 |
+
self._study2()
|
| 580 |
+
return _closere(self.code, self.stmt_start) is not None
|
| 581 |
+
|
| 582 |
+
def get_last_stmt_bracketing(self):
|
| 583 |
+
"""Return bracketing structure of the last interesting statement.
|
| 584 |
+
|
| 585 |
+
The returned tuple is in the format defined in _study2().
|
| 586 |
+
"""
|
| 587 |
+
self._study2()
|
| 588 |
+
return self.stmt_bracketing
|
| 589 |
+
|
| 590 |
+
|
| 591 |
+
if __name__ == '__main__':
|
| 592 |
+
from unittest import main
|
| 593 |
+
main('idlelib.idle_test.test_pyparse', verbosity=2)
|
my_container_sandbox/workspace/anaconda3/lib/python3.8/idlelib/pyshell.py
ADDED
|
@@ -0,0 +1,1579 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#! /usr/bin/env python3
|
| 2 |
+
|
| 3 |
+
import sys
|
| 4 |
+
if __name__ == "__main__":
|
| 5 |
+
sys.modules['idlelib.pyshell'] = sys.modules['__main__']
|
| 6 |
+
|
| 7 |
+
try:
|
| 8 |
+
from tkinter import *
|
| 9 |
+
except ImportError:
|
| 10 |
+
print("** IDLE can't import Tkinter.\n"
|
| 11 |
+
"Your Python may not be configured for Tk. **", file=sys.__stderr__)
|
| 12 |
+
raise SystemExit(1)
|
| 13 |
+
|
| 14 |
+
# Valid arguments for the ...Awareness call below are defined in the following.
|
| 15 |
+
# https://msdn.microsoft.com/en-us/library/windows/desktop/dn280512(v=vs.85).aspx
|
| 16 |
+
if sys.platform == 'win32':
|
| 17 |
+
try:
|
| 18 |
+
import ctypes
|
| 19 |
+
PROCESS_SYSTEM_DPI_AWARE = 1 # Int required.
|
| 20 |
+
ctypes.OleDLL('shcore').SetProcessDpiAwareness(PROCESS_SYSTEM_DPI_AWARE)
|
| 21 |
+
except (ImportError, AttributeError, OSError):
|
| 22 |
+
pass
|
| 23 |
+
|
| 24 |
+
from tkinter import messagebox
|
| 25 |
+
if TkVersion < 8.5:
|
| 26 |
+
root = Tk() # otherwise create root in main
|
| 27 |
+
root.withdraw()
|
| 28 |
+
from idlelib.run import fix_scaling
|
| 29 |
+
fix_scaling(root)
|
| 30 |
+
messagebox.showerror("Idle Cannot Start",
|
| 31 |
+
"Idle requires tcl/tk 8.5+, not %s." % TkVersion,
|
| 32 |
+
parent=root)
|
| 33 |
+
raise SystemExit(1)
|
| 34 |
+
|
| 35 |
+
from code import InteractiveInterpreter
|
| 36 |
+
import linecache
|
| 37 |
+
import os
|
| 38 |
+
import os.path
|
| 39 |
+
from platform import python_version
|
| 40 |
+
import re
|
| 41 |
+
import socket
|
| 42 |
+
import subprocess
|
| 43 |
+
from textwrap import TextWrapper
|
| 44 |
+
import threading
|
| 45 |
+
import time
|
| 46 |
+
import tokenize
|
| 47 |
+
import warnings
|
| 48 |
+
|
| 49 |
+
from idlelib.colorizer import ColorDelegator
|
| 50 |
+
from idlelib.config import idleConf
|
| 51 |
+
from idlelib import debugger
|
| 52 |
+
from idlelib import debugger_r
|
| 53 |
+
from idlelib.editor import EditorWindow, fixwordbreaks
|
| 54 |
+
from idlelib.filelist import FileList
|
| 55 |
+
from idlelib.outwin import OutputWindow
|
| 56 |
+
from idlelib import rpc
|
| 57 |
+
from idlelib.run import idle_formatwarning, StdInputFile, StdOutputFile
|
| 58 |
+
from idlelib.undo import UndoDelegator
|
| 59 |
+
|
| 60 |
+
HOST = '127.0.0.1' # python execution server on localhost loopback
|
| 61 |
+
PORT = 0 # someday pass in host, port for remote debug capability
|
| 62 |
+
|
| 63 |
+
# Override warnings module to write to warning_stream. Initialize to send IDLE
|
| 64 |
+
# internal warnings to the console. ScriptBinding.check_syntax() will
|
| 65 |
+
# temporarily redirect the stream to the shell window to display warnings when
|
| 66 |
+
# checking user's code.
|
| 67 |
+
warning_stream = sys.__stderr__ # None, at least on Windows, if no console.
|
| 68 |
+
|
| 69 |
+
def idle_showwarning(
|
| 70 |
+
message, category, filename, lineno, file=None, line=None):
|
| 71 |
+
"""Show Idle-format warning (after replacing warnings.showwarning).
|
| 72 |
+
|
| 73 |
+
The differences are the formatter called, the file=None replacement,
|
| 74 |
+
which can be None, the capture of the consequence AttributeError,
|
| 75 |
+
and the output of a hard-coded prompt.
|
| 76 |
+
"""
|
| 77 |
+
if file is None:
|
| 78 |
+
file = warning_stream
|
| 79 |
+
try:
|
| 80 |
+
file.write(idle_formatwarning(
|
| 81 |
+
message, category, filename, lineno, line=line))
|
| 82 |
+
file.write(">>> ")
|
| 83 |
+
except (AttributeError, OSError):
|
| 84 |
+
pass # if file (probably __stderr__) is invalid, skip warning.
|
| 85 |
+
|
| 86 |
+
_warnings_showwarning = None
|
| 87 |
+
|
| 88 |
+
def capture_warnings(capture):
|
| 89 |
+
"Replace warning.showwarning with idle_showwarning, or reverse."
|
| 90 |
+
|
| 91 |
+
global _warnings_showwarning
|
| 92 |
+
if capture:
|
| 93 |
+
if _warnings_showwarning is None:
|
| 94 |
+
_warnings_showwarning = warnings.showwarning
|
| 95 |
+
warnings.showwarning = idle_showwarning
|
| 96 |
+
else:
|
| 97 |
+
if _warnings_showwarning is not None:
|
| 98 |
+
warnings.showwarning = _warnings_showwarning
|
| 99 |
+
_warnings_showwarning = None
|
| 100 |
+
|
| 101 |
+
capture_warnings(True)
|
| 102 |
+
|
| 103 |
+
def extended_linecache_checkcache(filename=None,
|
| 104 |
+
orig_checkcache=linecache.checkcache):
|
| 105 |
+
"""Extend linecache.checkcache to preserve the <pyshell#...> entries
|
| 106 |
+
|
| 107 |
+
Rather than repeating the linecache code, patch it to save the
|
| 108 |
+
<pyshell#...> entries, call the original linecache.checkcache()
|
| 109 |
+
(skipping them), and then restore the saved entries.
|
| 110 |
+
|
| 111 |
+
orig_checkcache is bound at definition time to the original
|
| 112 |
+
method, allowing it to be patched.
|
| 113 |
+
"""
|
| 114 |
+
cache = linecache.cache
|
| 115 |
+
save = {}
|
| 116 |
+
for key in list(cache):
|
| 117 |
+
if key[:1] + key[-1:] == '<>':
|
| 118 |
+
save[key] = cache.pop(key)
|
| 119 |
+
orig_checkcache(filename)
|
| 120 |
+
cache.update(save)
|
| 121 |
+
|
| 122 |
+
# Patch linecache.checkcache():
|
| 123 |
+
linecache.checkcache = extended_linecache_checkcache
|
| 124 |
+
|
| 125 |
+
|
| 126 |
+
class PyShellEditorWindow(EditorWindow):
|
| 127 |
+
"Regular text edit window in IDLE, supports breakpoints"
|
| 128 |
+
|
| 129 |
+
def __init__(self, *args):
|
| 130 |
+
self.breakpoints = []
|
| 131 |
+
EditorWindow.__init__(self, *args)
|
| 132 |
+
self.text.bind("<<set-breakpoint-here>>", self.set_breakpoint_here)
|
| 133 |
+
self.text.bind("<<clear-breakpoint-here>>", self.clear_breakpoint_here)
|
| 134 |
+
self.text.bind("<<open-python-shell>>", self.flist.open_shell)
|
| 135 |
+
|
| 136 |
+
#TODO: don't read/write this from/to .idlerc when testing
|
| 137 |
+
self.breakpointPath = os.path.join(
|
| 138 |
+
idleConf.userdir, 'breakpoints.lst')
|
| 139 |
+
# whenever a file is changed, restore breakpoints
|
| 140 |
+
def filename_changed_hook(old_hook=self.io.filename_change_hook,
|
| 141 |
+
self=self):
|
| 142 |
+
self.restore_file_breaks()
|
| 143 |
+
old_hook()
|
| 144 |
+
self.io.set_filename_change_hook(filename_changed_hook)
|
| 145 |
+
if self.io.filename:
|
| 146 |
+
self.restore_file_breaks()
|
| 147 |
+
self.color_breakpoint_text()
|
| 148 |
+
|
| 149 |
+
rmenu_specs = [
|
| 150 |
+
("Cut", "<<cut>>", "rmenu_check_cut"),
|
| 151 |
+
("Copy", "<<copy>>", "rmenu_check_copy"),
|
| 152 |
+
("Paste", "<<paste>>", "rmenu_check_paste"),
|
| 153 |
+
(None, None, None),
|
| 154 |
+
("Set Breakpoint", "<<set-breakpoint-here>>", None),
|
| 155 |
+
("Clear Breakpoint", "<<clear-breakpoint-here>>", None)
|
| 156 |
+
]
|
| 157 |
+
|
| 158 |
+
def color_breakpoint_text(self, color=True):
|
| 159 |
+
"Turn colorizing of breakpoint text on or off"
|
| 160 |
+
if self.io is None:
|
| 161 |
+
# possible due to update in restore_file_breaks
|
| 162 |
+
return
|
| 163 |
+
if color:
|
| 164 |
+
theme = idleConf.CurrentTheme()
|
| 165 |
+
cfg = idleConf.GetHighlight(theme, "break")
|
| 166 |
+
else:
|
| 167 |
+
cfg = {'foreground': '', 'background': ''}
|
| 168 |
+
self.text.tag_config('BREAK', cfg)
|
| 169 |
+
|
| 170 |
+
def set_breakpoint(self, lineno):
|
| 171 |
+
text = self.text
|
| 172 |
+
filename = self.io.filename
|
| 173 |
+
text.tag_add("BREAK", "%d.0" % lineno, "%d.0" % (lineno+1))
|
| 174 |
+
try:
|
| 175 |
+
self.breakpoints.index(lineno)
|
| 176 |
+
except ValueError: # only add if missing, i.e. do once
|
| 177 |
+
self.breakpoints.append(lineno)
|
| 178 |
+
try: # update the subprocess debugger
|
| 179 |
+
debug = self.flist.pyshell.interp.debugger
|
| 180 |
+
debug.set_breakpoint_here(filename, lineno)
|
| 181 |
+
except: # but debugger may not be active right now....
|
| 182 |
+
pass
|
| 183 |
+
|
| 184 |
+
def set_breakpoint_here(self, event=None):
|
| 185 |
+
text = self.text
|
| 186 |
+
filename = self.io.filename
|
| 187 |
+
if not filename:
|
| 188 |
+
text.bell()
|
| 189 |
+
return
|
| 190 |
+
lineno = int(float(text.index("insert")))
|
| 191 |
+
self.set_breakpoint(lineno)
|
| 192 |
+
|
| 193 |
+
def clear_breakpoint_here(self, event=None):
|
| 194 |
+
text = self.text
|
| 195 |
+
filename = self.io.filename
|
| 196 |
+
if not filename:
|
| 197 |
+
text.bell()
|
| 198 |
+
return
|
| 199 |
+
lineno = int(float(text.index("insert")))
|
| 200 |
+
try:
|
| 201 |
+
self.breakpoints.remove(lineno)
|
| 202 |
+
except:
|
| 203 |
+
pass
|
| 204 |
+
text.tag_remove("BREAK", "insert linestart",\
|
| 205 |
+
"insert lineend +1char")
|
| 206 |
+
try:
|
| 207 |
+
debug = self.flist.pyshell.interp.debugger
|
| 208 |
+
debug.clear_breakpoint_here(filename, lineno)
|
| 209 |
+
except:
|
| 210 |
+
pass
|
| 211 |
+
|
| 212 |
+
def clear_file_breaks(self):
|
| 213 |
+
if self.breakpoints:
|
| 214 |
+
text = self.text
|
| 215 |
+
filename = self.io.filename
|
| 216 |
+
if not filename:
|
| 217 |
+
text.bell()
|
| 218 |
+
return
|
| 219 |
+
self.breakpoints = []
|
| 220 |
+
text.tag_remove("BREAK", "1.0", END)
|
| 221 |
+
try:
|
| 222 |
+
debug = self.flist.pyshell.interp.debugger
|
| 223 |
+
debug.clear_file_breaks(filename)
|
| 224 |
+
except:
|
| 225 |
+
pass
|
| 226 |
+
|
| 227 |
+
def store_file_breaks(self):
|
| 228 |
+
"Save breakpoints when file is saved"
|
| 229 |
+
# XXX 13 Dec 2002 KBK Currently the file must be saved before it can
|
| 230 |
+
# be run. The breaks are saved at that time. If we introduce
|
| 231 |
+
# a temporary file save feature the save breaks functionality
|
| 232 |
+
# needs to be re-verified, since the breaks at the time the
|
| 233 |
+
# temp file is created may differ from the breaks at the last
|
| 234 |
+
# permanent save of the file. Currently, a break introduced
|
| 235 |
+
# after a save will be effective, but not persistent.
|
| 236 |
+
# This is necessary to keep the saved breaks synched with the
|
| 237 |
+
# saved file.
|
| 238 |
+
#
|
| 239 |
+
# Breakpoints are set as tagged ranges in the text.
|
| 240 |
+
# Since a modified file has to be saved before it is
|
| 241 |
+
# run, and since self.breakpoints (from which the subprocess
|
| 242 |
+
# debugger is loaded) is updated during the save, the visible
|
| 243 |
+
# breaks stay synched with the subprocess even if one of these
|
| 244 |
+
# unexpected breakpoint deletions occurs.
|
| 245 |
+
breaks = self.breakpoints
|
| 246 |
+
filename = self.io.filename
|
| 247 |
+
try:
|
| 248 |
+
with open(self.breakpointPath, "r") as fp:
|
| 249 |
+
lines = fp.readlines()
|
| 250 |
+
except OSError:
|
| 251 |
+
lines = []
|
| 252 |
+
try:
|
| 253 |
+
with open(self.breakpointPath, "w") as new_file:
|
| 254 |
+
for line in lines:
|
| 255 |
+
if not line.startswith(filename + '='):
|
| 256 |
+
new_file.write(line)
|
| 257 |
+
self.update_breakpoints()
|
| 258 |
+
breaks = self.breakpoints
|
| 259 |
+
if breaks:
|
| 260 |
+
new_file.write(filename + '=' + str(breaks) + '\n')
|
| 261 |
+
except OSError as err:
|
| 262 |
+
if not getattr(self.root, "breakpoint_error_displayed", False):
|
| 263 |
+
self.root.breakpoint_error_displayed = True
|
| 264 |
+
messagebox.showerror(title='IDLE Error',
|
| 265 |
+
message='Unable to update breakpoint list:\n%s'
|
| 266 |
+
% str(err),
|
| 267 |
+
parent=self.text)
|
| 268 |
+
|
| 269 |
+
def restore_file_breaks(self):
|
| 270 |
+
self.text.update() # this enables setting "BREAK" tags to be visible
|
| 271 |
+
if self.io is None:
|
| 272 |
+
# can happen if IDLE closes due to the .update() call
|
| 273 |
+
return
|
| 274 |
+
filename = self.io.filename
|
| 275 |
+
if filename is None:
|
| 276 |
+
return
|
| 277 |
+
if os.path.isfile(self.breakpointPath):
|
| 278 |
+
with open(self.breakpointPath, "r") as fp:
|
| 279 |
+
lines = fp.readlines()
|
| 280 |
+
for line in lines:
|
| 281 |
+
if line.startswith(filename + '='):
|
| 282 |
+
breakpoint_linenumbers = eval(line[len(filename)+1:])
|
| 283 |
+
for breakpoint_linenumber in breakpoint_linenumbers:
|
| 284 |
+
self.set_breakpoint(breakpoint_linenumber)
|
| 285 |
+
|
| 286 |
+
def update_breakpoints(self):
|
| 287 |
+
"Retrieves all the breakpoints in the current window"
|
| 288 |
+
text = self.text
|
| 289 |
+
ranges = text.tag_ranges("BREAK")
|
| 290 |
+
linenumber_list = self.ranges_to_linenumbers(ranges)
|
| 291 |
+
self.breakpoints = linenumber_list
|
| 292 |
+
|
| 293 |
+
def ranges_to_linenumbers(self, ranges):
|
| 294 |
+
lines = []
|
| 295 |
+
for index in range(0, len(ranges), 2):
|
| 296 |
+
lineno = int(float(ranges[index].string))
|
| 297 |
+
end = int(float(ranges[index+1].string))
|
| 298 |
+
while lineno < end:
|
| 299 |
+
lines.append(lineno)
|
| 300 |
+
lineno += 1
|
| 301 |
+
return lines
|
| 302 |
+
|
| 303 |
+
# XXX 13 Dec 2002 KBK Not used currently
|
| 304 |
+
# def saved_change_hook(self):
|
| 305 |
+
# "Extend base method - clear breaks if module is modified"
|
| 306 |
+
# if not self.get_saved():
|
| 307 |
+
# self.clear_file_breaks()
|
| 308 |
+
# EditorWindow.saved_change_hook(self)
|
| 309 |
+
|
| 310 |
+
def _close(self):
|
| 311 |
+
"Extend base method - clear breaks when module is closed"
|
| 312 |
+
self.clear_file_breaks()
|
| 313 |
+
EditorWindow._close(self)
|
| 314 |
+
|
| 315 |
+
|
| 316 |
+
class PyShellFileList(FileList):
|
| 317 |
+
"Extend base class: IDLE supports a shell and breakpoints"
|
| 318 |
+
|
| 319 |
+
# override FileList's class variable, instances return PyShellEditorWindow
|
| 320 |
+
# instead of EditorWindow when new edit windows are created.
|
| 321 |
+
EditorWindow = PyShellEditorWindow
|
| 322 |
+
|
| 323 |
+
pyshell = None
|
| 324 |
+
|
| 325 |
+
def open_shell(self, event=None):
|
| 326 |
+
if self.pyshell:
|
| 327 |
+
self.pyshell.top.wakeup()
|
| 328 |
+
else:
|
| 329 |
+
self.pyshell = PyShell(self)
|
| 330 |
+
if self.pyshell:
|
| 331 |
+
if not self.pyshell.begin():
|
| 332 |
+
return None
|
| 333 |
+
return self.pyshell
|
| 334 |
+
|
| 335 |
+
|
| 336 |
+
class ModifiedColorDelegator(ColorDelegator):
|
| 337 |
+
"Extend base class: colorizer for the shell window itself"
|
| 338 |
+
|
| 339 |
+
def __init__(self):
|
| 340 |
+
ColorDelegator.__init__(self)
|
| 341 |
+
self.LoadTagDefs()
|
| 342 |
+
|
| 343 |
+
def recolorize_main(self):
|
| 344 |
+
self.tag_remove("TODO", "1.0", "iomark")
|
| 345 |
+
self.tag_add("SYNC", "1.0", "iomark")
|
| 346 |
+
ColorDelegator.recolorize_main(self)
|
| 347 |
+
|
| 348 |
+
def LoadTagDefs(self):
|
| 349 |
+
ColorDelegator.LoadTagDefs(self)
|
| 350 |
+
theme = idleConf.CurrentTheme()
|
| 351 |
+
self.tagdefs.update({
|
| 352 |
+
"stdin": {'background':None,'foreground':None},
|
| 353 |
+
"stdout": idleConf.GetHighlight(theme, "stdout"),
|
| 354 |
+
"stderr": idleConf.GetHighlight(theme, "stderr"),
|
| 355 |
+
"console": idleConf.GetHighlight(theme, "console"),
|
| 356 |
+
})
|
| 357 |
+
|
| 358 |
+
def removecolors(self):
|
| 359 |
+
# Don't remove shell color tags before "iomark"
|
| 360 |
+
for tag in self.tagdefs:
|
| 361 |
+
self.tag_remove(tag, "iomark", "end")
|
| 362 |
+
|
| 363 |
+
class ModifiedUndoDelegator(UndoDelegator):
|
| 364 |
+
"Extend base class: forbid insert/delete before the I/O mark"
|
| 365 |
+
|
| 366 |
+
def insert(self, index, chars, tags=None):
|
| 367 |
+
try:
|
| 368 |
+
if self.delegate.compare(index, "<", "iomark"):
|
| 369 |
+
self.delegate.bell()
|
| 370 |
+
return
|
| 371 |
+
except TclError:
|
| 372 |
+
pass
|
| 373 |
+
UndoDelegator.insert(self, index, chars, tags)
|
| 374 |
+
|
| 375 |
+
def delete(self, index1, index2=None):
|
| 376 |
+
try:
|
| 377 |
+
if self.delegate.compare(index1, "<", "iomark"):
|
| 378 |
+
self.delegate.bell()
|
| 379 |
+
return
|
| 380 |
+
except TclError:
|
| 381 |
+
pass
|
| 382 |
+
UndoDelegator.delete(self, index1, index2)
|
| 383 |
+
|
| 384 |
+
|
| 385 |
+
class MyRPCClient(rpc.RPCClient):
|
| 386 |
+
|
| 387 |
+
def handle_EOF(self):
|
| 388 |
+
"Override the base class - just re-raise EOFError"
|
| 389 |
+
raise EOFError
|
| 390 |
+
|
| 391 |
+
def restart_line(width, filename): # See bpo-38141.
|
| 392 |
+
"""Return width long restart line formatted with filename.
|
| 393 |
+
|
| 394 |
+
Fill line with balanced '='s, with any extras and at least one at
|
| 395 |
+
the beginning. Do not end with a trailing space.
|
| 396 |
+
"""
|
| 397 |
+
tag = f"= RESTART: {filename or 'Shell'} ="
|
| 398 |
+
if width >= len(tag):
|
| 399 |
+
div, mod = divmod((width -len(tag)), 2)
|
| 400 |
+
return f"{(div+mod)*'='}{tag}{div*'='}"
|
| 401 |
+
else:
|
| 402 |
+
return tag[:-2] # Remove ' ='.
|
| 403 |
+
|
| 404 |
+
|
| 405 |
+
class ModifiedInterpreter(InteractiveInterpreter):
|
| 406 |
+
|
| 407 |
+
def __init__(self, tkconsole):
|
| 408 |
+
self.tkconsole = tkconsole
|
| 409 |
+
locals = sys.modules['__main__'].__dict__
|
| 410 |
+
InteractiveInterpreter.__init__(self, locals=locals)
|
| 411 |
+
self.restarting = False
|
| 412 |
+
self.subprocess_arglist = None
|
| 413 |
+
self.port = PORT
|
| 414 |
+
self.original_compiler_flags = self.compile.compiler.flags
|
| 415 |
+
|
| 416 |
+
_afterid = None
|
| 417 |
+
rpcclt = None
|
| 418 |
+
rpcsubproc = None
|
| 419 |
+
|
| 420 |
+
def spawn_subprocess(self):
|
| 421 |
+
if self.subprocess_arglist is None:
|
| 422 |
+
self.subprocess_arglist = self.build_subprocess_arglist()
|
| 423 |
+
self.rpcsubproc = subprocess.Popen(self.subprocess_arglist)
|
| 424 |
+
|
| 425 |
+
def build_subprocess_arglist(self):
|
| 426 |
+
assert (self.port!=0), (
|
| 427 |
+
"Socket should have been assigned a port number.")
|
| 428 |
+
w = ['-W' + s for s in sys.warnoptions]
|
| 429 |
+
# Maybe IDLE is installed and is being accessed via sys.path,
|
| 430 |
+
# or maybe it's not installed and the idle.py script is being
|
| 431 |
+
# run from the IDLE source directory.
|
| 432 |
+
del_exitf = idleConf.GetOption('main', 'General', 'delete-exitfunc',
|
| 433 |
+
default=False, type='bool')
|
| 434 |
+
command = "__import__('idlelib.run').run.main(%r)" % (del_exitf,)
|
| 435 |
+
return [sys.executable] + w + ["-c", command, str(self.port)]
|
| 436 |
+
|
| 437 |
+
def start_subprocess(self):
|
| 438 |
+
addr = (HOST, self.port)
|
| 439 |
+
# GUI makes several attempts to acquire socket, listens for connection
|
| 440 |
+
for i in range(3):
|
| 441 |
+
time.sleep(i)
|
| 442 |
+
try:
|
| 443 |
+
self.rpcclt = MyRPCClient(addr)
|
| 444 |
+
break
|
| 445 |
+
except OSError:
|
| 446 |
+
pass
|
| 447 |
+
else:
|
| 448 |
+
self.display_port_binding_error()
|
| 449 |
+
return None
|
| 450 |
+
# if PORT was 0, system will assign an 'ephemeral' port. Find it out:
|
| 451 |
+
self.port = self.rpcclt.listening_sock.getsockname()[1]
|
| 452 |
+
# if PORT was not 0, probably working with a remote execution server
|
| 453 |
+
if PORT != 0:
|
| 454 |
+
# To allow reconnection within the 2MSL wait (cf. Stevens TCP
|
| 455 |
+
# V1, 18.6), set SO_REUSEADDR. Note that this can be problematic
|
| 456 |
+
# on Windows since the implementation allows two active sockets on
|
| 457 |
+
# the same address!
|
| 458 |
+
self.rpcclt.listening_sock.setsockopt(socket.SOL_SOCKET,
|
| 459 |
+
socket.SO_REUSEADDR, 1)
|
| 460 |
+
self.spawn_subprocess()
|
| 461 |
+
#time.sleep(20) # test to simulate GUI not accepting connection
|
| 462 |
+
# Accept the connection from the Python execution server
|
| 463 |
+
self.rpcclt.listening_sock.settimeout(10)
|
| 464 |
+
try:
|
| 465 |
+
self.rpcclt.accept()
|
| 466 |
+
except socket.timeout:
|
| 467 |
+
self.display_no_subprocess_error()
|
| 468 |
+
return None
|
| 469 |
+
self.rpcclt.register("console", self.tkconsole)
|
| 470 |
+
self.rpcclt.register("stdin", self.tkconsole.stdin)
|
| 471 |
+
self.rpcclt.register("stdout", self.tkconsole.stdout)
|
| 472 |
+
self.rpcclt.register("stderr", self.tkconsole.stderr)
|
| 473 |
+
self.rpcclt.register("flist", self.tkconsole.flist)
|
| 474 |
+
self.rpcclt.register("linecache", linecache)
|
| 475 |
+
self.rpcclt.register("interp", self)
|
| 476 |
+
self.transfer_path(with_cwd=True)
|
| 477 |
+
self.poll_subprocess()
|
| 478 |
+
return self.rpcclt
|
| 479 |
+
|
| 480 |
+
def restart_subprocess(self, with_cwd=False, filename=''):
|
| 481 |
+
if self.restarting:
|
| 482 |
+
return self.rpcclt
|
| 483 |
+
self.restarting = True
|
| 484 |
+
# close only the subprocess debugger
|
| 485 |
+
debug = self.getdebugger()
|
| 486 |
+
if debug:
|
| 487 |
+
try:
|
| 488 |
+
# Only close subprocess debugger, don't unregister gui_adap!
|
| 489 |
+
debugger_r.close_subprocess_debugger(self.rpcclt)
|
| 490 |
+
except:
|
| 491 |
+
pass
|
| 492 |
+
# Kill subprocess, spawn a new one, accept connection.
|
| 493 |
+
self.rpcclt.close()
|
| 494 |
+
self.terminate_subprocess()
|
| 495 |
+
console = self.tkconsole
|
| 496 |
+
was_executing = console.executing
|
| 497 |
+
console.executing = False
|
| 498 |
+
self.spawn_subprocess()
|
| 499 |
+
try:
|
| 500 |
+
self.rpcclt.accept()
|
| 501 |
+
except socket.timeout:
|
| 502 |
+
self.display_no_subprocess_error()
|
| 503 |
+
return None
|
| 504 |
+
self.transfer_path(with_cwd=with_cwd)
|
| 505 |
+
console.stop_readline()
|
| 506 |
+
# annotate restart in shell window and mark it
|
| 507 |
+
console.text.delete("iomark", "end-1c")
|
| 508 |
+
console.write('\n')
|
| 509 |
+
console.write(restart_line(console.width, filename))
|
| 510 |
+
console.text.mark_set("restart", "end-1c")
|
| 511 |
+
console.text.mark_gravity("restart", "left")
|
| 512 |
+
if not filename:
|
| 513 |
+
console.showprompt()
|
| 514 |
+
# restart subprocess debugger
|
| 515 |
+
if debug:
|
| 516 |
+
# Restarted debugger connects to current instance of debug GUI
|
| 517 |
+
debugger_r.restart_subprocess_debugger(self.rpcclt)
|
| 518 |
+
# reload remote debugger breakpoints for all PyShellEditWindows
|
| 519 |
+
debug.load_breakpoints()
|
| 520 |
+
self.compile.compiler.flags = self.original_compiler_flags
|
| 521 |
+
self.restarting = False
|
| 522 |
+
return self.rpcclt
|
| 523 |
+
|
| 524 |
+
def __request_interrupt(self):
|
| 525 |
+
self.rpcclt.remotecall("exec", "interrupt_the_server", (), {})
|
| 526 |
+
|
| 527 |
+
def interrupt_subprocess(self):
|
| 528 |
+
threading.Thread(target=self.__request_interrupt).start()
|
| 529 |
+
|
| 530 |
+
def kill_subprocess(self):
|
| 531 |
+
if self._afterid is not None:
|
| 532 |
+
self.tkconsole.text.after_cancel(self._afterid)
|
| 533 |
+
try:
|
| 534 |
+
self.rpcclt.listening_sock.close()
|
| 535 |
+
except AttributeError: # no socket
|
| 536 |
+
pass
|
| 537 |
+
try:
|
| 538 |
+
self.rpcclt.close()
|
| 539 |
+
except AttributeError: # no socket
|
| 540 |
+
pass
|
| 541 |
+
self.terminate_subprocess()
|
| 542 |
+
self.tkconsole.executing = False
|
| 543 |
+
self.rpcclt = None
|
| 544 |
+
|
| 545 |
+
def terminate_subprocess(self):
|
| 546 |
+
"Make sure subprocess is terminated"
|
| 547 |
+
try:
|
| 548 |
+
self.rpcsubproc.kill()
|
| 549 |
+
except OSError:
|
| 550 |
+
# process already terminated
|
| 551 |
+
return
|
| 552 |
+
else:
|
| 553 |
+
try:
|
| 554 |
+
self.rpcsubproc.wait()
|
| 555 |
+
except OSError:
|
| 556 |
+
return
|
| 557 |
+
|
| 558 |
+
def transfer_path(self, with_cwd=False):
|
| 559 |
+
if with_cwd: # Issue 13506
|
| 560 |
+
path = [''] # include Current Working Directory
|
| 561 |
+
path.extend(sys.path)
|
| 562 |
+
else:
|
| 563 |
+
path = sys.path
|
| 564 |
+
|
| 565 |
+
self.runcommand("""if 1:
|
| 566 |
+
import sys as _sys
|
| 567 |
+
_sys.path = %r
|
| 568 |
+
del _sys
|
| 569 |
+
\n""" % (path,))
|
| 570 |
+
|
| 571 |
+
active_seq = None
|
| 572 |
+
|
| 573 |
+
def poll_subprocess(self):
|
| 574 |
+
clt = self.rpcclt
|
| 575 |
+
if clt is None:
|
| 576 |
+
return
|
| 577 |
+
try:
|
| 578 |
+
response = clt.pollresponse(self.active_seq, wait=0.05)
|
| 579 |
+
except (EOFError, OSError, KeyboardInterrupt):
|
| 580 |
+
# lost connection or subprocess terminated itself, restart
|
| 581 |
+
# [the KBI is from rpc.SocketIO.handle_EOF()]
|
| 582 |
+
if self.tkconsole.closing:
|
| 583 |
+
return
|
| 584 |
+
response = None
|
| 585 |
+
self.restart_subprocess()
|
| 586 |
+
if response:
|
| 587 |
+
self.tkconsole.resetoutput()
|
| 588 |
+
self.active_seq = None
|
| 589 |
+
how, what = response
|
| 590 |
+
console = self.tkconsole.console
|
| 591 |
+
if how == "OK":
|
| 592 |
+
if what is not None:
|
| 593 |
+
print(repr(what), file=console)
|
| 594 |
+
elif how == "EXCEPTION":
|
| 595 |
+
if self.tkconsole.getvar("<<toggle-jit-stack-viewer>>"):
|
| 596 |
+
self.remote_stack_viewer()
|
| 597 |
+
elif how == "ERROR":
|
| 598 |
+
errmsg = "pyshell.ModifiedInterpreter: Subprocess ERROR:\n"
|
| 599 |
+
print(errmsg, what, file=sys.__stderr__)
|
| 600 |
+
print(errmsg, what, file=console)
|
| 601 |
+
# we received a response to the currently active seq number:
|
| 602 |
+
try:
|
| 603 |
+
self.tkconsole.endexecuting()
|
| 604 |
+
except AttributeError: # shell may have closed
|
| 605 |
+
pass
|
| 606 |
+
# Reschedule myself
|
| 607 |
+
if not self.tkconsole.closing:
|
| 608 |
+
self._afterid = self.tkconsole.text.after(
|
| 609 |
+
self.tkconsole.pollinterval, self.poll_subprocess)
|
| 610 |
+
|
| 611 |
+
debugger = None
|
| 612 |
+
|
| 613 |
+
def setdebugger(self, debugger):
|
| 614 |
+
self.debugger = debugger
|
| 615 |
+
|
| 616 |
+
def getdebugger(self):
|
| 617 |
+
return self.debugger
|
| 618 |
+
|
| 619 |
+
def open_remote_stack_viewer(self):
|
| 620 |
+
"""Initiate the remote stack viewer from a separate thread.
|
| 621 |
+
|
| 622 |
+
This method is called from the subprocess, and by returning from this
|
| 623 |
+
method we allow the subprocess to unblock. After a bit the shell
|
| 624 |
+
requests the subprocess to open the remote stack viewer which returns a
|
| 625 |
+
static object looking at the last exception. It is queried through
|
| 626 |
+
the RPC mechanism.
|
| 627 |
+
|
| 628 |
+
"""
|
| 629 |
+
self.tkconsole.text.after(300, self.remote_stack_viewer)
|
| 630 |
+
return
|
| 631 |
+
|
| 632 |
+
def remote_stack_viewer(self):
|
| 633 |
+
from idlelib import debugobj_r
|
| 634 |
+
oid = self.rpcclt.remotequeue("exec", "stackviewer", ("flist",), {})
|
| 635 |
+
if oid is None:
|
| 636 |
+
self.tkconsole.root.bell()
|
| 637 |
+
return
|
| 638 |
+
item = debugobj_r.StubObjectTreeItem(self.rpcclt, oid)
|
| 639 |
+
from idlelib.tree import ScrolledCanvas, TreeNode
|
| 640 |
+
top = Toplevel(self.tkconsole.root)
|
| 641 |
+
theme = idleConf.CurrentTheme()
|
| 642 |
+
background = idleConf.GetHighlight(theme, 'normal')['background']
|
| 643 |
+
sc = ScrolledCanvas(top, bg=background, highlightthickness=0)
|
| 644 |
+
sc.frame.pack(expand=1, fill="both")
|
| 645 |
+
node = TreeNode(sc.canvas, None, item)
|
| 646 |
+
node.expand()
|
| 647 |
+
# XXX Should GC the remote tree when closing the window
|
| 648 |
+
|
| 649 |
+
gid = 0
|
| 650 |
+
|
| 651 |
+
def execsource(self, source):
|
| 652 |
+
"Like runsource() but assumes complete exec source"
|
| 653 |
+
filename = self.stuffsource(source)
|
| 654 |
+
self.execfile(filename, source)
|
| 655 |
+
|
| 656 |
+
def execfile(self, filename, source=None):
|
| 657 |
+
"Execute an existing file"
|
| 658 |
+
if source is None:
|
| 659 |
+
with tokenize.open(filename) as fp:
|
| 660 |
+
source = fp.read()
|
| 661 |
+
if use_subprocess:
|
| 662 |
+
source = (f"__file__ = r'''{os.path.abspath(filename)}'''\n"
|
| 663 |
+
+ source + "\ndel __file__")
|
| 664 |
+
try:
|
| 665 |
+
code = compile(source, filename, "exec")
|
| 666 |
+
except (OverflowError, SyntaxError):
|
| 667 |
+
self.tkconsole.resetoutput()
|
| 668 |
+
print('*** Error in script or command!\n'
|
| 669 |
+
'Traceback (most recent call last):',
|
| 670 |
+
file=self.tkconsole.stderr)
|
| 671 |
+
InteractiveInterpreter.showsyntaxerror(self, filename)
|
| 672 |
+
self.tkconsole.showprompt()
|
| 673 |
+
else:
|
| 674 |
+
self.runcode(code)
|
| 675 |
+
|
| 676 |
+
def runsource(self, source):
|
| 677 |
+
"Extend base class method: Stuff the source in the line cache first"
|
| 678 |
+
filename = self.stuffsource(source)
|
| 679 |
+
# at the moment, InteractiveInterpreter expects str
|
| 680 |
+
assert isinstance(source, str)
|
| 681 |
+
# InteractiveInterpreter.runsource() calls its runcode() method,
|
| 682 |
+
# which is overridden (see below)
|
| 683 |
+
return InteractiveInterpreter.runsource(self, source, filename)
|
| 684 |
+
|
| 685 |
+
def stuffsource(self, source):
|
| 686 |
+
"Stuff source in the filename cache"
|
| 687 |
+
filename = "<pyshell#%d>" % self.gid
|
| 688 |
+
self.gid = self.gid + 1
|
| 689 |
+
lines = source.split("\n")
|
| 690 |
+
linecache.cache[filename] = len(source)+1, 0, lines, filename
|
| 691 |
+
return filename
|
| 692 |
+
|
| 693 |
+
def prepend_syspath(self, filename):
|
| 694 |
+
"Prepend sys.path with file's directory if not already included"
|
| 695 |
+
self.runcommand("""if 1:
|
| 696 |
+
_filename = %r
|
| 697 |
+
import sys as _sys
|
| 698 |
+
from os.path import dirname as _dirname
|
| 699 |
+
_dir = _dirname(_filename)
|
| 700 |
+
if not _dir in _sys.path:
|
| 701 |
+
_sys.path.insert(0, _dir)
|
| 702 |
+
del _filename, _sys, _dirname, _dir
|
| 703 |
+
\n""" % (filename,))
|
| 704 |
+
|
| 705 |
+
def showsyntaxerror(self, filename=None):
|
| 706 |
+
"""Override Interactive Interpreter method: Use Colorizing
|
| 707 |
+
|
| 708 |
+
Color the offending position instead of printing it and pointing at it
|
| 709 |
+
with a caret.
|
| 710 |
+
|
| 711 |
+
"""
|
| 712 |
+
tkconsole = self.tkconsole
|
| 713 |
+
text = tkconsole.text
|
| 714 |
+
text.tag_remove("ERROR", "1.0", "end")
|
| 715 |
+
type, value, tb = sys.exc_info()
|
| 716 |
+
msg = getattr(value, 'msg', '') or value or "<no detail available>"
|
| 717 |
+
lineno = getattr(value, 'lineno', '') or 1
|
| 718 |
+
offset = getattr(value, 'offset', '') or 0
|
| 719 |
+
if offset == 0:
|
| 720 |
+
lineno += 1 #mark end of offending line
|
| 721 |
+
if lineno == 1:
|
| 722 |
+
pos = "iomark + %d chars" % (offset-1)
|
| 723 |
+
else:
|
| 724 |
+
pos = "iomark linestart + %d lines + %d chars" % \
|
| 725 |
+
(lineno-1, offset-1)
|
| 726 |
+
tkconsole.colorize_syntax_error(text, pos)
|
| 727 |
+
tkconsole.resetoutput()
|
| 728 |
+
self.write("SyntaxError: %s\n" % msg)
|
| 729 |
+
tkconsole.showprompt()
|
| 730 |
+
|
| 731 |
+
def showtraceback(self):
|
| 732 |
+
"Extend base class method to reset output properly"
|
| 733 |
+
self.tkconsole.resetoutput()
|
| 734 |
+
self.checklinecache()
|
| 735 |
+
InteractiveInterpreter.showtraceback(self)
|
| 736 |
+
if self.tkconsole.getvar("<<toggle-jit-stack-viewer>>"):
|
| 737 |
+
self.tkconsole.open_stack_viewer()
|
| 738 |
+
|
| 739 |
+
def checklinecache(self):
|
| 740 |
+
c = linecache.cache
|
| 741 |
+
for key in list(c.keys()):
|
| 742 |
+
if key[:1] + key[-1:] != "<>":
|
| 743 |
+
del c[key]
|
| 744 |
+
|
| 745 |
+
def runcommand(self, code):
|
| 746 |
+
"Run the code without invoking the debugger"
|
| 747 |
+
# The code better not raise an exception!
|
| 748 |
+
if self.tkconsole.executing:
|
| 749 |
+
self.display_executing_dialog()
|
| 750 |
+
return 0
|
| 751 |
+
if self.rpcclt:
|
| 752 |
+
self.rpcclt.remotequeue("exec", "runcode", (code,), {})
|
| 753 |
+
else:
|
| 754 |
+
exec(code, self.locals)
|
| 755 |
+
return 1
|
| 756 |
+
|
| 757 |
+
def runcode(self, code):
|
| 758 |
+
"Override base class method"
|
| 759 |
+
if self.tkconsole.executing:
|
| 760 |
+
self.restart_subprocess()
|
| 761 |
+
self.checklinecache()
|
| 762 |
+
debugger = self.debugger
|
| 763 |
+
try:
|
| 764 |
+
self.tkconsole.beginexecuting()
|
| 765 |
+
if not debugger and self.rpcclt is not None:
|
| 766 |
+
self.active_seq = self.rpcclt.asyncqueue("exec", "runcode",
|
| 767 |
+
(code,), {})
|
| 768 |
+
elif debugger:
|
| 769 |
+
debugger.run(code, self.locals)
|
| 770 |
+
else:
|
| 771 |
+
exec(code, self.locals)
|
| 772 |
+
except SystemExit:
|
| 773 |
+
if not self.tkconsole.closing:
|
| 774 |
+
if messagebox.askyesno(
|
| 775 |
+
"Exit?",
|
| 776 |
+
"Do you want to exit altogether?",
|
| 777 |
+
default="yes",
|
| 778 |
+
parent=self.tkconsole.text):
|
| 779 |
+
raise
|
| 780 |
+
else:
|
| 781 |
+
self.showtraceback()
|
| 782 |
+
else:
|
| 783 |
+
raise
|
| 784 |
+
except:
|
| 785 |
+
if use_subprocess:
|
| 786 |
+
print("IDLE internal error in runcode()",
|
| 787 |
+
file=self.tkconsole.stderr)
|
| 788 |
+
self.showtraceback()
|
| 789 |
+
self.tkconsole.endexecuting()
|
| 790 |
+
else:
|
| 791 |
+
if self.tkconsole.canceled:
|
| 792 |
+
self.tkconsole.canceled = False
|
| 793 |
+
print("KeyboardInterrupt", file=self.tkconsole.stderr)
|
| 794 |
+
else:
|
| 795 |
+
self.showtraceback()
|
| 796 |
+
finally:
|
| 797 |
+
if not use_subprocess:
|
| 798 |
+
try:
|
| 799 |
+
self.tkconsole.endexecuting()
|
| 800 |
+
except AttributeError: # shell may have closed
|
| 801 |
+
pass
|
| 802 |
+
|
| 803 |
+
def write(self, s):
|
| 804 |
+
"Override base class method"
|
| 805 |
+
return self.tkconsole.stderr.write(s)
|
| 806 |
+
|
| 807 |
+
def display_port_binding_error(self):
|
| 808 |
+
messagebox.showerror(
|
| 809 |
+
"Port Binding Error",
|
| 810 |
+
"IDLE can't bind to a TCP/IP port, which is necessary to "
|
| 811 |
+
"communicate with its Python execution server. This might be "
|
| 812 |
+
"because no networking is installed on this computer. "
|
| 813 |
+
"Run IDLE with the -n command line switch to start without a "
|
| 814 |
+
"subprocess and refer to Help/IDLE Help 'Running without a "
|
| 815 |
+
"subprocess' for further details.",
|
| 816 |
+
parent=self.tkconsole.text)
|
| 817 |
+
|
| 818 |
+
def display_no_subprocess_error(self):
|
| 819 |
+
messagebox.showerror(
|
| 820 |
+
"Subprocess Connection Error",
|
| 821 |
+
"IDLE's subprocess didn't make connection.\n"
|
| 822 |
+
"See the 'Startup failure' section of the IDLE doc, online at\n"
|
| 823 |
+
"https://docs.python.org/3/library/idle.html#startup-failure",
|
| 824 |
+
parent=self.tkconsole.text)
|
| 825 |
+
|
| 826 |
+
def display_executing_dialog(self):
|
| 827 |
+
messagebox.showerror(
|
| 828 |
+
"Already executing",
|
| 829 |
+
"The Python Shell window is already executing a command; "
|
| 830 |
+
"please wait until it is finished.",
|
| 831 |
+
parent=self.tkconsole.text)
|
| 832 |
+
|
| 833 |
+
|
| 834 |
+
class PyShell(OutputWindow):
|
| 835 |
+
|
| 836 |
+
shell_title = "IDLE Shell " + python_version()
|
| 837 |
+
|
| 838 |
+
# Override classes
|
| 839 |
+
ColorDelegator = ModifiedColorDelegator
|
| 840 |
+
UndoDelegator = ModifiedUndoDelegator
|
| 841 |
+
|
| 842 |
+
# Override menus
|
| 843 |
+
menu_specs = [
|
| 844 |
+
("file", "_File"),
|
| 845 |
+
("edit", "_Edit"),
|
| 846 |
+
("debug", "_Debug"),
|
| 847 |
+
("options", "_Options"),
|
| 848 |
+
("window", "_Window"),
|
| 849 |
+
("help", "_Help"),
|
| 850 |
+
]
|
| 851 |
+
|
| 852 |
+
# Extend right-click context menu
|
| 853 |
+
rmenu_specs = OutputWindow.rmenu_specs + [
|
| 854 |
+
("Squeeze", "<<squeeze-current-text>>"),
|
| 855 |
+
]
|
| 856 |
+
|
| 857 |
+
allow_line_numbers = False
|
| 858 |
+
|
| 859 |
+
# New classes
|
| 860 |
+
from idlelib.history import History
|
| 861 |
+
|
| 862 |
+
def __init__(self, flist=None):
|
| 863 |
+
if use_subprocess:
|
| 864 |
+
ms = self.menu_specs
|
| 865 |
+
if ms[2][0] != "shell":
|
| 866 |
+
ms.insert(2, ("shell", "She_ll"))
|
| 867 |
+
self.interp = ModifiedInterpreter(self)
|
| 868 |
+
if flist is None:
|
| 869 |
+
root = Tk()
|
| 870 |
+
fixwordbreaks(root)
|
| 871 |
+
root.withdraw()
|
| 872 |
+
flist = PyShellFileList(root)
|
| 873 |
+
|
| 874 |
+
OutputWindow.__init__(self, flist, None, None)
|
| 875 |
+
|
| 876 |
+
self.usetabs = True
|
| 877 |
+
# indentwidth must be 8 when using tabs. See note in EditorWindow:
|
| 878 |
+
self.indentwidth = 8
|
| 879 |
+
|
| 880 |
+
self.sys_ps1 = sys.ps1 if hasattr(sys, 'ps1') else '>>> '
|
| 881 |
+
self.prompt_last_line = self.sys_ps1.split('\n')[-1]
|
| 882 |
+
self.prompt = self.sys_ps1 # Changes when debug active
|
| 883 |
+
|
| 884 |
+
text = self.text
|
| 885 |
+
text.configure(wrap="char")
|
| 886 |
+
text.bind("<<newline-and-indent>>", self.enter_callback)
|
| 887 |
+
text.bind("<<plain-newline-and-indent>>", self.linefeed_callback)
|
| 888 |
+
text.bind("<<interrupt-execution>>", self.cancel_callback)
|
| 889 |
+
text.bind("<<end-of-file>>", self.eof_callback)
|
| 890 |
+
text.bind("<<open-stack-viewer>>", self.open_stack_viewer)
|
| 891 |
+
text.bind("<<toggle-debugger>>", self.toggle_debugger)
|
| 892 |
+
text.bind("<<toggle-jit-stack-viewer>>", self.toggle_jit_stack_viewer)
|
| 893 |
+
if use_subprocess:
|
| 894 |
+
text.bind("<<view-restart>>", self.view_restart_mark)
|
| 895 |
+
text.bind("<<restart-shell>>", self.restart_shell)
|
| 896 |
+
squeezer = self.Squeezer(self)
|
| 897 |
+
text.bind("<<squeeze-current-text>>",
|
| 898 |
+
squeezer.squeeze_current_text_event)
|
| 899 |
+
|
| 900 |
+
self.save_stdout = sys.stdout
|
| 901 |
+
self.save_stderr = sys.stderr
|
| 902 |
+
self.save_stdin = sys.stdin
|
| 903 |
+
from idlelib import iomenu
|
| 904 |
+
self.stdin = StdInputFile(self, "stdin",
|
| 905 |
+
iomenu.encoding, iomenu.errors)
|
| 906 |
+
self.stdout = StdOutputFile(self, "stdout",
|
| 907 |
+
iomenu.encoding, iomenu.errors)
|
| 908 |
+
self.stderr = StdOutputFile(self, "stderr",
|
| 909 |
+
iomenu.encoding, "backslashreplace")
|
| 910 |
+
self.console = StdOutputFile(self, "console",
|
| 911 |
+
iomenu.encoding, iomenu.errors)
|
| 912 |
+
if not use_subprocess:
|
| 913 |
+
sys.stdout = self.stdout
|
| 914 |
+
sys.stderr = self.stderr
|
| 915 |
+
sys.stdin = self.stdin
|
| 916 |
+
try:
|
| 917 |
+
# page help() text to shell.
|
| 918 |
+
import pydoc # import must be done here to capture i/o rebinding.
|
| 919 |
+
# XXX KBK 27Dec07 use text viewer someday, but must work w/o subproc
|
| 920 |
+
pydoc.pager = pydoc.plainpager
|
| 921 |
+
except:
|
| 922 |
+
sys.stderr = sys.__stderr__
|
| 923 |
+
raise
|
| 924 |
+
#
|
| 925 |
+
self.history = self.History(self.text)
|
| 926 |
+
#
|
| 927 |
+
self.pollinterval = 50 # millisec
|
| 928 |
+
|
| 929 |
+
def get_standard_extension_names(self):
|
| 930 |
+
return idleConf.GetExtensions(shell_only=True)
|
| 931 |
+
|
| 932 |
+
reading = False
|
| 933 |
+
executing = False
|
| 934 |
+
canceled = False
|
| 935 |
+
endoffile = False
|
| 936 |
+
closing = False
|
| 937 |
+
_stop_readline_flag = False
|
| 938 |
+
|
| 939 |
+
def set_warning_stream(self, stream):
|
| 940 |
+
global warning_stream
|
| 941 |
+
warning_stream = stream
|
| 942 |
+
|
| 943 |
+
def get_warning_stream(self):
|
| 944 |
+
return warning_stream
|
| 945 |
+
|
| 946 |
+
def toggle_debugger(self, event=None):
|
| 947 |
+
if self.executing:
|
| 948 |
+
messagebox.showerror("Don't debug now",
|
| 949 |
+
"You can only toggle the debugger when idle",
|
| 950 |
+
parent=self.text)
|
| 951 |
+
self.set_debugger_indicator()
|
| 952 |
+
return "break"
|
| 953 |
+
else:
|
| 954 |
+
db = self.interp.getdebugger()
|
| 955 |
+
if db:
|
| 956 |
+
self.close_debugger()
|
| 957 |
+
else:
|
| 958 |
+
self.open_debugger()
|
| 959 |
+
|
| 960 |
+
def set_debugger_indicator(self):
|
| 961 |
+
db = self.interp.getdebugger()
|
| 962 |
+
self.setvar("<<toggle-debugger>>", not not db)
|
| 963 |
+
|
| 964 |
+
def toggle_jit_stack_viewer(self, event=None):
|
| 965 |
+
pass # All we need is the variable
|
| 966 |
+
|
| 967 |
+
def close_debugger(self):
|
| 968 |
+
db = self.interp.getdebugger()
|
| 969 |
+
if db:
|
| 970 |
+
self.interp.setdebugger(None)
|
| 971 |
+
db.close()
|
| 972 |
+
if self.interp.rpcclt:
|
| 973 |
+
debugger_r.close_remote_debugger(self.interp.rpcclt)
|
| 974 |
+
self.resetoutput()
|
| 975 |
+
self.console.write("[DEBUG OFF]\n")
|
| 976 |
+
self.prompt = self.sys_ps1
|
| 977 |
+
self.showprompt()
|
| 978 |
+
self.set_debugger_indicator()
|
| 979 |
+
|
| 980 |
+
def open_debugger(self):
|
| 981 |
+
if self.interp.rpcclt:
|
| 982 |
+
dbg_gui = debugger_r.start_remote_debugger(self.interp.rpcclt,
|
| 983 |
+
self)
|
| 984 |
+
else:
|
| 985 |
+
dbg_gui = debugger.Debugger(self)
|
| 986 |
+
self.interp.setdebugger(dbg_gui)
|
| 987 |
+
dbg_gui.load_breakpoints()
|
| 988 |
+
self.prompt = "[DEBUG ON]\n" + self.sys_ps1
|
| 989 |
+
self.showprompt()
|
| 990 |
+
self.set_debugger_indicator()
|
| 991 |
+
|
| 992 |
+
def debug_menu_postcommand(self):
|
| 993 |
+
state = 'disabled' if self.executing else 'normal'
|
| 994 |
+
self.update_menu_state('debug', '*tack*iewer', state)
|
| 995 |
+
|
| 996 |
+
def beginexecuting(self):
|
| 997 |
+
"Helper for ModifiedInterpreter"
|
| 998 |
+
self.resetoutput()
|
| 999 |
+
self.executing = True
|
| 1000 |
+
|
| 1001 |
+
def endexecuting(self):
|
| 1002 |
+
"Helper for ModifiedInterpreter"
|
| 1003 |
+
self.executing = False
|
| 1004 |
+
self.canceled = False
|
| 1005 |
+
self.showprompt()
|
| 1006 |
+
|
| 1007 |
+
def close(self):
|
| 1008 |
+
"Extend EditorWindow.close()"
|
| 1009 |
+
if self.executing:
|
| 1010 |
+
response = messagebox.askokcancel(
|
| 1011 |
+
"Kill?",
|
| 1012 |
+
"Your program is still running!\n Do you want to kill it?",
|
| 1013 |
+
default="ok",
|
| 1014 |
+
parent=self.text)
|
| 1015 |
+
if response is False:
|
| 1016 |
+
return "cancel"
|
| 1017 |
+
self.stop_readline()
|
| 1018 |
+
self.canceled = True
|
| 1019 |
+
self.closing = True
|
| 1020 |
+
return EditorWindow.close(self)
|
| 1021 |
+
|
| 1022 |
+
def _close(self):
|
| 1023 |
+
"Extend EditorWindow._close(), shut down debugger and execution server"
|
| 1024 |
+
self.close_debugger()
|
| 1025 |
+
if use_subprocess:
|
| 1026 |
+
self.interp.kill_subprocess()
|
| 1027 |
+
# Restore std streams
|
| 1028 |
+
sys.stdout = self.save_stdout
|
| 1029 |
+
sys.stderr = self.save_stderr
|
| 1030 |
+
sys.stdin = self.save_stdin
|
| 1031 |
+
# Break cycles
|
| 1032 |
+
self.interp = None
|
| 1033 |
+
self.console = None
|
| 1034 |
+
self.flist.pyshell = None
|
| 1035 |
+
self.history = None
|
| 1036 |
+
EditorWindow._close(self)
|
| 1037 |
+
|
| 1038 |
+
def ispythonsource(self, filename):
|
| 1039 |
+
"Override EditorWindow method: never remove the colorizer"
|
| 1040 |
+
return True
|
| 1041 |
+
|
| 1042 |
+
def short_title(self):
|
| 1043 |
+
return self.shell_title
|
| 1044 |
+
|
| 1045 |
+
COPYRIGHT = \
|
| 1046 |
+
'Type "help", "copyright", "credits" or "license()" for more information.'
|
| 1047 |
+
|
| 1048 |
+
def begin(self):
|
| 1049 |
+
self.text.mark_set("iomark", "insert")
|
| 1050 |
+
self.resetoutput()
|
| 1051 |
+
if use_subprocess:
|
| 1052 |
+
nosub = ''
|
| 1053 |
+
client = self.interp.start_subprocess()
|
| 1054 |
+
if not client:
|
| 1055 |
+
self.close()
|
| 1056 |
+
return False
|
| 1057 |
+
else:
|
| 1058 |
+
nosub = ("==== No Subprocess ====\n\n" +
|
| 1059 |
+
"WARNING: Running IDLE without a Subprocess is deprecated\n" +
|
| 1060 |
+
"and will be removed in a later version. See Help/IDLE Help\n" +
|
| 1061 |
+
"for details.\n\n")
|
| 1062 |
+
sys.displayhook = rpc.displayhook
|
| 1063 |
+
|
| 1064 |
+
self.write("Python %s on %s\n%s\n%s" %
|
| 1065 |
+
(sys.version, sys.platform, self.COPYRIGHT, nosub))
|
| 1066 |
+
self.text.focus_force()
|
| 1067 |
+
self.showprompt()
|
| 1068 |
+
# User code should use separate default Tk root window
|
| 1069 |
+
import tkinter
|
| 1070 |
+
tkinter._support_default_root = True
|
| 1071 |
+
tkinter._default_root = None
|
| 1072 |
+
return True
|
| 1073 |
+
|
| 1074 |
+
def stop_readline(self):
|
| 1075 |
+
if not self.reading: # no nested mainloop to exit.
|
| 1076 |
+
return
|
| 1077 |
+
self._stop_readline_flag = True
|
| 1078 |
+
self.top.quit()
|
| 1079 |
+
|
| 1080 |
+
def readline(self):
|
| 1081 |
+
save = self.reading
|
| 1082 |
+
try:
|
| 1083 |
+
self.reading = True
|
| 1084 |
+
self.top.mainloop() # nested mainloop()
|
| 1085 |
+
finally:
|
| 1086 |
+
self.reading = save
|
| 1087 |
+
if self._stop_readline_flag:
|
| 1088 |
+
self._stop_readline_flag = False
|
| 1089 |
+
return ""
|
| 1090 |
+
line = self.text.get("iomark", "end-1c")
|
| 1091 |
+
if len(line) == 0: # may be EOF if we quit our mainloop with Ctrl-C
|
| 1092 |
+
line = "\n"
|
| 1093 |
+
self.resetoutput()
|
| 1094 |
+
if self.canceled:
|
| 1095 |
+
self.canceled = False
|
| 1096 |
+
if not use_subprocess:
|
| 1097 |
+
raise KeyboardInterrupt
|
| 1098 |
+
if self.endoffile:
|
| 1099 |
+
self.endoffile = False
|
| 1100 |
+
line = ""
|
| 1101 |
+
return line
|
| 1102 |
+
|
| 1103 |
+
def isatty(self):
|
| 1104 |
+
return True
|
| 1105 |
+
|
| 1106 |
+
def cancel_callback(self, event=None):
|
| 1107 |
+
try:
|
| 1108 |
+
if self.text.compare("sel.first", "!=", "sel.last"):
|
| 1109 |
+
return # Active selection -- always use default binding
|
| 1110 |
+
except:
|
| 1111 |
+
pass
|
| 1112 |
+
if not (self.executing or self.reading):
|
| 1113 |
+
self.resetoutput()
|
| 1114 |
+
self.interp.write("KeyboardInterrupt\n")
|
| 1115 |
+
self.showprompt()
|
| 1116 |
+
return "break"
|
| 1117 |
+
self.endoffile = False
|
| 1118 |
+
self.canceled = True
|
| 1119 |
+
if (self.executing and self.interp.rpcclt):
|
| 1120 |
+
if self.interp.getdebugger():
|
| 1121 |
+
self.interp.restart_subprocess()
|
| 1122 |
+
else:
|
| 1123 |
+
self.interp.interrupt_subprocess()
|
| 1124 |
+
if self.reading:
|
| 1125 |
+
self.top.quit() # exit the nested mainloop() in readline()
|
| 1126 |
+
return "break"
|
| 1127 |
+
|
| 1128 |
+
def eof_callback(self, event):
|
| 1129 |
+
if self.executing and not self.reading:
|
| 1130 |
+
return # Let the default binding (delete next char) take over
|
| 1131 |
+
if not (self.text.compare("iomark", "==", "insert") and
|
| 1132 |
+
self.text.compare("insert", "==", "end-1c")):
|
| 1133 |
+
return # Let the default binding (delete next char) take over
|
| 1134 |
+
if not self.executing:
|
| 1135 |
+
self.resetoutput()
|
| 1136 |
+
self.close()
|
| 1137 |
+
else:
|
| 1138 |
+
self.canceled = False
|
| 1139 |
+
self.endoffile = True
|
| 1140 |
+
self.top.quit()
|
| 1141 |
+
return "break"
|
| 1142 |
+
|
| 1143 |
+
def linefeed_callback(self, event):
|
| 1144 |
+
# Insert a linefeed without entering anything (still autoindented)
|
| 1145 |
+
if self.reading:
|
| 1146 |
+
self.text.insert("insert", "\n")
|
| 1147 |
+
self.text.see("insert")
|
| 1148 |
+
else:
|
| 1149 |
+
self.newline_and_indent_event(event)
|
| 1150 |
+
return "break"
|
| 1151 |
+
|
| 1152 |
+
def enter_callback(self, event):
|
| 1153 |
+
if self.executing and not self.reading:
|
| 1154 |
+
return # Let the default binding (insert '\n') take over
|
| 1155 |
+
# If some text is selected, recall the selection
|
| 1156 |
+
# (but only if this before the I/O mark)
|
| 1157 |
+
try:
|
| 1158 |
+
sel = self.text.get("sel.first", "sel.last")
|
| 1159 |
+
if sel:
|
| 1160 |
+
if self.text.compare("sel.last", "<=", "iomark"):
|
| 1161 |
+
self.recall(sel, event)
|
| 1162 |
+
return "break"
|
| 1163 |
+
except:
|
| 1164 |
+
pass
|
| 1165 |
+
# If we're strictly before the line containing iomark, recall
|
| 1166 |
+
# the current line, less a leading prompt, less leading or
|
| 1167 |
+
# trailing whitespace
|
| 1168 |
+
if self.text.compare("insert", "<", "iomark linestart"):
|
| 1169 |
+
# Check if there's a relevant stdin range -- if so, use it
|
| 1170 |
+
prev = self.text.tag_prevrange("stdin", "insert")
|
| 1171 |
+
if prev and self.text.compare("insert", "<", prev[1]):
|
| 1172 |
+
self.recall(self.text.get(prev[0], prev[1]), event)
|
| 1173 |
+
return "break"
|
| 1174 |
+
next = self.text.tag_nextrange("stdin", "insert")
|
| 1175 |
+
if next and self.text.compare("insert lineend", ">=", next[0]):
|
| 1176 |
+
self.recall(self.text.get(next[0], next[1]), event)
|
| 1177 |
+
return "break"
|
| 1178 |
+
# No stdin mark -- just get the current line, less any prompt
|
| 1179 |
+
indices = self.text.tag_nextrange("console", "insert linestart")
|
| 1180 |
+
if indices and \
|
| 1181 |
+
self.text.compare(indices[0], "<=", "insert linestart"):
|
| 1182 |
+
self.recall(self.text.get(indices[1], "insert lineend"), event)
|
| 1183 |
+
else:
|
| 1184 |
+
self.recall(self.text.get("insert linestart", "insert lineend"), event)
|
| 1185 |
+
return "break"
|
| 1186 |
+
# If we're between the beginning of the line and the iomark, i.e.
|
| 1187 |
+
# in the prompt area, move to the end of the prompt
|
| 1188 |
+
if self.text.compare("insert", "<", "iomark"):
|
| 1189 |
+
self.text.mark_set("insert", "iomark")
|
| 1190 |
+
# If we're in the current input and there's only whitespace
|
| 1191 |
+
# beyond the cursor, erase that whitespace first
|
| 1192 |
+
s = self.text.get("insert", "end-1c")
|
| 1193 |
+
if s and not s.strip():
|
| 1194 |
+
self.text.delete("insert", "end-1c")
|
| 1195 |
+
# If we're in the current input before its last line,
|
| 1196 |
+
# insert a newline right at the insert point
|
| 1197 |
+
if self.text.compare("insert", "<", "end-1c linestart"):
|
| 1198 |
+
self.newline_and_indent_event(event)
|
| 1199 |
+
return "break"
|
| 1200 |
+
# We're in the last line; append a newline and submit it
|
| 1201 |
+
self.text.mark_set("insert", "end-1c")
|
| 1202 |
+
if self.reading:
|
| 1203 |
+
self.text.insert("insert", "\n")
|
| 1204 |
+
self.text.see("insert")
|
| 1205 |
+
else:
|
| 1206 |
+
self.newline_and_indent_event(event)
|
| 1207 |
+
self.text.tag_add("stdin", "iomark", "end-1c")
|
| 1208 |
+
self.text.update_idletasks()
|
| 1209 |
+
if self.reading:
|
| 1210 |
+
self.top.quit() # Break out of recursive mainloop()
|
| 1211 |
+
else:
|
| 1212 |
+
self.runit()
|
| 1213 |
+
return "break"
|
| 1214 |
+
|
| 1215 |
+
def recall(self, s, event):
|
| 1216 |
+
# remove leading and trailing empty or whitespace lines
|
| 1217 |
+
s = re.sub(r'^\s*\n', '' , s)
|
| 1218 |
+
s = re.sub(r'\n\s*$', '', s)
|
| 1219 |
+
lines = s.split('\n')
|
| 1220 |
+
self.text.undo_block_start()
|
| 1221 |
+
try:
|
| 1222 |
+
self.text.tag_remove("sel", "1.0", "end")
|
| 1223 |
+
self.text.mark_set("insert", "end-1c")
|
| 1224 |
+
prefix = self.text.get("insert linestart", "insert")
|
| 1225 |
+
if prefix.rstrip().endswith(':'):
|
| 1226 |
+
self.newline_and_indent_event(event)
|
| 1227 |
+
prefix = self.text.get("insert linestart", "insert")
|
| 1228 |
+
self.text.insert("insert", lines[0].strip())
|
| 1229 |
+
if len(lines) > 1:
|
| 1230 |
+
orig_base_indent = re.search(r'^([ \t]*)', lines[0]).group(0)
|
| 1231 |
+
new_base_indent = re.search(r'^([ \t]*)', prefix).group(0)
|
| 1232 |
+
for line in lines[1:]:
|
| 1233 |
+
if line.startswith(orig_base_indent):
|
| 1234 |
+
# replace orig base indentation with new indentation
|
| 1235 |
+
line = new_base_indent + line[len(orig_base_indent):]
|
| 1236 |
+
self.text.insert('insert', '\n'+line.rstrip())
|
| 1237 |
+
finally:
|
| 1238 |
+
self.text.see("insert")
|
| 1239 |
+
self.text.undo_block_stop()
|
| 1240 |
+
|
| 1241 |
+
def runit(self):
|
| 1242 |
+
line = self.text.get("iomark", "end-1c")
|
| 1243 |
+
# Strip off last newline and surrounding whitespace.
|
| 1244 |
+
# (To allow you to hit return twice to end a statement.)
|
| 1245 |
+
i = len(line)
|
| 1246 |
+
while i > 0 and line[i-1] in " \t":
|
| 1247 |
+
i = i-1
|
| 1248 |
+
if i > 0 and line[i-1] == "\n":
|
| 1249 |
+
i = i-1
|
| 1250 |
+
while i > 0 and line[i-1] in " \t":
|
| 1251 |
+
i = i-1
|
| 1252 |
+
line = line[:i]
|
| 1253 |
+
self.interp.runsource(line)
|
| 1254 |
+
|
| 1255 |
+
def open_stack_viewer(self, event=None):
|
| 1256 |
+
if self.interp.rpcclt:
|
| 1257 |
+
return self.interp.remote_stack_viewer()
|
| 1258 |
+
try:
|
| 1259 |
+
sys.last_traceback
|
| 1260 |
+
except:
|
| 1261 |
+
messagebox.showerror("No stack trace",
|
| 1262 |
+
"There is no stack trace yet.\n"
|
| 1263 |
+
"(sys.last_traceback is not defined)",
|
| 1264 |
+
parent=self.text)
|
| 1265 |
+
return
|
| 1266 |
+
from idlelib.stackviewer import StackBrowser
|
| 1267 |
+
StackBrowser(self.root, self.flist)
|
| 1268 |
+
|
| 1269 |
+
def view_restart_mark(self, event=None):
|
| 1270 |
+
self.text.see("iomark")
|
| 1271 |
+
self.text.see("restart")
|
| 1272 |
+
|
| 1273 |
+
def restart_shell(self, event=None):
|
| 1274 |
+
"Callback for Run/Restart Shell Cntl-F6"
|
| 1275 |
+
self.interp.restart_subprocess(with_cwd=True)
|
| 1276 |
+
|
| 1277 |
+
def showprompt(self):
|
| 1278 |
+
self.resetoutput()
|
| 1279 |
+
self.console.write(self.prompt)
|
| 1280 |
+
self.text.mark_set("insert", "end-1c")
|
| 1281 |
+
self.set_line_and_column()
|
| 1282 |
+
self.io.reset_undo()
|
| 1283 |
+
|
| 1284 |
+
def show_warning(self, msg):
|
| 1285 |
+
width = self.interp.tkconsole.width
|
| 1286 |
+
wrapper = TextWrapper(width=width, tabsize=8, expand_tabs=True)
|
| 1287 |
+
wrapped_msg = '\n'.join(wrapper.wrap(msg))
|
| 1288 |
+
if not wrapped_msg.endswith('\n'):
|
| 1289 |
+
wrapped_msg += '\n'
|
| 1290 |
+
self.per.bottom.insert("iomark linestart", wrapped_msg, "stderr")
|
| 1291 |
+
|
| 1292 |
+
def resetoutput(self):
|
| 1293 |
+
source = self.text.get("iomark", "end-1c")
|
| 1294 |
+
if self.history:
|
| 1295 |
+
self.history.store(source)
|
| 1296 |
+
if self.text.get("end-2c") != "\n":
|
| 1297 |
+
self.text.insert("end-1c", "\n")
|
| 1298 |
+
self.text.mark_set("iomark", "end-1c")
|
| 1299 |
+
self.set_line_and_column()
|
| 1300 |
+
self.ctip.remove_calltip_window()
|
| 1301 |
+
|
| 1302 |
+
def write(self, s, tags=()):
|
| 1303 |
+
try:
|
| 1304 |
+
self.text.mark_gravity("iomark", "right")
|
| 1305 |
+
count = OutputWindow.write(self, s, tags, "iomark")
|
| 1306 |
+
self.text.mark_gravity("iomark", "left")
|
| 1307 |
+
except:
|
| 1308 |
+
raise ###pass # ### 11Aug07 KBK if we are expecting exceptions
|
| 1309 |
+
# let's find out what they are and be specific.
|
| 1310 |
+
if self.canceled:
|
| 1311 |
+
self.canceled = False
|
| 1312 |
+
if not use_subprocess:
|
| 1313 |
+
raise KeyboardInterrupt
|
| 1314 |
+
return count
|
| 1315 |
+
|
| 1316 |
+
def rmenu_check_cut(self):
|
| 1317 |
+
try:
|
| 1318 |
+
if self.text.compare('sel.first', '<', 'iomark'):
|
| 1319 |
+
return 'disabled'
|
| 1320 |
+
except TclError: # no selection, so the index 'sel.first' doesn't exist
|
| 1321 |
+
return 'disabled'
|
| 1322 |
+
return super().rmenu_check_cut()
|
| 1323 |
+
|
| 1324 |
+
def rmenu_check_paste(self):
|
| 1325 |
+
if self.text.compare('insert','<','iomark'):
|
| 1326 |
+
return 'disabled'
|
| 1327 |
+
return super().rmenu_check_paste()
|
| 1328 |
+
|
| 1329 |
+
|
| 1330 |
+
def fix_x11_paste(root):
|
| 1331 |
+
"Make paste replace selection on x11. See issue #5124."
|
| 1332 |
+
if root._windowingsystem == 'x11':
|
| 1333 |
+
for cls in 'Text', 'Entry', 'Spinbox':
|
| 1334 |
+
root.bind_class(
|
| 1335 |
+
cls,
|
| 1336 |
+
'<<Paste>>',
|
| 1337 |
+
'catch {%W delete sel.first sel.last}\n' +
|
| 1338 |
+
root.bind_class(cls, '<<Paste>>'))
|
| 1339 |
+
|
| 1340 |
+
|
| 1341 |
+
usage_msg = """\
|
| 1342 |
+
|
| 1343 |
+
USAGE: idle [-deins] [-t title] [file]*
|
| 1344 |
+
idle [-dns] [-t title] (-c cmd | -r file) [arg]*
|
| 1345 |
+
idle [-dns] [-t title] - [arg]*
|
| 1346 |
+
|
| 1347 |
+
-h print this help message and exit
|
| 1348 |
+
-n run IDLE without a subprocess (DEPRECATED,
|
| 1349 |
+
see Help/IDLE Help for details)
|
| 1350 |
+
|
| 1351 |
+
The following options will override the IDLE 'settings' configuration:
|
| 1352 |
+
|
| 1353 |
+
-e open an edit window
|
| 1354 |
+
-i open a shell window
|
| 1355 |
+
|
| 1356 |
+
The following options imply -i and will open a shell:
|
| 1357 |
+
|
| 1358 |
+
-c cmd run the command in a shell, or
|
| 1359 |
+
-r file run script from file
|
| 1360 |
+
|
| 1361 |
+
-d enable the debugger
|
| 1362 |
+
-s run $IDLESTARTUP or $PYTHONSTARTUP before anything else
|
| 1363 |
+
-t title set title of shell window
|
| 1364 |
+
|
| 1365 |
+
A default edit window will be bypassed when -c, -r, or - are used.
|
| 1366 |
+
|
| 1367 |
+
[arg]* are passed to the command (-c) or script (-r) in sys.argv[1:].
|
| 1368 |
+
|
| 1369 |
+
Examples:
|
| 1370 |
+
|
| 1371 |
+
idle
|
| 1372 |
+
Open an edit window or shell depending on IDLE's configuration.
|
| 1373 |
+
|
| 1374 |
+
idle foo.py foobar.py
|
| 1375 |
+
Edit the files, also open a shell if configured to start with shell.
|
| 1376 |
+
|
| 1377 |
+
idle -est "Baz" foo.py
|
| 1378 |
+
Run $IDLESTARTUP or $PYTHONSTARTUP, edit foo.py, and open a shell
|
| 1379 |
+
window with the title "Baz".
|
| 1380 |
+
|
| 1381 |
+
idle -c "import sys; print(sys.argv)" "foo"
|
| 1382 |
+
Open a shell window and run the command, passing "-c" in sys.argv[0]
|
| 1383 |
+
and "foo" in sys.argv[1].
|
| 1384 |
+
|
| 1385 |
+
idle -d -s -r foo.py "Hello World"
|
| 1386 |
+
Open a shell window, run a startup script, enable the debugger, and
|
| 1387 |
+
run foo.py, passing "foo.py" in sys.argv[0] and "Hello World" in
|
| 1388 |
+
sys.argv[1].
|
| 1389 |
+
|
| 1390 |
+
echo "import sys; print(sys.argv)" | idle - "foobar"
|
| 1391 |
+
Open a shell window, run the script piped in, passing '' in sys.argv[0]
|
| 1392 |
+
and "foobar" in sys.argv[1].
|
| 1393 |
+
"""
|
| 1394 |
+
|
| 1395 |
+
def main():
|
| 1396 |
+
import getopt
|
| 1397 |
+
from platform import system
|
| 1398 |
+
from idlelib import testing # bool value
|
| 1399 |
+
from idlelib import macosx
|
| 1400 |
+
|
| 1401 |
+
global flist, root, use_subprocess
|
| 1402 |
+
|
| 1403 |
+
capture_warnings(True)
|
| 1404 |
+
use_subprocess = True
|
| 1405 |
+
enable_shell = False
|
| 1406 |
+
enable_edit = False
|
| 1407 |
+
debug = False
|
| 1408 |
+
cmd = None
|
| 1409 |
+
script = None
|
| 1410 |
+
startup = False
|
| 1411 |
+
try:
|
| 1412 |
+
opts, args = getopt.getopt(sys.argv[1:], "c:deihnr:st:")
|
| 1413 |
+
except getopt.error as msg:
|
| 1414 |
+
print("Error: %s\n%s" % (msg, usage_msg), file=sys.stderr)
|
| 1415 |
+
sys.exit(2)
|
| 1416 |
+
for o, a in opts:
|
| 1417 |
+
if o == '-c':
|
| 1418 |
+
cmd = a
|
| 1419 |
+
enable_shell = True
|
| 1420 |
+
if o == '-d':
|
| 1421 |
+
debug = True
|
| 1422 |
+
enable_shell = True
|
| 1423 |
+
if o == '-e':
|
| 1424 |
+
enable_edit = True
|
| 1425 |
+
if o == '-h':
|
| 1426 |
+
sys.stdout.write(usage_msg)
|
| 1427 |
+
sys.exit()
|
| 1428 |
+
if o == '-i':
|
| 1429 |
+
enable_shell = True
|
| 1430 |
+
if o == '-n':
|
| 1431 |
+
print(" Warning: running IDLE without a subprocess is deprecated.",
|
| 1432 |
+
file=sys.stderr)
|
| 1433 |
+
use_subprocess = False
|
| 1434 |
+
if o == '-r':
|
| 1435 |
+
script = a
|
| 1436 |
+
if os.path.isfile(script):
|
| 1437 |
+
pass
|
| 1438 |
+
else:
|
| 1439 |
+
print("No script file: ", script)
|
| 1440 |
+
sys.exit()
|
| 1441 |
+
enable_shell = True
|
| 1442 |
+
if o == '-s':
|
| 1443 |
+
startup = True
|
| 1444 |
+
enable_shell = True
|
| 1445 |
+
if o == '-t':
|
| 1446 |
+
PyShell.shell_title = a
|
| 1447 |
+
enable_shell = True
|
| 1448 |
+
if args and args[0] == '-':
|
| 1449 |
+
cmd = sys.stdin.read()
|
| 1450 |
+
enable_shell = True
|
| 1451 |
+
# process sys.argv and sys.path:
|
| 1452 |
+
for i in range(len(sys.path)):
|
| 1453 |
+
sys.path[i] = os.path.abspath(sys.path[i])
|
| 1454 |
+
if args and args[0] == '-':
|
| 1455 |
+
sys.argv = [''] + args[1:]
|
| 1456 |
+
elif cmd:
|
| 1457 |
+
sys.argv = ['-c'] + args
|
| 1458 |
+
elif script:
|
| 1459 |
+
sys.argv = [script] + args
|
| 1460 |
+
elif args:
|
| 1461 |
+
enable_edit = True
|
| 1462 |
+
pathx = []
|
| 1463 |
+
for filename in args:
|
| 1464 |
+
pathx.append(os.path.dirname(filename))
|
| 1465 |
+
for dir in pathx:
|
| 1466 |
+
dir = os.path.abspath(dir)
|
| 1467 |
+
if not dir in sys.path:
|
| 1468 |
+
sys.path.insert(0, dir)
|
| 1469 |
+
else:
|
| 1470 |
+
dir = os.getcwd()
|
| 1471 |
+
if dir not in sys.path:
|
| 1472 |
+
sys.path.insert(0, dir)
|
| 1473 |
+
# check the IDLE settings configuration (but command line overrides)
|
| 1474 |
+
edit_start = idleConf.GetOption('main', 'General',
|
| 1475 |
+
'editor-on-startup', type='bool')
|
| 1476 |
+
enable_edit = enable_edit or edit_start
|
| 1477 |
+
enable_shell = enable_shell or not enable_edit
|
| 1478 |
+
|
| 1479 |
+
# Setup root. Don't break user code run in IDLE process.
|
| 1480 |
+
# Don't change environment when testing.
|
| 1481 |
+
if use_subprocess and not testing:
|
| 1482 |
+
NoDefaultRoot()
|
| 1483 |
+
root = Tk(className="Idle")
|
| 1484 |
+
root.withdraw()
|
| 1485 |
+
from idlelib.run import fix_scaling
|
| 1486 |
+
fix_scaling(root)
|
| 1487 |
+
|
| 1488 |
+
# set application icon
|
| 1489 |
+
icondir = os.path.join(os.path.dirname(__file__), 'Icons')
|
| 1490 |
+
if system() == 'Windows':
|
| 1491 |
+
iconfile = os.path.join(icondir, 'idle.ico')
|
| 1492 |
+
root.wm_iconbitmap(default=iconfile)
|
| 1493 |
+
elif not macosx.isAquaTk():
|
| 1494 |
+
if TkVersion >= 8.6:
|
| 1495 |
+
ext = '.png'
|
| 1496 |
+
sizes = (16, 32, 48, 256)
|
| 1497 |
+
else:
|
| 1498 |
+
ext = '.gif'
|
| 1499 |
+
sizes = (16, 32, 48)
|
| 1500 |
+
iconfiles = [os.path.join(icondir, 'idle_%d%s' % (size, ext))
|
| 1501 |
+
for size in sizes]
|
| 1502 |
+
icons = [PhotoImage(master=root, file=iconfile)
|
| 1503 |
+
for iconfile in iconfiles]
|
| 1504 |
+
root.wm_iconphoto(True, *icons)
|
| 1505 |
+
|
| 1506 |
+
# start editor and/or shell windows:
|
| 1507 |
+
fixwordbreaks(root)
|
| 1508 |
+
fix_x11_paste(root)
|
| 1509 |
+
flist = PyShellFileList(root)
|
| 1510 |
+
macosx.setupApp(root, flist)
|
| 1511 |
+
|
| 1512 |
+
if enable_edit:
|
| 1513 |
+
if not (cmd or script):
|
| 1514 |
+
for filename in args[:]:
|
| 1515 |
+
if flist.open(filename) is None:
|
| 1516 |
+
# filename is a directory actually, disconsider it
|
| 1517 |
+
args.remove(filename)
|
| 1518 |
+
if not args:
|
| 1519 |
+
flist.new()
|
| 1520 |
+
|
| 1521 |
+
if enable_shell:
|
| 1522 |
+
shell = flist.open_shell()
|
| 1523 |
+
if not shell:
|
| 1524 |
+
return # couldn't open shell
|
| 1525 |
+
if macosx.isAquaTk() and flist.dict:
|
| 1526 |
+
# On OSX: when the user has double-clicked on a file that causes
|
| 1527 |
+
# IDLE to be launched the shell window will open just in front of
|
| 1528 |
+
# the file she wants to see. Lower the interpreter window when
|
| 1529 |
+
# there are open files.
|
| 1530 |
+
shell.top.lower()
|
| 1531 |
+
else:
|
| 1532 |
+
shell = flist.pyshell
|
| 1533 |
+
|
| 1534 |
+
# Handle remaining options. If any of these are set, enable_shell
|
| 1535 |
+
# was set also, so shell must be true to reach here.
|
| 1536 |
+
if debug:
|
| 1537 |
+
shell.open_debugger()
|
| 1538 |
+
if startup:
|
| 1539 |
+
filename = os.environ.get("IDLESTARTUP") or \
|
| 1540 |
+
os.environ.get("PYTHONSTARTUP")
|
| 1541 |
+
if filename and os.path.isfile(filename):
|
| 1542 |
+
shell.interp.execfile(filename)
|
| 1543 |
+
if cmd or script:
|
| 1544 |
+
shell.interp.runcommand("""if 1:
|
| 1545 |
+
import sys as _sys
|
| 1546 |
+
_sys.argv = %r
|
| 1547 |
+
del _sys
|
| 1548 |
+
\n""" % (sys.argv,))
|
| 1549 |
+
if cmd:
|
| 1550 |
+
shell.interp.execsource(cmd)
|
| 1551 |
+
elif script:
|
| 1552 |
+
shell.interp.prepend_syspath(script)
|
| 1553 |
+
shell.interp.execfile(script)
|
| 1554 |
+
elif shell:
|
| 1555 |
+
# If there is a shell window and no cmd or script in progress,
|
| 1556 |
+
# check for problematic issues and print warning message(s) in
|
| 1557 |
+
# the IDLE shell window; this is less intrusive than always
|
| 1558 |
+
# opening a separate window.
|
| 1559 |
+
|
| 1560 |
+
# Warn if using a problematic OS X Tk version.
|
| 1561 |
+
tkversionwarning = macosx.tkVersionWarning(root)
|
| 1562 |
+
if tkversionwarning:
|
| 1563 |
+
shell.show_warning(tkversionwarning)
|
| 1564 |
+
|
| 1565 |
+
# Warn if the "Prefer tabs when opening documents" system
|
| 1566 |
+
# preference is set to "Always".
|
| 1567 |
+
prefer_tabs_preference_warning = macosx.preferTabsPreferenceWarning()
|
| 1568 |
+
if prefer_tabs_preference_warning:
|
| 1569 |
+
shell.show_warning(prefer_tabs_preference_warning)
|
| 1570 |
+
|
| 1571 |
+
while flist.inversedict: # keep IDLE running while files are open.
|
| 1572 |
+
root.mainloop()
|
| 1573 |
+
root.destroy()
|
| 1574 |
+
capture_warnings(False)
|
| 1575 |
+
|
| 1576 |
+
if __name__ == "__main__":
|
| 1577 |
+
main()
|
| 1578 |
+
|
| 1579 |
+
capture_warnings(False) # Make sure turned off; see issue 18081
|