diff --git a/.gitattributes b/.gitattributes index af1a465afa15b4aff3845167280cdcbd853ec7f3..b82fa1b76c5ebd85aca49d3ee01c6f6ce077618f 100644 --- a/.gitattributes +++ b/.gitattributes @@ -148,3 +148,5 @@ my_container_sandbox/workspace/anaconda3/lib/libnppial.so filter=lfs diff=lfs me my_container_sandbox/workspace/anaconda3/lib/libsqlite3.so.0.8.6 filter=lfs diff=lfs merge=lfs -text my_container_sandbox/workspace/anaconda3/lib/libcudart.so filter=lfs diff=lfs merge=lfs -text my_container_sandbox/workspace/anaconda3/lib/libz.so.1.2.13 filter=lfs diff=lfs merge=lfs -text +my_container_sandbox/workspace/anaconda3/lib/libnppicc.so filter=lfs diff=lfs merge=lfs -text +my_container_sandbox/workspace/anaconda3/lib/libnppitc.so.11 filter=lfs diff=lfs merge=lfs -text diff --git a/my_container_sandbox/workspace/anaconda3/lib/itcl4.2.2/libitclstub4.2.2.a b/my_container_sandbox/workspace/anaconda3/lib/itcl4.2.2/libitclstub4.2.2.a new file mode 100644 index 0000000000000000000000000000000000000000..b72a075991d88910f8af8a69111d1d854ee05afe Binary files /dev/null and b/my_container_sandbox/workspace/anaconda3/lib/itcl4.2.2/libitclstub4.2.2.a differ diff --git a/my_container_sandbox/workspace/anaconda3/lib/libnppicc.so b/my_container_sandbox/workspace/anaconda3/lib/libnppicc.so new file mode 100644 index 0000000000000000000000000000000000000000..481d423bb6f78fc6e2de2ee781ae1a6f6f5944e4 --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/libnppicc.so @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c7c756f65b011b05e8bc1dfcd7d2856159d3dacb2e7753caa97965d862ca08b0 +size 6357432 diff --git a/my_container_sandbox/workspace/anaconda3/lib/libnppitc.so.11 b/my_container_sandbox/workspace/anaconda3/lib/libnppitc.so.11 new file mode 100644 index 0000000000000000000000000000000000000000..a9aed02bfa815157d581065cc61cca70600c5901 --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/libnppitc.so.11 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e59c811a2478ca8f1335e787c046eabaaa9a0ac1589b898f3f5bc10b57527503 +size 4306408 diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/__future__.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/__future__.py new file mode 100644 index 0000000000000000000000000000000000000000..0e7b5552343356d75cd17b64c33c0db276bc5848 --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/__future__.py @@ -0,0 +1,147 @@ +"""Record of phased-in incompatible language changes. + +Each line is of the form: + + FeatureName = "_Feature(" OptionalRelease "," MandatoryRelease "," + CompilerFlag ")" + +where, normally, OptionalRelease < MandatoryRelease, and both are 5-tuples +of the same form as sys.version_info: + + (PY_MAJOR_VERSION, # the 2 in 2.1.0a3; an int + PY_MINOR_VERSION, # the 1; an int + PY_MICRO_VERSION, # the 0; an int + PY_RELEASE_LEVEL, # "alpha", "beta", "candidate" or "final"; string + PY_RELEASE_SERIAL # the 3; an int + ) + +OptionalRelease records the first release in which + + from __future__ import FeatureName + +was accepted. + +In the case of MandatoryReleases that have not yet occurred, +MandatoryRelease predicts the release in which the feature will become part +of the language. + +Else MandatoryRelease records when the feature became part of the language; +in releases at or after that, modules no longer need + + from __future__ import FeatureName + +to use the feature in question, but may continue to use such imports. + +MandatoryRelease may also be None, meaning that a planned feature got +dropped. + +Instances of class _Feature have two corresponding methods, +.getOptionalRelease() and .getMandatoryRelease(). + +CompilerFlag is the (bitfield) flag that should be passed in the fourth +argument to the builtin function compile() to enable the feature in +dynamically compiled code. This flag is stored in the .compiler_flag +attribute on _Future instances. These values must match the appropriate +#defines of CO_xxx flags in Include/compile.h. + +No feature line is ever to be deleted from this file. +""" + +all_feature_names = [ + "nested_scopes", + "generators", + "division", + "absolute_import", + "with_statement", + "print_function", + "unicode_literals", + "barry_as_FLUFL", + "generator_stop", + "annotations", +] + +__all__ = ["all_feature_names"] + all_feature_names + +# The CO_xxx symbols are defined here under the same names defined in +# code.h and used by compile.h, so that an editor search will find them here. +# However, they're not exported in __all__, because they don't really belong to +# this module. +CO_NESTED = 0x0010 # nested_scopes +CO_GENERATOR_ALLOWED = 0 # generators (obsolete, was 0x1000) +CO_FUTURE_DIVISION = 0x20000 # division +CO_FUTURE_ABSOLUTE_IMPORT = 0x40000 # perform absolute imports by default +CO_FUTURE_WITH_STATEMENT = 0x80000 # with statement +CO_FUTURE_PRINT_FUNCTION = 0x100000 # print function +CO_FUTURE_UNICODE_LITERALS = 0x200000 # unicode string literals +CO_FUTURE_BARRY_AS_BDFL = 0x400000 +CO_FUTURE_GENERATOR_STOP = 0x800000 # StopIteration becomes RuntimeError in generators +CO_FUTURE_ANNOTATIONS = 0x1000000 # annotations become strings at runtime + + +class _Feature: + + def __init__(self, optionalRelease, mandatoryRelease, compiler_flag): + self.optional = optionalRelease + self.mandatory = mandatoryRelease + self.compiler_flag = compiler_flag + + def getOptionalRelease(self): + """Return first release in which this feature was recognized. + + This is a 5-tuple, of the same form as sys.version_info. + """ + return self.optional + + def getMandatoryRelease(self): + """Return release in which this feature will become mandatory. + + This is a 5-tuple, of the same form as sys.version_info, or, if + the feature was dropped, is None. + """ + return self.mandatory + + def __repr__(self): + return "_Feature" + repr((self.optional, + self.mandatory, + self.compiler_flag)) + + +nested_scopes = _Feature((2, 1, 0, "beta", 1), + (2, 2, 0, "alpha", 0), + CO_NESTED) + +generators = _Feature((2, 2, 0, "alpha", 1), + (2, 3, 0, "final", 0), + CO_GENERATOR_ALLOWED) + +division = _Feature((2, 2, 0, "alpha", 2), + (3, 0, 0, "alpha", 0), + CO_FUTURE_DIVISION) + +absolute_import = _Feature((2, 5, 0, "alpha", 1), + (3, 0, 0, "alpha", 0), + CO_FUTURE_ABSOLUTE_IMPORT) + +with_statement = _Feature((2, 5, 0, "alpha", 1), + (2, 6, 0, "alpha", 0), + CO_FUTURE_WITH_STATEMENT) + +print_function = _Feature((2, 6, 0, "alpha", 2), + (3, 0, 0, "alpha", 0), + CO_FUTURE_PRINT_FUNCTION) + +unicode_literals = _Feature((2, 6, 0, "alpha", 2), + (3, 0, 0, "alpha", 0), + CO_FUTURE_UNICODE_LITERALS) + +barry_as_FLUFL = _Feature((3, 1, 0, "alpha", 2), + (4, 0, 0, "alpha", 0), + CO_FUTURE_BARRY_AS_BDFL) + +generator_stop = _Feature((3, 5, 0, "beta", 1), + (3, 7, 0, "alpha", 0), + CO_FUTURE_GENERATOR_STOP) + +annotations = _Feature((3, 7, 0, "beta", 1), + (3, 10, 0, "alpha", 0), + CO_FUTURE_ANNOTATIONS) diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/_markupbase.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/_markupbase.py new file mode 100644 index 0000000000000000000000000000000000000000..2af5f1c23b6066284938fb1cc697bc1fc2fea6ea --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/_markupbase.py @@ -0,0 +1,395 @@ +"""Shared support for scanning document type declarations in HTML and XHTML. + +This module is used as a foundation for the html.parser module. It has no +documented public API and should not be used directly. + +""" + +import re + +_declname_match = re.compile(r'[a-zA-Z][-_.a-zA-Z0-9]*\s*').match +_declstringlit_match = re.compile(r'(\'[^\']*\'|"[^"]*")\s*').match +_commentclose = re.compile(r'--\s*>') +_markedsectionclose = re.compile(r']\s*]\s*>') + +# An analysis of the MS-Word extensions is available at +# http://www.planetpublish.com/xmlarena/xap/Thursday/WordtoXML.pdf + +_msmarkedsectionclose = re.compile(r']\s*>') + +del re + + +class ParserBase: + """Parser base class which provides some common support methods used + by the SGML/HTML and XHTML parsers.""" + + def __init__(self): + if self.__class__ is ParserBase: + raise RuntimeError( + "_markupbase.ParserBase must be subclassed") + + def error(self, message): + raise NotImplementedError( + "subclasses of ParserBase must override error()") + + def reset(self): + self.lineno = 1 + self.offset = 0 + + def getpos(self): + """Return current line number and offset.""" + return self.lineno, self.offset + + # Internal -- update line number and offset. This should be + # called for each piece of data exactly once, in order -- in other + # words the concatenation of all the input strings to this + # function should be exactly the entire input. + def updatepos(self, i, j): + if i >= j: + return j + rawdata = self.rawdata + nlines = rawdata.count("\n", i, j) + if nlines: + self.lineno = self.lineno + nlines + pos = rawdata.rindex("\n", i, j) # Should not fail + self.offset = j-(pos+1) + else: + self.offset = self.offset + j-i + return j + + _decl_otherchars = '' + + # Internal -- parse declaration (for use by subclasses). + def parse_declaration(self, i): + # This is some sort of declaration; in "HTML as + # deployed," this should only be the document type + # declaration (""). + # ISO 8879:1986, however, has more complex + # declaration syntax for elements in , including: + # --comment-- + # [marked section] + # name in the following list: ENTITY, DOCTYPE, ELEMENT, + # ATTLIST, NOTATION, SHORTREF, USEMAP, + # LINKTYPE, LINK, IDLINK, USELINK, SYSTEM + rawdata = self.rawdata + j = i + 2 + assert rawdata[i:j] == "": + # the empty comment + return j + 1 + if rawdata[j:j+1] in ("-", ""): + # Start of comment followed by buffer boundary, + # or just a buffer boundary. + return -1 + # A simple, practical version could look like: ((name|stringlit) S*) + '>' + n = len(rawdata) + if rawdata[j:j+2] == '--': #comment + # Locate --.*-- as the body of the comment + return self.parse_comment(i) + elif rawdata[j] == '[': #marked section + # Locate [statusWord [...arbitrary SGML...]] as the body of the marked section + # Where statusWord is one of TEMP, CDATA, IGNORE, INCLUDE, RCDATA + # Note that this is extended by Microsoft Office "Save as Web" function + # to include [if...] and [endif]. + return self.parse_marked_section(i) + else: #all other declaration elements + decltype, j = self._scan_name(j, i) + if j < 0: + return j + if decltype == "doctype": + self._decl_otherchars = '' + while j < n: + c = rawdata[j] + if c == ">": + # end of declaration syntax + data = rawdata[i+2:j] + if decltype == "doctype": + self.handle_decl(data) + else: + # According to the HTML5 specs sections "8.2.4.44 Bogus + # comment state" and "8.2.4.45 Markup declaration open + # state", a comment token should be emitted. + # Calling unknown_decl provides more flexibility though. + self.unknown_decl(data) + return j + 1 + if c in "\"'": + m = _declstringlit_match(rawdata, j) + if not m: + return -1 # incomplete + j = m.end() + elif c in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ": + name, j = self._scan_name(j, i) + elif c in self._decl_otherchars: + j = j + 1 + elif c == "[": + # this could be handled in a separate doctype parser + if decltype == "doctype": + j = self._parse_doctype_subset(j + 1, i) + elif decltype in {"attlist", "linktype", "link", "element"}: + # must tolerate []'d groups in a content model in an element declaration + # also in data attribute specifications of attlist declaration + # also link type declaration subsets in linktype declarations + # also link attribute specification lists in link declarations + self.error("unsupported '[' char in %s declaration" % decltype) + else: + self.error("unexpected '[' char in declaration") + else: + self.error( + "unexpected %r char in declaration" % rawdata[j]) + if j < 0: + return j + return -1 # incomplete + + # Internal -- parse a marked section + # Override this to handle MS-word extension syntax content + def parse_marked_section(self, i, report=1): + rawdata= self.rawdata + assert rawdata[i:i+3] == ' ending + match= _markedsectionclose.search(rawdata, i+3) + elif sectName in {"if", "else", "endif"}: + # look for MS Office ]> ending + match= _msmarkedsectionclose.search(rawdata, i+3) + else: + self.error('unknown status keyword %r in marked section' % rawdata[i+3:j]) + if not match: + return -1 + if report: + j = match.start(0) + self.unknown_decl(rawdata[i+3: j]) + return match.end(0) + + # Internal -- parse comment, return length or -1 if not terminated + def parse_comment(self, i, report=1): + rawdata = self.rawdata + if rawdata[i:i+4] != ' + --> --> + + ''' + +__UNDEF__ = [] # a special sentinel object +def small(text): + if text: + return '' + text + '' + else: + return '' + +def strong(text): + if text: + return '' + text + '' + else: + return '' + +def grey(text): + if text: + return '' + text + '' + else: + return '' + +def lookup(name, frame, locals): + """Find the value for a given name in the given environment.""" + if name in locals: + return 'local', locals[name] + if name in frame.f_globals: + return 'global', frame.f_globals[name] + if '__builtins__' in frame.f_globals: + builtins = frame.f_globals['__builtins__'] + if type(builtins) is type({}): + if name in builtins: + return 'builtin', builtins[name] + else: + if hasattr(builtins, name): + return 'builtin', getattr(builtins, name) + return None, __UNDEF__ + +def scanvars(reader, frame, locals): + """Scan one logical line of Python and look up values of variables used.""" + vars, lasttoken, parent, prefix, value = [], None, None, '', __UNDEF__ + for ttype, token, start, end, line in tokenize.generate_tokens(reader): + if ttype == tokenize.NEWLINE: break + if ttype == tokenize.NAME and token not in keyword.kwlist: + if lasttoken == '.': + if parent is not __UNDEF__: + value = getattr(parent, token, __UNDEF__) + vars.append((prefix + token, prefix, value)) + else: + where, value = lookup(token, frame, locals) + vars.append((token, where, value)) + elif token == '.': + prefix += lasttoken + '.' + parent = value + else: + parent, prefix = None, '' + lasttoken = token + return vars + +def html(einfo, context=5): + """Return a nice HTML document describing a given traceback.""" + etype, evalue, etb = einfo + if isinstance(etype, type): + etype = etype.__name__ + pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable + date = time.ctime(time.time()) + head = '' + pydoc.html.heading( + '%s' % + strong(pydoc.html.escape(str(etype))), + '#ffffff', '#6622aa', pyver + '
' + date) + ''' +

A problem occurred in a Python script. Here is the sequence of +function calls leading up to the error, in the order they occurred.

''' + + indent = '' + small(' ' * 5) + ' ' + frames = [] + records = inspect.getinnerframes(etb, context) + for frame, file, lnum, func, lines, index in records: + if file: + file = os.path.abspath(file) + link = '%s' % (file, pydoc.html.escape(file)) + else: + file = link = '?' + args, varargs, varkw, locals = inspect.getargvalues(frame) + call = '' + if func != '?': + call = 'in ' + strong(pydoc.html.escape(func)) + if func != "": + call += inspect.formatargvalues(args, varargs, varkw, locals, + formatvalue=lambda value: '=' + pydoc.html.repr(value)) + + highlight = {} + def reader(lnum=[lnum]): + highlight[lnum[0]] = 1 + try: return linecache.getline(file, lnum[0]) + finally: lnum[0] += 1 + vars = scanvars(reader, frame, locals) + + rows = ['%s%s %s' % + (' ', link, call)] + if index is not None: + i = lnum - index + for line in lines: + num = small(' ' * (5-len(str(i))) + str(i)) + ' ' + if i in highlight: + line = '=>%s%s' % (num, pydoc.html.preformat(line)) + rows.append('%s' % line) + else: + line = '  %s%s' % (num, pydoc.html.preformat(line)) + rows.append('%s' % grey(line)) + i += 1 + + done, dump = {}, [] + for name, where, value in vars: + if name in done: continue + done[name] = 1 + if value is not __UNDEF__: + if where in ('global', 'builtin'): + name = ('%s ' % where) + strong(name) + elif where == 'local': + name = strong(name) + else: + name = where + strong(name.split('.')[-1]) + dump.append('%s = %s' % (name, pydoc.html.repr(value))) + else: + dump.append(name + ' undefined') + + rows.append('%s' % small(grey(', '.join(dump)))) + frames.append(''' + +%s
''' % '\n'.join(rows)) + + exception = ['

%s: %s' % (strong(pydoc.html.escape(str(etype))), + pydoc.html.escape(str(evalue)))] + for name in dir(evalue): + if name[:1] == '_': continue + value = pydoc.html.repr(getattr(evalue, name)) + exception.append('\n
%s%s =\n%s' % (indent, name, value)) + + return head + ''.join(frames) + ''.join(exception) + ''' + + + +''' % pydoc.html.escape( + ''.join(traceback.format_exception(etype, evalue, etb))) + +def text(einfo, context=5): + """Return a plain text document describing a given traceback.""" + etype, evalue, etb = einfo + if isinstance(etype, type): + etype = etype.__name__ + pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable + date = time.ctime(time.time()) + head = "%s\n%s\n%s\n" % (str(etype), pyver, date) + ''' +A problem occurred in a Python script. Here is the sequence of +function calls leading up to the error, in the order they occurred. +''' + + frames = [] + records = inspect.getinnerframes(etb, context) + for frame, file, lnum, func, lines, index in records: + file = file and os.path.abspath(file) or '?' + args, varargs, varkw, locals = inspect.getargvalues(frame) + call = '' + if func != '?': + call = 'in ' + func + if func != "": + call += inspect.formatargvalues(args, varargs, varkw, locals, + formatvalue=lambda value: '=' + pydoc.text.repr(value)) + + highlight = {} + def reader(lnum=[lnum]): + highlight[lnum[0]] = 1 + try: return linecache.getline(file, lnum[0]) + finally: lnum[0] += 1 + vars = scanvars(reader, frame, locals) + + rows = [' %s %s' % (file, call)] + if index is not None: + i = lnum - index + for line in lines: + num = '%5d ' % i + rows.append(num+line.rstrip()) + i += 1 + + done, dump = {}, [] + for name, where, value in vars: + if name in done: continue + done[name] = 1 + if value is not __UNDEF__: + if where == 'global': name = 'global ' + name + elif where != 'local': name = where + name.split('.')[-1] + dump.append('%s = %s' % (name, pydoc.text.repr(value))) + else: + dump.append(name + ' undefined') + + rows.append('\n'.join(dump)) + frames.append('\n%s\n' % '\n'.join(rows)) + + exception = ['%s: %s' % (str(etype), str(evalue))] + for name in dir(evalue): + value = pydoc.text.repr(getattr(evalue, name)) + exception.append('\n%s%s = %s' % (" "*4, name, value)) + + return head + ''.join(frames) + ''.join(exception) + ''' + +The above is a description of an error in a Python program. Here is +the original traceback: + +%s +''' % ''.join(traceback.format_exception(etype, evalue, etb)) + +class Hook: + """A hook to replace sys.excepthook that shows tracebacks in HTML.""" + + def __init__(self, display=1, logdir=None, context=5, file=None, + format="html"): + self.display = display # send tracebacks to browser if true + self.logdir = logdir # log tracebacks to files if not None + self.context = context # number of source code lines per frame + self.file = file or sys.stdout # place to send the output + self.format = format + + def __call__(self, etype, evalue, etb): + self.handle((etype, evalue, etb)) + + def handle(self, info=None): + info = info or sys.exc_info() + if self.format == "html": + self.file.write(reset()) + + formatter = (self.format=="html") and html or text + plain = False + try: + doc = formatter(info, self.context) + except: # just in case something goes wrong + doc = ''.join(traceback.format_exception(*info)) + plain = True + + if self.display: + if plain: + doc = pydoc.html.escape(doc) + self.file.write('

' + doc + '
\n') + else: + self.file.write(doc + '\n') + else: + self.file.write('

A problem occurred in a Python script.\n') + + if self.logdir is not None: + suffix = ['.txt', '.html'][self.format=="html"] + (fd, path) = tempfile.mkstemp(suffix=suffix, dir=self.logdir) + + try: + with os.fdopen(fd, 'w') as file: + file.write(doc) + msg = '%s contains the description of this error.' % path + except: + msg = 'Tried to save traceback to %s, but failed.' % path + + if self.format == 'html': + self.file.write('

%s

\n' % msg) + else: + self.file.write(msg + '\n') + try: + self.file.flush() + except: pass + +handler = Hook().handle +def enable(display=1, logdir=None, context=5, format="html"): + """Install an exception handler that formats tracebacks as HTML. + + The optional argument 'display' can be set to 0 to suppress sending the + traceback to the browser, and 'logdir' can be set to a directory to cause + tracebacks to be written to files there.""" + sys.excepthook = Hook(display=display, logdir=logdir, + context=context, format=format) diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/code.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/code.py new file mode 100644 index 0000000000000000000000000000000000000000..76000f8c8b2c1e1c98f8fb4c831c2ea3e2de268d --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/code.py @@ -0,0 +1,315 @@ +"""Utilities needed to emulate Python's interactive interpreter. + +""" + +# Inspired by similar code by Jeff Epler and Fredrik Lundh. + + +import sys +import traceback +from codeop import CommandCompiler, compile_command + +__all__ = ["InteractiveInterpreter", "InteractiveConsole", "interact", + "compile_command"] + +class InteractiveInterpreter: + """Base class for InteractiveConsole. + + This class deals with parsing and interpreter state (the user's + namespace); it doesn't deal with input buffering or prompting or + input file naming (the filename is always passed in explicitly). + + """ + + def __init__(self, locals=None): + """Constructor. + + The optional 'locals' argument specifies the dictionary in + which code will be executed; it defaults to a newly created + dictionary with key "__name__" set to "__console__" and key + "__doc__" set to None. + + """ + if locals is None: + locals = {"__name__": "__console__", "__doc__": None} + self.locals = locals + self.compile = CommandCompiler() + + def runsource(self, source, filename="", symbol="single"): + """Compile and run some source in the interpreter. + + Arguments are as for compile_command(). + + One of several things can happen: + + 1) The input is incorrect; compile_command() raised an + exception (SyntaxError or OverflowError). A syntax traceback + will be printed by calling the showsyntaxerror() method. + + 2) The input is incomplete, and more input is required; + compile_command() returned None. Nothing happens. + + 3) The input is complete; compile_command() returned a code + object. The code is executed by calling self.runcode() (which + also handles run-time exceptions, except for SystemExit). + + The return value is True in case 2, False in the other cases (unless + an exception is raised). The return value can be used to + decide whether to use sys.ps1 or sys.ps2 to prompt the next + line. + + """ + try: + code = self.compile(source, filename, symbol) + except (OverflowError, SyntaxError, ValueError): + # Case 1 + self.showsyntaxerror(filename) + return False + + if code is None: + # Case 2 + return True + + # Case 3 + self.runcode(code) + return False + + def runcode(self, code): + """Execute a code object. + + When an exception occurs, self.showtraceback() is called to + display a traceback. All exceptions are caught except + SystemExit, which is reraised. + + A note about KeyboardInterrupt: this exception may occur + elsewhere in this code, and may not always be caught. The + caller should be prepared to deal with it. + + """ + try: + exec(code, self.locals) + except SystemExit: + raise + except: + self.showtraceback() + + def showsyntaxerror(self, filename=None): + """Display the syntax error that just occurred. + + This doesn't display a stack trace because there isn't one. + + If a filename is given, it is stuffed in the exception instead + of what was there before (because Python's parser always uses + "" when reading from a string). + + The output is written by self.write(), below. + + """ + type, value, tb = sys.exc_info() + sys.last_type = type + sys.last_value = value + sys.last_traceback = tb + if filename and type is SyntaxError: + # Work hard to stuff the correct filename in the exception + try: + msg, (dummy_filename, lineno, offset, line) = value.args + except ValueError: + # Not the format we expect; leave it alone + pass + else: + # Stuff in the right filename + value = SyntaxError(msg, (filename, lineno, offset, line)) + sys.last_value = value + if sys.excepthook is sys.__excepthook__: + lines = traceback.format_exception_only(type, value) + self.write(''.join(lines)) + else: + # If someone has set sys.excepthook, we let that take precedence + # over self.write + sys.excepthook(type, value, tb) + + def showtraceback(self): + """Display the exception that just occurred. + + We remove the first stack item because it is our own code. + + The output is written by self.write(), below. + + """ + sys.last_type, sys.last_value, last_tb = ei = sys.exc_info() + sys.last_traceback = last_tb + try: + lines = traceback.format_exception(ei[0], ei[1], last_tb.tb_next) + if sys.excepthook is sys.__excepthook__: + self.write(''.join(lines)) + else: + # If someone has set sys.excepthook, we let that take precedence + # over self.write + sys.excepthook(ei[0], ei[1], last_tb) + finally: + last_tb = ei = None + + def write(self, data): + """Write a string. + + The base implementation writes to sys.stderr; a subclass may + replace this with a different implementation. + + """ + sys.stderr.write(data) + + +class InteractiveConsole(InteractiveInterpreter): + """Closely emulate the behavior of the interactive Python interpreter. + + This class builds on InteractiveInterpreter and adds prompting + using the familiar sys.ps1 and sys.ps2, and input buffering. + + """ + + def __init__(self, locals=None, filename=""): + """Constructor. + + The optional locals argument will be passed to the + InteractiveInterpreter base class. + + The optional filename argument should specify the (file)name + of the input stream; it will show up in tracebacks. + + """ + InteractiveInterpreter.__init__(self, locals) + self.filename = filename + self.resetbuffer() + + def resetbuffer(self): + """Reset the input buffer.""" + self.buffer = [] + + def interact(self, banner=None, exitmsg=None): + """Closely emulate the interactive Python console. + + The optional banner argument specifies the banner to print + before the first interaction; by default it prints a banner + similar to the one printed by the real Python interpreter, + followed by the current class name in parentheses (so as not + to confuse this with the real interpreter -- since it's so + close!). + + The optional exitmsg argument specifies the exit message + printed when exiting. Pass the empty string to suppress + printing an exit message. If exitmsg is not given or None, + a default message is printed. + + """ + try: + sys.ps1 + except AttributeError: + sys.ps1 = ">>> " + try: + sys.ps2 + except AttributeError: + sys.ps2 = "... " + cprt = 'Type "help", "copyright", "credits" or "license" for more information.' + if banner is None: + self.write("Python %s on %s\n%s\n(%s)\n" % + (sys.version, sys.platform, cprt, + self.__class__.__name__)) + elif banner: + self.write("%s\n" % str(banner)) + more = 0 + while 1: + try: + if more: + prompt = sys.ps2 + else: + prompt = sys.ps1 + try: + line = self.raw_input(prompt) + except EOFError: + self.write("\n") + break + else: + more = self.push(line) + except KeyboardInterrupt: + self.write("\nKeyboardInterrupt\n") + self.resetbuffer() + more = 0 + if exitmsg is None: + self.write('now exiting %s...\n' % self.__class__.__name__) + elif exitmsg != '': + self.write('%s\n' % exitmsg) + + def push(self, line): + """Push a line to the interpreter. + + The line should not have a trailing newline; it may have + internal newlines. The line is appended to a buffer and the + interpreter's runsource() method is called with the + concatenated contents of the buffer as source. If this + indicates that the command was executed or invalid, the buffer + is reset; otherwise, the command is incomplete, and the buffer + is left as it was after the line was appended. The return + value is 1 if more input is required, 0 if the line was dealt + with in some way (this is the same as runsource()). + + """ + self.buffer.append(line) + source = "\n".join(self.buffer) + more = self.runsource(source, self.filename) + if not more: + self.resetbuffer() + return more + + def raw_input(self, prompt=""): + """Write a prompt and read a line. + + The returned line does not include the trailing newline. + When the user enters the EOF key sequence, EOFError is raised. + + The base implementation uses the built-in function + input(); a subclass may replace this with a different + implementation. + + """ + return input(prompt) + + + +def interact(banner=None, readfunc=None, local=None, exitmsg=None): + """Closely emulate the interactive Python interpreter. + + This is a backwards compatible interface to the InteractiveConsole + class. When readfunc is not specified, it attempts to import the + readline module to enable GNU readline if it is available. + + Arguments (all optional, all default to None): + + banner -- passed to InteractiveConsole.interact() + readfunc -- if not None, replaces InteractiveConsole.raw_input() + local -- passed to InteractiveInterpreter.__init__() + exitmsg -- passed to InteractiveConsole.interact() + + """ + console = InteractiveConsole(local) + if readfunc is not None: + console.raw_input = readfunc + else: + try: + import readline + except ImportError: + pass + console.interact(banner, exitmsg) + + +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser() + parser.add_argument('-q', action='store_true', + help="don't print version and copyright messages") + args = parser.parse_args() + if args.q or sys.flags.quiet: + banner = '' + else: + banner = None + interact(banner) diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/codecs.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/codecs.py new file mode 100644 index 0000000000000000000000000000000000000000..7f23e9775df804e79e4d0869a29dc05e1396a892 --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/codecs.py @@ -0,0 +1,1126 @@ +""" codecs -- Python Codec Registry, API and helpers. + + +Written by Marc-Andre Lemburg (mal@lemburg.com). + +(c) Copyright CNRI, All Rights Reserved. NO WARRANTY. + +""" + +import builtins +import sys + +### Registry and builtin stateless codec functions + +try: + from _codecs import * +except ImportError as why: + raise SystemError('Failed to load the builtin codecs: %s' % why) + +__all__ = ["register", "lookup", "open", "EncodedFile", "BOM", "BOM_BE", + "BOM_LE", "BOM32_BE", "BOM32_LE", "BOM64_BE", "BOM64_LE", + "BOM_UTF8", "BOM_UTF16", "BOM_UTF16_LE", "BOM_UTF16_BE", + "BOM_UTF32", "BOM_UTF32_LE", "BOM_UTF32_BE", + "CodecInfo", "Codec", "IncrementalEncoder", "IncrementalDecoder", + "StreamReader", "StreamWriter", + "StreamReaderWriter", "StreamRecoder", + "getencoder", "getdecoder", "getincrementalencoder", + "getincrementaldecoder", "getreader", "getwriter", + "encode", "decode", "iterencode", "iterdecode", + "strict_errors", "ignore_errors", "replace_errors", + "xmlcharrefreplace_errors", + "backslashreplace_errors", "namereplace_errors", + "register_error", "lookup_error"] + +### Constants + +# +# Byte Order Mark (BOM = ZERO WIDTH NO-BREAK SPACE = U+FEFF) +# and its possible byte string values +# for UTF8/UTF16/UTF32 output and little/big endian machines +# + +# UTF-8 +BOM_UTF8 = b'\xef\xbb\xbf' + +# UTF-16, little endian +BOM_LE = BOM_UTF16_LE = b'\xff\xfe' + +# UTF-16, big endian +BOM_BE = BOM_UTF16_BE = b'\xfe\xff' + +# UTF-32, little endian +BOM_UTF32_LE = b'\xff\xfe\x00\x00' + +# UTF-32, big endian +BOM_UTF32_BE = b'\x00\x00\xfe\xff' + +if sys.byteorder == 'little': + + # UTF-16, native endianness + BOM = BOM_UTF16 = BOM_UTF16_LE + + # UTF-32, native endianness + BOM_UTF32 = BOM_UTF32_LE + +else: + + # UTF-16, native endianness + BOM = BOM_UTF16 = BOM_UTF16_BE + + # UTF-32, native endianness + BOM_UTF32 = BOM_UTF32_BE + +# Old broken names (don't use in new code) +BOM32_LE = BOM_UTF16_LE +BOM32_BE = BOM_UTF16_BE +BOM64_LE = BOM_UTF32_LE +BOM64_BE = BOM_UTF32_BE + + +### Codec base classes (defining the API) + +class CodecInfo(tuple): + """Codec details when looking up the codec registry""" + + # Private API to allow Python 3.4 to blacklist the known non-Unicode + # codecs in the standard library. A more general mechanism to + # reliably distinguish test encodings from other codecs will hopefully + # be defined for Python 3.5 + # + # See http://bugs.python.org/issue19619 + _is_text_encoding = True # Assume codecs are text encodings by default + + def __new__(cls, encode, decode, streamreader=None, streamwriter=None, + incrementalencoder=None, incrementaldecoder=None, name=None, + *, _is_text_encoding=None): + self = tuple.__new__(cls, (encode, decode, streamreader, streamwriter)) + self.name = name + self.encode = encode + self.decode = decode + self.incrementalencoder = incrementalencoder + self.incrementaldecoder = incrementaldecoder + self.streamwriter = streamwriter + self.streamreader = streamreader + if _is_text_encoding is not None: + self._is_text_encoding = _is_text_encoding + return self + + def __repr__(self): + return "<%s.%s object for encoding %s at %#x>" % \ + (self.__class__.__module__, self.__class__.__qualname__, + self.name, id(self)) + +class Codec: + + """ Defines the interface for stateless encoders/decoders. + + The .encode()/.decode() methods may use different error + handling schemes by providing the errors argument. These + string values are predefined: + + 'strict' - raise a ValueError error (or a subclass) + 'ignore' - ignore the character and continue with the next + 'replace' - replace with a suitable replacement character; + Python will use the official U+FFFD REPLACEMENT + CHARACTER for the builtin Unicode codecs on + decoding and '?' on encoding. + 'surrogateescape' - replace with private code points U+DCnn. + 'xmlcharrefreplace' - Replace with the appropriate XML + character reference (only for encoding). + 'backslashreplace' - Replace with backslashed escape sequences. + 'namereplace' - Replace with \\N{...} escape sequences + (only for encoding). + + The set of allowed values can be extended via register_error. + + """ + def encode(self, input, errors='strict'): + + """ Encodes the object input and returns a tuple (output + object, length consumed). + + errors defines the error handling to apply. It defaults to + 'strict' handling. + + The method may not store state in the Codec instance. Use + StreamWriter for codecs which have to keep state in order to + make encoding efficient. + + The encoder must be able to handle zero length input and + return an empty object of the output object type in this + situation. + + """ + raise NotImplementedError + + def decode(self, input, errors='strict'): + + """ Decodes the object input and returns a tuple (output + object, length consumed). + + input must be an object which provides the bf_getreadbuf + buffer slot. Python strings, buffer objects and memory + mapped files are examples of objects providing this slot. + + errors defines the error handling to apply. It defaults to + 'strict' handling. + + The method may not store state in the Codec instance. Use + StreamReader for codecs which have to keep state in order to + make decoding efficient. + + The decoder must be able to handle zero length input and + return an empty object of the output object type in this + situation. + + """ + raise NotImplementedError + +class IncrementalEncoder(object): + """ + An IncrementalEncoder encodes an input in multiple steps. The input can + be passed piece by piece to the encode() method. The IncrementalEncoder + remembers the state of the encoding process between calls to encode(). + """ + def __init__(self, errors='strict'): + """ + Creates an IncrementalEncoder instance. + + The IncrementalEncoder may use different error handling schemes by + providing the errors keyword argument. See the module docstring + for a list of possible values. + """ + self.errors = errors + self.buffer = "" + + def encode(self, input, final=False): + """ + Encodes input and returns the resulting object. + """ + raise NotImplementedError + + def reset(self): + """ + Resets the encoder to the initial state. + """ + + def getstate(self): + """ + Return the current state of the encoder. + """ + return 0 + + def setstate(self, state): + """ + Set the current state of the encoder. state must have been + returned by getstate(). + """ + +class BufferedIncrementalEncoder(IncrementalEncoder): + """ + This subclass of IncrementalEncoder can be used as the baseclass for an + incremental encoder if the encoder must keep some of the output in a + buffer between calls to encode(). + """ + def __init__(self, errors='strict'): + IncrementalEncoder.__init__(self, errors) + # unencoded input that is kept between calls to encode() + self.buffer = "" + + def _buffer_encode(self, input, errors, final): + # Overwrite this method in subclasses: It must encode input + # and return an (output, length consumed) tuple + raise NotImplementedError + + def encode(self, input, final=False): + # encode input (taking the buffer into account) + data = self.buffer + input + (result, consumed) = self._buffer_encode(data, self.errors, final) + # keep unencoded input until the next call + self.buffer = data[consumed:] + return result + + def reset(self): + IncrementalEncoder.reset(self) + self.buffer = "" + + def getstate(self): + return self.buffer or 0 + + def setstate(self, state): + self.buffer = state or "" + +class IncrementalDecoder(object): + """ + An IncrementalDecoder decodes an input in multiple steps. The input can + be passed piece by piece to the decode() method. The IncrementalDecoder + remembers the state of the decoding process between calls to decode(). + """ + def __init__(self, errors='strict'): + """ + Create an IncrementalDecoder instance. + + The IncrementalDecoder may use different error handling schemes by + providing the errors keyword argument. See the module docstring + for a list of possible values. + """ + self.errors = errors + + def decode(self, input, final=False): + """ + Decode input and returns the resulting object. + """ + raise NotImplementedError + + def reset(self): + """ + Reset the decoder to the initial state. + """ + + def getstate(self): + """ + Return the current state of the decoder. + + This must be a (buffered_input, additional_state_info) tuple. + buffered_input must be a bytes object containing bytes that + were passed to decode() that have not yet been converted. + additional_state_info must be a non-negative integer + representing the state of the decoder WITHOUT yet having + processed the contents of buffered_input. In the initial state + and after reset(), getstate() must return (b"", 0). + """ + return (b"", 0) + + def setstate(self, state): + """ + Set the current state of the decoder. + + state must have been returned by getstate(). The effect of + setstate((b"", 0)) must be equivalent to reset(). + """ + +class BufferedIncrementalDecoder(IncrementalDecoder): + """ + This subclass of IncrementalDecoder can be used as the baseclass for an + incremental decoder if the decoder must be able to handle incomplete + byte sequences. + """ + def __init__(self, errors='strict'): + IncrementalDecoder.__init__(self, errors) + # undecoded input that is kept between calls to decode() + self.buffer = b"" + + def _buffer_decode(self, input, errors, final): + # Overwrite this method in subclasses: It must decode input + # and return an (output, length consumed) tuple + raise NotImplementedError + + def decode(self, input, final=False): + # decode input (taking the buffer into account) + data = self.buffer + input + (result, consumed) = self._buffer_decode(data, self.errors, final) + # keep undecoded input until the next call + self.buffer = data[consumed:] + return result + + def reset(self): + IncrementalDecoder.reset(self) + self.buffer = b"" + + def getstate(self): + # additional state info is always 0 + return (self.buffer, 0) + + def setstate(self, state): + # ignore additional state info + self.buffer = state[0] + +# +# The StreamWriter and StreamReader class provide generic working +# interfaces which can be used to implement new encoding submodules +# very easily. See encodings/utf_8.py for an example on how this is +# done. +# + +class StreamWriter(Codec): + + def __init__(self, stream, errors='strict'): + + """ Creates a StreamWriter instance. + + stream must be a file-like object open for writing. + + The StreamWriter may use different error handling + schemes by providing the errors keyword argument. These + parameters are predefined: + + 'strict' - raise a ValueError (or a subclass) + 'ignore' - ignore the character and continue with the next + 'replace'- replace with a suitable replacement character + 'xmlcharrefreplace' - Replace with the appropriate XML + character reference. + 'backslashreplace' - Replace with backslashed escape + sequences. + 'namereplace' - Replace with \\N{...} escape sequences. + + The set of allowed parameter values can be extended via + register_error. + """ + self.stream = stream + self.errors = errors + + def write(self, object): + + """ Writes the object's contents encoded to self.stream. + """ + data, consumed = self.encode(object, self.errors) + self.stream.write(data) + + def writelines(self, list): + + """ Writes the concatenated list of strings to the stream + using .write(). + """ + self.write(''.join(list)) + + def reset(self): + + """ Flushes and resets the codec buffers used for keeping state. + + Calling this method should ensure that the data on the + output is put into a clean state, that allows appending + of new fresh data without having to rescan the whole + stream to recover state. + + """ + pass + + def seek(self, offset, whence=0): + self.stream.seek(offset, whence) + if whence == 0 and offset == 0: + self.reset() + + def __getattr__(self, name, + getattr=getattr): + + """ Inherit all other methods from the underlying stream. + """ + return getattr(self.stream, name) + + def __enter__(self): + return self + + def __exit__(self, type, value, tb): + self.stream.close() + +### + +class StreamReader(Codec): + + charbuffertype = str + + def __init__(self, stream, errors='strict'): + + """ Creates a StreamReader instance. + + stream must be a file-like object open for reading. + + The StreamReader may use different error handling + schemes by providing the errors keyword argument. These + parameters are predefined: + + 'strict' - raise a ValueError (or a subclass) + 'ignore' - ignore the character and continue with the next + 'replace'- replace with a suitable replacement character + 'backslashreplace' - Replace with backslashed escape sequences; + + The set of allowed parameter values can be extended via + register_error. + """ + self.stream = stream + self.errors = errors + self.bytebuffer = b"" + self._empty_charbuffer = self.charbuffertype() + self.charbuffer = self._empty_charbuffer + self.linebuffer = None + + def decode(self, input, errors='strict'): + raise NotImplementedError + + def read(self, size=-1, chars=-1, firstline=False): + + """ Decodes data from the stream self.stream and returns the + resulting object. + + chars indicates the number of decoded code points or bytes to + return. read() will never return more data than requested, + but it might return less, if there is not enough available. + + size indicates the approximate maximum number of decoded + bytes or code points to read for decoding. The decoder + can modify this setting as appropriate. The default value + -1 indicates to read and decode as much as possible. size + is intended to prevent having to decode huge files in one + step. + + If firstline is true, and a UnicodeDecodeError happens + after the first line terminator in the input only the first line + will be returned, the rest of the input will be kept until the + next call to read(). + + The method should use a greedy read strategy, meaning that + it should read as much data as is allowed within the + definition of the encoding and the given size, e.g. if + optional encoding endings or state markers are available + on the stream, these should be read too. + """ + # If we have lines cached, first merge them back into characters + if self.linebuffer: + self.charbuffer = self._empty_charbuffer.join(self.linebuffer) + self.linebuffer = None + + if chars < 0: + # For compatibility with other read() methods that take a + # single argument + chars = size + + # read until we get the required number of characters (if available) + while True: + # can the request be satisfied from the character buffer? + if chars >= 0: + if len(self.charbuffer) >= chars: + break + # we need more data + if size < 0: + newdata = self.stream.read() + else: + newdata = self.stream.read(size) + # decode bytes (those remaining from the last call included) + data = self.bytebuffer + newdata + if not data: + break + try: + newchars, decodedbytes = self.decode(data, self.errors) + except UnicodeDecodeError as exc: + if firstline: + newchars, decodedbytes = \ + self.decode(data[:exc.start], self.errors) + lines = newchars.splitlines(keepends=True) + if len(lines)<=1: + raise + else: + raise + # keep undecoded bytes until the next call + self.bytebuffer = data[decodedbytes:] + # put new characters in the character buffer + self.charbuffer += newchars + # there was no data available + if not newdata: + break + if chars < 0: + # Return everything we've got + result = self.charbuffer + self.charbuffer = self._empty_charbuffer + else: + # Return the first chars characters + result = self.charbuffer[:chars] + self.charbuffer = self.charbuffer[chars:] + return result + + def readline(self, size=None, keepends=True): + + """ Read one line from the input stream and return the + decoded data. + + size, if given, is passed as size argument to the + read() method. + + """ + # If we have lines cached from an earlier read, return + # them unconditionally + if self.linebuffer: + line = self.linebuffer[0] + del self.linebuffer[0] + if len(self.linebuffer) == 1: + # revert to charbuffer mode; we might need more data + # next time + self.charbuffer = self.linebuffer[0] + self.linebuffer = None + if not keepends: + line = line.splitlines(keepends=False)[0] + return line + + readsize = size or 72 + line = self._empty_charbuffer + # If size is given, we call read() only once + while True: + data = self.read(readsize, firstline=True) + if data: + # If we're at a "\r" read one extra character (which might + # be a "\n") to get a proper line ending. If the stream is + # temporarily exhausted we return the wrong line ending. + if (isinstance(data, str) and data.endswith("\r")) or \ + (isinstance(data, bytes) and data.endswith(b"\r")): + data += self.read(size=1, chars=1) + + line += data + lines = line.splitlines(keepends=True) + if lines: + if len(lines) > 1: + # More than one line result; the first line is a full line + # to return + line = lines[0] + del lines[0] + if len(lines) > 1: + # cache the remaining lines + lines[-1] += self.charbuffer + self.linebuffer = lines + self.charbuffer = None + else: + # only one remaining line, put it back into charbuffer + self.charbuffer = lines[0] + self.charbuffer + if not keepends: + line = line.splitlines(keepends=False)[0] + break + line0withend = lines[0] + line0withoutend = lines[0].splitlines(keepends=False)[0] + if line0withend != line0withoutend: # We really have a line end + # Put the rest back together and keep it until the next call + self.charbuffer = self._empty_charbuffer.join(lines[1:]) + \ + self.charbuffer + if keepends: + line = line0withend + else: + line = line0withoutend + break + # we didn't get anything or this was our only try + if not data or size is not None: + if line and not keepends: + line = line.splitlines(keepends=False)[0] + break + if readsize < 8000: + readsize *= 2 + return line + + def readlines(self, sizehint=None, keepends=True): + + """ Read all lines available on the input stream + and return them as a list. + + Line breaks are implemented using the codec's decoder + method and are included in the list entries. + + sizehint, if given, is ignored since there is no efficient + way to finding the true end-of-line. + + """ + data = self.read() + return data.splitlines(keepends) + + def reset(self): + + """ Resets the codec buffers used for keeping state. + + Note that no stream repositioning should take place. + This method is primarily intended to be able to recover + from decoding errors. + + """ + self.bytebuffer = b"" + self.charbuffer = self._empty_charbuffer + self.linebuffer = None + + def seek(self, offset, whence=0): + """ Set the input stream's current position. + + Resets the codec buffers used for keeping state. + """ + self.stream.seek(offset, whence) + self.reset() + + def __next__(self): + + """ Return the next decoded line from the input stream.""" + line = self.readline() + if line: + return line + raise StopIteration + + def __iter__(self): + return self + + def __getattr__(self, name, + getattr=getattr): + + """ Inherit all other methods from the underlying stream. + """ + return getattr(self.stream, name) + + def __enter__(self): + return self + + def __exit__(self, type, value, tb): + self.stream.close() + +### + +class StreamReaderWriter: + + """ StreamReaderWriter instances allow wrapping streams which + work in both read and write modes. + + The design is such that one can use the factory functions + returned by the codec.lookup() function to construct the + instance. + + """ + # Optional attributes set by the file wrappers below + encoding = 'unknown' + + def __init__(self, stream, Reader, Writer, errors='strict'): + + """ Creates a StreamReaderWriter instance. + + stream must be a Stream-like object. + + Reader, Writer must be factory functions or classes + providing the StreamReader, StreamWriter interface resp. + + Error handling is done in the same way as defined for the + StreamWriter/Readers. + + """ + self.stream = stream + self.reader = Reader(stream, errors) + self.writer = Writer(stream, errors) + self.errors = errors + + def read(self, size=-1): + + return self.reader.read(size) + + def readline(self, size=None): + + return self.reader.readline(size) + + def readlines(self, sizehint=None): + + return self.reader.readlines(sizehint) + + def __next__(self): + + """ Return the next decoded line from the input stream.""" + return next(self.reader) + + def __iter__(self): + return self + + def write(self, data): + + return self.writer.write(data) + + def writelines(self, list): + + return self.writer.writelines(list) + + def reset(self): + + self.reader.reset() + self.writer.reset() + + def seek(self, offset, whence=0): + self.stream.seek(offset, whence) + self.reader.reset() + if whence == 0 and offset == 0: + self.writer.reset() + + def __getattr__(self, name, + getattr=getattr): + + """ Inherit all other methods from the underlying stream. + """ + return getattr(self.stream, name) + + # these are needed to make "with StreamReaderWriter(...)" work properly + + def __enter__(self): + return self + + def __exit__(self, type, value, tb): + self.stream.close() + +### + +class StreamRecoder: + + """ StreamRecoder instances translate data from one encoding to another. + + They use the complete set of APIs returned by the + codecs.lookup() function to implement their task. + + Data written to the StreamRecoder is first decoded into an + intermediate format (depending on the "decode" codec) and then + written to the underlying stream using an instance of the provided + Writer class. + + In the other direction, data is read from the underlying stream using + a Reader instance and then encoded and returned to the caller. + + """ + # Optional attributes set by the file wrappers below + data_encoding = 'unknown' + file_encoding = 'unknown' + + def __init__(self, stream, encode, decode, Reader, Writer, + errors='strict'): + + """ Creates a StreamRecoder instance which implements a two-way + conversion: encode and decode work on the frontend (the + data visible to .read() and .write()) while Reader and Writer + work on the backend (the data in stream). + + You can use these objects to do transparent + transcodings from e.g. latin-1 to utf-8 and back. + + stream must be a file-like object. + + encode and decode must adhere to the Codec interface; Reader and + Writer must be factory functions or classes providing the + StreamReader and StreamWriter interfaces resp. + + Error handling is done in the same way as defined for the + StreamWriter/Readers. + + """ + self.stream = stream + self.encode = encode + self.decode = decode + self.reader = Reader(stream, errors) + self.writer = Writer(stream, errors) + self.errors = errors + + def read(self, size=-1): + + data = self.reader.read(size) + data, bytesencoded = self.encode(data, self.errors) + return data + + def readline(self, size=None): + + if size is None: + data = self.reader.readline() + else: + data = self.reader.readline(size) + data, bytesencoded = self.encode(data, self.errors) + return data + + def readlines(self, sizehint=None): + + data = self.reader.read() + data, bytesencoded = self.encode(data, self.errors) + return data.splitlines(keepends=True) + + def __next__(self): + + """ Return the next decoded line from the input stream.""" + data = next(self.reader) + data, bytesencoded = self.encode(data, self.errors) + return data + + def __iter__(self): + return self + + def write(self, data): + + data, bytesdecoded = self.decode(data, self.errors) + return self.writer.write(data) + + def writelines(self, list): + + data = b''.join(list) + data, bytesdecoded = self.decode(data, self.errors) + return self.writer.write(data) + + def reset(self): + + self.reader.reset() + self.writer.reset() + + def seek(self, offset, whence=0): + # Seeks must be propagated to both the readers and writers + # as they might need to reset their internal buffers. + self.reader.seek(offset, whence) + self.writer.seek(offset, whence) + + def __getattr__(self, name, + getattr=getattr): + + """ Inherit all other methods from the underlying stream. + """ + return getattr(self.stream, name) + + def __enter__(self): + return self + + def __exit__(self, type, value, tb): + self.stream.close() + +### Shortcuts + +def open(filename, mode='r', encoding=None, errors='strict', buffering=-1): + + """ Open an encoded file using the given mode and return + a wrapped version providing transparent encoding/decoding. + + Note: The wrapped version will only accept the object format + defined by the codecs, i.e. Unicode objects for most builtin + codecs. Output is also codec dependent and will usually be + Unicode as well. + + Underlying encoded files are always opened in binary mode. + The default file mode is 'r', meaning to open the file in read mode. + + encoding specifies the encoding which is to be used for the + file. + + errors may be given to define the error handling. It defaults + to 'strict' which causes ValueErrors to be raised in case an + encoding error occurs. + + buffering has the same meaning as for the builtin open() API. + It defaults to -1 which means that the default buffer size will + be used. + + The returned wrapped file object provides an extra attribute + .encoding which allows querying the used encoding. This + attribute is only available if an encoding was specified as + parameter. + + """ + if encoding is not None and \ + 'b' not in mode: + # Force opening of the file in binary mode + mode = mode + 'b' + file = builtins.open(filename, mode, buffering) + if encoding is None: + return file + + try: + info = lookup(encoding) + srw = StreamReaderWriter(file, info.streamreader, info.streamwriter, errors) + # Add attributes to simplify introspection + srw.encoding = encoding + return srw + except: + file.close() + raise + +def EncodedFile(file, data_encoding, file_encoding=None, errors='strict'): + + """ Return a wrapped version of file which provides transparent + encoding translation. + + Data written to the wrapped file is decoded according + to the given data_encoding and then encoded to the underlying + file using file_encoding. The intermediate data type + will usually be Unicode but depends on the specified codecs. + + Bytes read from the file are decoded using file_encoding and then + passed back to the caller encoded using data_encoding. + + If file_encoding is not given, it defaults to data_encoding. + + errors may be given to define the error handling. It defaults + to 'strict' which causes ValueErrors to be raised in case an + encoding error occurs. + + The returned wrapped file object provides two extra attributes + .data_encoding and .file_encoding which reflect the given + parameters of the same name. The attributes can be used for + introspection by Python programs. + + """ + if file_encoding is None: + file_encoding = data_encoding + data_info = lookup(data_encoding) + file_info = lookup(file_encoding) + sr = StreamRecoder(file, data_info.encode, data_info.decode, + file_info.streamreader, file_info.streamwriter, errors) + # Add attributes to simplify introspection + sr.data_encoding = data_encoding + sr.file_encoding = file_encoding + return sr + +### Helpers for codec lookup + +def getencoder(encoding): + + """ Lookup up the codec for the given encoding and return + its encoder function. + + Raises a LookupError in case the encoding cannot be found. + + """ + return lookup(encoding).encode + +def getdecoder(encoding): + + """ Lookup up the codec for the given encoding and return + its decoder function. + + Raises a LookupError in case the encoding cannot be found. + + """ + return lookup(encoding).decode + +def getincrementalencoder(encoding): + + """ Lookup up the codec for the given encoding and return + its IncrementalEncoder class or factory function. + + Raises a LookupError in case the encoding cannot be found + or the codecs doesn't provide an incremental encoder. + + """ + encoder = lookup(encoding).incrementalencoder + if encoder is None: + raise LookupError(encoding) + return encoder + +def getincrementaldecoder(encoding): + + """ Lookup up the codec for the given encoding and return + its IncrementalDecoder class or factory function. + + Raises a LookupError in case the encoding cannot be found + or the codecs doesn't provide an incremental decoder. + + """ + decoder = lookup(encoding).incrementaldecoder + if decoder is None: + raise LookupError(encoding) + return decoder + +def getreader(encoding): + + """ Lookup up the codec for the given encoding and return + its StreamReader class or factory function. + + Raises a LookupError in case the encoding cannot be found. + + """ + return lookup(encoding).streamreader + +def getwriter(encoding): + + """ Lookup up the codec for the given encoding and return + its StreamWriter class or factory function. + + Raises a LookupError in case the encoding cannot be found. + + """ + return lookup(encoding).streamwriter + +def iterencode(iterator, encoding, errors='strict', **kwargs): + """ + Encoding iterator. + + Encodes the input strings from the iterator using an IncrementalEncoder. + + errors and kwargs are passed through to the IncrementalEncoder + constructor. + """ + encoder = getincrementalencoder(encoding)(errors, **kwargs) + for input in iterator: + output = encoder.encode(input) + if output: + yield output + output = encoder.encode("", True) + if output: + yield output + +def iterdecode(iterator, encoding, errors='strict', **kwargs): + """ + Decoding iterator. + + Decodes the input strings from the iterator using an IncrementalDecoder. + + errors and kwargs are passed through to the IncrementalDecoder + constructor. + """ + decoder = getincrementaldecoder(encoding)(errors, **kwargs) + for input in iterator: + output = decoder.decode(input) + if output: + yield output + output = decoder.decode(b"", True) + if output: + yield output + +### Helpers for charmap-based codecs + +def make_identity_dict(rng): + + """ make_identity_dict(rng) -> dict + + Return a dictionary where elements of the rng sequence are + mapped to themselves. + + """ + return {i:i for i in rng} + +def make_encoding_map(decoding_map): + + """ Creates an encoding map from a decoding map. + + If a target mapping in the decoding map occurs multiple + times, then that target is mapped to None (undefined mapping), + causing an exception when encountered by the charmap codec + during translation. + + One example where this happens is cp875.py which decodes + multiple character to \\u001a. + + """ + m = {} + for k,v in decoding_map.items(): + if not v in m: + m[v] = k + else: + m[v] = None + return m + +### error handlers + +try: + strict_errors = lookup_error("strict") + ignore_errors = lookup_error("ignore") + replace_errors = lookup_error("replace") + xmlcharrefreplace_errors = lookup_error("xmlcharrefreplace") + backslashreplace_errors = lookup_error("backslashreplace") + namereplace_errors = lookup_error("namereplace") +except LookupError: + # In --disable-unicode builds, these error handler are missing + strict_errors = None + ignore_errors = None + replace_errors = None + xmlcharrefreplace_errors = None + backslashreplace_errors = None + namereplace_errors = None + +# Tell modulefinder that using codecs probably needs the encodings +# package +_false = 0 +if _false: + import encodings + +### Tests + +if __name__ == '__main__': + + # Make stdout translate Latin-1 output into UTF-8 output + sys.stdout = EncodedFile(sys.stdout, 'latin-1', 'utf-8') + + # Have stdin translate Latin-1 input into UTF-8 input + sys.stdin = EncodedFile(sys.stdin, 'utf-8', 'latin-1') diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/codeop.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/codeop.py new file mode 100644 index 0000000000000000000000000000000000000000..04ca6b9317087c07c907d2d9a9fd035ba1cc11d1 --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/codeop.py @@ -0,0 +1,178 @@ +r"""Utilities to compile possibly incomplete Python source code. + +This module provides two interfaces, broadly similar to the builtin +function compile(), which take program text, a filename and a 'mode' +and: + +- Return code object if the command is complete and valid +- Return None if the command is incomplete +- Raise SyntaxError, ValueError or OverflowError if the command is a + syntax error (OverflowError and ValueError can be produced by + malformed literals). + +Approach: + +First, check if the source consists entirely of blank lines and +comments; if so, replace it with 'pass', because the built-in +parser doesn't always do the right thing for these. + +Compile three times: as is, with \n, and with \n\n appended. If it +compiles as is, it's complete. If it compiles with one \n appended, +we expect more. If it doesn't compile either way, we compare the +error we get when compiling with \n or \n\n appended. If the errors +are the same, the code is broken. But if the errors are different, we +expect more. Not intuitive; not even guaranteed to hold in future +releases; but this matches the compiler's behavior from Python 1.4 +through 2.2, at least. + +Caveat: + +It is possible (but not likely) that the parser stops parsing with a +successful outcome before reaching the end of the source; in this +case, trailing symbols may be ignored instead of causing an error. +For example, a backslash followed by two newlines may be followed by +arbitrary garbage. This will be fixed once the API for the parser is +better. + +The two interfaces are: + +compile_command(source, filename, symbol): + + Compiles a single command in the manner described above. + +CommandCompiler(): + + Instances of this class have __call__ methods identical in + signature to compile_command; the difference is that if the + instance compiles program text containing a __future__ statement, + the instance 'remembers' and compiles all subsequent program texts + with the statement in force. + +The module also provides another class: + +Compile(): + + Instances of this class act like the built-in function compile, + but with 'memory' in the sense described above. +""" + +import __future__ +import warnings + +_features = [getattr(__future__, fname) + for fname in __future__.all_feature_names] + +__all__ = ["compile_command", "Compile", "CommandCompiler"] + +PyCF_DONT_IMPLY_DEDENT = 0x200 # Matches pythonrun.h + +def _maybe_compile(compiler, source, filename, symbol): + # Check for source consisting of only blank lines and comments + for line in source.split("\n"): + line = line.strip() + if line and line[0] != '#': + break # Leave it alone + else: + if symbol != "eval": + source = "pass" # Replace it with a 'pass' statement + + err = err1 = err2 = None + code = code1 = code2 = None + + try: + code = compiler(source, filename, symbol) + except SyntaxError as err: + pass + + # Catch syntax warnings after the first compile + # to emit warnings (SyntaxWarning, DeprecationWarning) at most once. + with warnings.catch_warnings(): + warnings.simplefilter("error") + + try: + code1 = compiler(source + "\n", filename, symbol) + except SyntaxError as e: + err1 = e + + try: + code2 = compiler(source + "\n\n", filename, symbol) + except SyntaxError as e: + err2 = e + + try: + if code: + return code + if not code1 and repr(err1) == repr(err2): + raise err1 + finally: + err1 = err2 = None + +def _compile(source, filename, symbol): + return compile(source, filename, symbol, PyCF_DONT_IMPLY_DEDENT) + +def compile_command(source, filename="", symbol="single"): + r"""Compile a command and determine whether it is incomplete. + + Arguments: + + source -- the source string; may contain \n characters + filename -- optional filename from which source was read; default + "" + symbol -- optional grammar start symbol; "single" (default), "exec" + or "eval" + + Return value / exceptions raised: + + - Return a code object if the command is complete and valid + - Return None if the command is incomplete + - Raise SyntaxError, ValueError or OverflowError if the command is a + syntax error (OverflowError and ValueError can be produced by + malformed literals). + """ + return _maybe_compile(_compile, source, filename, symbol) + +class Compile: + """Instances of this class behave much like the built-in compile + function, but if one is used to compile text containing a future + statement, it "remembers" and compiles all subsequent program texts + with the statement in force.""" + def __init__(self): + self.flags = PyCF_DONT_IMPLY_DEDENT + + def __call__(self, source, filename, symbol): + codeob = compile(source, filename, symbol, self.flags, 1) + for feature in _features: + if codeob.co_flags & feature.compiler_flag: + self.flags |= feature.compiler_flag + return codeob + +class CommandCompiler: + """Instances of this class have __call__ methods identical in + signature to compile_command; the difference is that if the + instance compiles program text containing a __future__ statement, + the instance 'remembers' and compiles all subsequent program texts + with the statement in force.""" + + def __init__(self,): + self.compiler = Compile() + + def __call__(self, source, filename="", symbol="single"): + r"""Compile a command and determine whether it is incomplete. + + Arguments: + + source -- the source string; may contain \n characters + filename -- optional filename from which source was read; + default "" + symbol -- optional grammar start symbol; "single" (default) or + "eval" + + Return value / exceptions raised: + + - Return a code object if the command is complete and valid + - Return None if the command is incomplete + - Raise SyntaxError, ValueError or OverflowError if the command is a + syntax error (OverflowError and ValueError can be produced by + malformed literals). + """ + return _maybe_compile(self.compiler, source, filename, symbol) diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/crypt.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/crypt.py new file mode 100644 index 0000000000000000000000000000000000000000..8846602d7613ec76c365021ab500252a3f0546e9 --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/crypt.py @@ -0,0 +1,112 @@ +"""Wrapper to the POSIX crypt library call and associated functionality.""" + +import sys as _sys + +try: + import _crypt +except ModuleNotFoundError: + if _sys.platform == 'win32': + raise ImportError("The crypt module is not supported on Windows") + else: + raise ImportError("The required _crypt module was not built as part of CPython") + +import string as _string +from random import SystemRandom as _SystemRandom +from collections import namedtuple as _namedtuple + + +_saltchars = _string.ascii_letters + _string.digits + './' +_sr = _SystemRandom() + + +class _Method(_namedtuple('_Method', 'name ident salt_chars total_size')): + + """Class representing a salt method per the Modular Crypt Format or the + legacy 2-character crypt method.""" + + def __repr__(self): + return ''.format(self.name) + + +def mksalt(method=None, *, rounds=None): + """Generate a salt for the specified method. + + If not specified, the strongest available method will be used. + + """ + if method is None: + method = methods[0] + if rounds is not None and not isinstance(rounds, int): + raise TypeError(f'{rounds.__class__.__name__} object cannot be ' + f'interpreted as an integer') + if not method.ident: # traditional + s = '' + else: # modular + s = f'${method.ident}$' + + if method.ident and method.ident[0] == '2': # Blowfish variants + if rounds is None: + log_rounds = 12 + else: + log_rounds = int.bit_length(rounds-1) + if rounds != 1 << log_rounds: + raise ValueError('rounds must be a power of 2') + if not 4 <= log_rounds <= 31: + raise ValueError('rounds out of the range 2**4 to 2**31') + s += f'{log_rounds:02d}$' + elif method.ident in ('5', '6'): # SHA-2 + if rounds is not None: + if not 1000 <= rounds <= 999_999_999: + raise ValueError('rounds out of the range 1000 to 999_999_999') + s += f'rounds={rounds}$' + elif rounds is not None: + raise ValueError(f"{method} doesn't support the rounds argument") + + s += ''.join(_sr.choice(_saltchars) for char in range(method.salt_chars)) + return s + + +def crypt(word, salt=None): + """Return a string representing the one-way hash of a password, with a salt + prepended. + + If ``salt`` is not specified or is ``None``, the strongest + available method will be selected and a salt generated. Otherwise, + ``salt`` may be one of the ``crypt.METHOD_*`` values, or a string as + returned by ``crypt.mksalt()``. + + """ + if salt is None or isinstance(salt, _Method): + salt = mksalt(salt) + return _crypt.crypt(word, salt) + + +# available salting/crypto methods +methods = [] + +def _add_method(name, *args, rounds=None): + method = _Method(name, *args) + globals()['METHOD_' + name] = method + salt = mksalt(method, rounds=rounds) + result = crypt('', salt) + if result and len(result) == method.total_size: + methods.append(method) + return True + return False + +_add_method('SHA512', '6', 16, 106) +_add_method('SHA256', '5', 16, 63) + +# Choose the strongest supported version of Blowfish hashing. +# Early versions have flaws. Version 'a' fixes flaws of +# the initial implementation, 'b' fixes flaws of 'a'. +# 'y' is the same as 'b', for compatibility +# with openwall crypt_blowfish. +for _v in 'b', 'y', 'a', '': + if _add_method('BLOWFISH', '2' + _v, 22, 59 + len(_v), rounds=1<<4): + break + +_add_method('MD5', '1', 8, 34) +_add_method('CRYPT', None, 2, 13) + +del _v, _add_method diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/decimal.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/decimal.py new file mode 100644 index 0000000000000000000000000000000000000000..7746ea2601024ceb7e750472753fcfc80fa2bd01 --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/decimal.py @@ -0,0 +1,11 @@ + +try: + from _decimal import * + from _decimal import __doc__ + from _decimal import __version__ + from _decimal import __libmpdec_version__ +except ImportError: + from _pydecimal import * + from _pydecimal import __doc__ + from _pydecimal import __version__ + from _pydecimal import __libmpdec_version__ diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/dis.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/dis.py new file mode 100644 index 0000000000000000000000000000000000000000..e289e176c78ffd78ee6e42a6b41acc0298f63933 --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/dis.py @@ -0,0 +1,553 @@ +"""Disassembler of Python byte code into mnemonics.""" + +import sys +import types +import collections +import io + +from opcode import * +from opcode import __all__ as _opcodes_all + +__all__ = ["code_info", "dis", "disassemble", "distb", "disco", + "findlinestarts", "findlabels", "show_code", + "get_instructions", "Instruction", "Bytecode"] + _opcodes_all +del _opcodes_all + +_have_code = (types.MethodType, types.FunctionType, types.CodeType, + classmethod, staticmethod, type) + +FORMAT_VALUE = opmap['FORMAT_VALUE'] +FORMAT_VALUE_CONVERTERS = ( + (None, ''), + (str, 'str'), + (repr, 'repr'), + (ascii, 'ascii'), +) +MAKE_FUNCTION = opmap['MAKE_FUNCTION'] +MAKE_FUNCTION_FLAGS = ('defaults', 'kwdefaults', 'annotations', 'closure') + + +def _try_compile(source, name): + """Attempts to compile the given source, first as an expression and + then as a statement if the first approach fails. + + Utility function to accept strings in functions that otherwise + expect code objects + """ + try: + c = compile(source, name, 'eval') + except SyntaxError: + c = compile(source, name, 'exec') + return c + +def dis(x=None, *, file=None, depth=None): + """Disassemble classes, methods, functions, and other compiled objects. + + With no argument, disassemble the last traceback. + + Compiled objects currently include generator objects, async generator + objects, and coroutine objects, all of which store their code object + in a special attribute. + """ + if x is None: + distb(file=file) + return + # Extract functions from methods. + if hasattr(x, '__func__'): + x = x.__func__ + # Extract compiled code objects from... + if hasattr(x, '__code__'): # ...a function, or + x = x.__code__ + elif hasattr(x, 'gi_code'): #...a generator object, or + x = x.gi_code + elif hasattr(x, 'ag_code'): #...an asynchronous generator object, or + x = x.ag_code + elif hasattr(x, 'cr_code'): #...a coroutine. + x = x.cr_code + # Perform the disassembly. + if hasattr(x, '__dict__'): # Class or module + items = sorted(x.__dict__.items()) + for name, x1 in items: + if isinstance(x1, _have_code): + print("Disassembly of %s:" % name, file=file) + try: + dis(x1, file=file, depth=depth) + except TypeError as msg: + print("Sorry:", msg, file=file) + print(file=file) + elif hasattr(x, 'co_code'): # Code object + _disassemble_recursive(x, file=file, depth=depth) + elif isinstance(x, (bytes, bytearray)): # Raw bytecode + _disassemble_bytes(x, file=file) + elif isinstance(x, str): # Source code + _disassemble_str(x, file=file, depth=depth) + else: + raise TypeError("don't know how to disassemble %s objects" % + type(x).__name__) + +def distb(tb=None, *, file=None): + """Disassemble a traceback (default: last traceback).""" + if tb is None: + try: + tb = sys.last_traceback + except AttributeError: + raise RuntimeError("no last traceback to disassemble") from None + while tb.tb_next: tb = tb.tb_next + disassemble(tb.tb_frame.f_code, tb.tb_lasti, file=file) + +# The inspect module interrogates this dictionary to build its +# list of CO_* constants. It is also used by pretty_flags to +# turn the co_flags field into a human readable list. +COMPILER_FLAG_NAMES = { + 1: "OPTIMIZED", + 2: "NEWLOCALS", + 4: "VARARGS", + 8: "VARKEYWORDS", + 16: "NESTED", + 32: "GENERATOR", + 64: "NOFREE", + 128: "COROUTINE", + 256: "ITERABLE_COROUTINE", + 512: "ASYNC_GENERATOR", +} + +def pretty_flags(flags): + """Return pretty representation of code flags.""" + names = [] + for i in range(32): + flag = 1<") + # By now, if we don't have a code object, we can't disassemble x. + if hasattr(x, 'co_code'): + return x + raise TypeError("don't know how to disassemble %s objects" % + type(x).__name__) + +def code_info(x): + """Formatted details of methods, functions, or code.""" + return _format_code_info(_get_code_object(x)) + +def _format_code_info(co): + lines = [] + lines.append("Name: %s" % co.co_name) + lines.append("Filename: %s" % co.co_filename) + lines.append("Argument count: %s" % co.co_argcount) + lines.append("Positional-only arguments: %s" % co.co_posonlyargcount) + lines.append("Kw-only arguments: %s" % co.co_kwonlyargcount) + lines.append("Number of locals: %s" % co.co_nlocals) + lines.append("Stack size: %s" % co.co_stacksize) + lines.append("Flags: %s" % pretty_flags(co.co_flags)) + if co.co_consts: + lines.append("Constants:") + for i_c in enumerate(co.co_consts): + lines.append("%4d: %r" % i_c) + if co.co_names: + lines.append("Names:") + for i_n in enumerate(co.co_names): + lines.append("%4d: %s" % i_n) + if co.co_varnames: + lines.append("Variable names:") + for i_n in enumerate(co.co_varnames): + lines.append("%4d: %s" % i_n) + if co.co_freevars: + lines.append("Free variables:") + for i_n in enumerate(co.co_freevars): + lines.append("%4d: %s" % i_n) + if co.co_cellvars: + lines.append("Cell variables:") + for i_n in enumerate(co.co_cellvars): + lines.append("%4d: %s" % i_n) + return "\n".join(lines) + +def show_code(co, *, file=None): + """Print details of methods, functions, or code to *file*. + + If *file* is not provided, the output is printed on stdout. + """ + print(code_info(co), file=file) + +_Instruction = collections.namedtuple("_Instruction", + "opname opcode arg argval argrepr offset starts_line is_jump_target") + +_Instruction.opname.__doc__ = "Human readable name for operation" +_Instruction.opcode.__doc__ = "Numeric code for operation" +_Instruction.arg.__doc__ = "Numeric argument to operation (if any), otherwise None" +_Instruction.argval.__doc__ = "Resolved arg value (if known), otherwise same as arg" +_Instruction.argrepr.__doc__ = "Human readable description of operation argument" +_Instruction.offset.__doc__ = "Start index of operation within bytecode sequence" +_Instruction.starts_line.__doc__ = "Line started by this opcode (if any), otherwise None" +_Instruction.is_jump_target.__doc__ = "True if other code jumps to here, otherwise False" + +_OPNAME_WIDTH = 20 +_OPARG_WIDTH = 5 + +class Instruction(_Instruction): + """Details for a bytecode operation + + Defined fields: + opname - human readable name for operation + opcode - numeric code for operation + arg - numeric argument to operation (if any), otherwise None + argval - resolved arg value (if known), otherwise same as arg + argrepr - human readable description of operation argument + offset - start index of operation within bytecode sequence + starts_line - line started by this opcode (if any), otherwise None + is_jump_target - True if other code jumps to here, otherwise False + """ + + def _disassemble(self, lineno_width=3, mark_as_current=False, offset_width=4): + """Format instruction details for inclusion in disassembly output + + *lineno_width* sets the width of the line number field (0 omits it) + *mark_as_current* inserts a '-->' marker arrow as part of the line + *offset_width* sets the width of the instruction offset field + """ + fields = [] + # Column: Source code line number + if lineno_width: + if self.starts_line is not None: + lineno_fmt = "%%%dd" % lineno_width + fields.append(lineno_fmt % self.starts_line) + else: + fields.append(' ' * lineno_width) + # Column: Current instruction indicator + if mark_as_current: + fields.append('-->') + else: + fields.append(' ') + # Column: Jump target marker + if self.is_jump_target: + fields.append('>>') + else: + fields.append(' ') + # Column: Instruction offset from start of code sequence + fields.append(repr(self.offset).rjust(offset_width)) + # Column: Opcode name + fields.append(self.opname.ljust(_OPNAME_WIDTH)) + # Column: Opcode argument + if self.arg is not None: + fields.append(repr(self.arg).rjust(_OPARG_WIDTH)) + # Column: Opcode argument details + if self.argrepr: + fields.append('(' + self.argrepr + ')') + return ' '.join(fields).rstrip() + + +def get_instructions(x, *, first_line=None): + """Iterator for the opcodes in methods, functions or code + + Generates a series of Instruction named tuples giving the details of + each operations in the supplied code. + + If *first_line* is not None, it indicates the line number that should + be reported for the first source line in the disassembled code. + Otherwise, the source line information (if any) is taken directly from + the disassembled code object. + """ + co = _get_code_object(x) + cell_names = co.co_cellvars + co.co_freevars + linestarts = dict(findlinestarts(co)) + if first_line is not None: + line_offset = first_line - co.co_firstlineno + else: + line_offset = 0 + return _get_instructions_bytes(co.co_code, co.co_varnames, co.co_names, + co.co_consts, cell_names, linestarts, + line_offset) + +def _get_const_info(const_index, const_list): + """Helper to get optional details about const references + + Returns the dereferenced constant and its repr if the constant + list is defined. + Otherwise returns the constant index and its repr(). + """ + argval = const_index + if const_list is not None: + argval = const_list[const_index] + return argval, repr(argval) + +def _get_name_info(name_index, name_list): + """Helper to get optional details about named references + + Returns the dereferenced name as both value and repr if the name + list is defined. + Otherwise returns the name index and its repr(). + """ + argval = name_index + if name_list is not None: + argval = name_list[name_index] + argrepr = argval + else: + argrepr = repr(argval) + return argval, argrepr + + +def _get_instructions_bytes(code, varnames=None, names=None, constants=None, + cells=None, linestarts=None, line_offset=0): + """Iterate over the instructions in a bytecode string. + + Generates a sequence of Instruction namedtuples giving the details of each + opcode. Additional information about the code's runtime environment + (e.g. variable names, constants) can be specified using optional + arguments. + + """ + labels = findlabels(code) + starts_line = None + for offset, op, arg in _unpack_opargs(code): + if linestarts is not None: + starts_line = linestarts.get(offset, None) + if starts_line is not None: + starts_line += line_offset + is_jump_target = offset in labels + argval = None + argrepr = '' + if arg is not None: + # Set argval to the dereferenced value of the argument when + # available, and argrepr to the string representation of argval. + # _disassemble_bytes needs the string repr of the + # raw name index for LOAD_GLOBAL, LOAD_CONST, etc. + argval = arg + if op in hasconst: + argval, argrepr = _get_const_info(arg, constants) + elif op in hasname: + argval, argrepr = _get_name_info(arg, names) + elif op in hasjrel: + argval = offset + 2 + arg + argrepr = "to " + repr(argval) + elif op in haslocal: + argval, argrepr = _get_name_info(arg, varnames) + elif op in hascompare: + argval = cmp_op[arg] + argrepr = argval + elif op in hasfree: + argval, argrepr = _get_name_info(arg, cells) + elif op == FORMAT_VALUE: + argval, argrepr = FORMAT_VALUE_CONVERTERS[arg & 0x3] + argval = (argval, bool(arg & 0x4)) + if argval[1]: + if argrepr: + argrepr += ', ' + argrepr += 'with format' + elif op == MAKE_FUNCTION: + argrepr = ', '.join(s for i, s in enumerate(MAKE_FUNCTION_FLAGS) + if arg & (1< 0: + if depth is not None: + depth = depth - 1 + for x in co.co_consts: + if hasattr(x, 'co_code'): + print(file=file) + print("Disassembly of %r:" % (x,), file=file) + _disassemble_recursive(x, file=file, depth=depth) + +def _disassemble_bytes(code, lasti=-1, varnames=None, names=None, + constants=None, cells=None, linestarts=None, + *, file=None, line_offset=0): + # Omit the line number column entirely if we have no line number info + show_lineno = linestarts is not None + if show_lineno: + maxlineno = max(linestarts.values()) + line_offset + if maxlineno >= 1000: + lineno_width = len(str(maxlineno)) + else: + lineno_width = 3 + else: + lineno_width = 0 + maxoffset = len(code) - 2 + if maxoffset >= 10000: + offset_width = len(str(maxoffset)) + else: + offset_width = 4 + for instr in _get_instructions_bytes(code, varnames, names, + constants, cells, linestarts, + line_offset=line_offset): + new_source_line = (show_lineno and + instr.starts_line is not None and + instr.offset > 0) + if new_source_line: + print(file=file) + is_current_instr = instr.offset == lasti + print(instr._disassemble(lineno_width, is_current_instr, offset_width), + file=file) + +def _disassemble_str(source, **kwargs): + """Compile the source string, then disassemble the code object.""" + _disassemble_recursive(_try_compile(source, ''), **kwargs) + +disco = disassemble # XXX For backwards compatibility + +def _unpack_opargs(code): + extended_arg = 0 + for i in range(0, len(code), 2): + op = code[i] + if op >= HAVE_ARGUMENT: + arg = code[i+1] | extended_arg + extended_arg = (arg << 8) if op == EXTENDED_ARG else 0 + else: + arg = None + yield (i, op, arg) + +def findlabels(code): + """Detect all offsets in a byte code which are jump targets. + + Return the list of offsets. + + """ + labels = [] + for offset, op, arg in _unpack_opargs(code): + if arg is not None: + if op in hasjrel: + label = offset + 2 + arg + elif op in hasjabs: + label = arg + else: + continue + if label not in labels: + labels.append(label) + return labels + +def findlinestarts(code): + """Find the offsets in a byte code which are start of lines in the source. + + Generate pairs (offset, lineno) as described in Python/compile.c. + + """ + byte_increments = code.co_lnotab[0::2] + line_increments = code.co_lnotab[1::2] + bytecode_len = len(code.co_code) + + lastlineno = None + lineno = code.co_firstlineno + addr = 0 + for byte_incr, line_incr in zip(byte_increments, line_increments): + if byte_incr: + if lineno != lastlineno: + yield (addr, lineno) + lastlineno = lineno + addr += byte_incr + if addr >= bytecode_len: + # The rest of the lnotab byte offsets are past the end of + # the bytecode, so the lines were optimized away. + return + if line_incr >= 0x80: + # line_increments is an array of 8-bit signed integers + line_incr -= 0x100 + lineno += line_incr + if lineno != lastlineno: + yield (addr, lineno) + +class Bytecode: + """The bytecode operations of a piece of code + + Instantiate this with a function, method, other compiled object, string of + code, or a code object (as returned by compile()). + + Iterating over this yields the bytecode operations as Instruction instances. + """ + def __init__(self, x, *, first_line=None, current_offset=None): + self.codeobj = co = _get_code_object(x) + if first_line is None: + self.first_line = co.co_firstlineno + self._line_offset = 0 + else: + self.first_line = first_line + self._line_offset = first_line - co.co_firstlineno + self._cell_names = co.co_cellvars + co.co_freevars + self._linestarts = dict(findlinestarts(co)) + self._original_object = x + self.current_offset = current_offset + + def __iter__(self): + co = self.codeobj + return _get_instructions_bytes(co.co_code, co.co_varnames, co.co_names, + co.co_consts, self._cell_names, + self._linestarts, + line_offset=self._line_offset) + + def __repr__(self): + return "{}({!r})".format(self.__class__.__name__, + self._original_object) + + @classmethod + def from_traceback(cls, tb): + """ Construct a Bytecode from the given traceback """ + while tb.tb_next: + tb = tb.tb_next + return cls(tb.tb_frame.f_code, current_offset=tb.tb_lasti) + + def info(self): + """Return formatted information about the code object.""" + return _format_code_info(self.codeobj) + + def dis(self): + """Return a formatted view of the bytecode operations.""" + co = self.codeobj + if self.current_offset is not None: + offset = self.current_offset + else: + offset = -1 + with io.StringIO() as output: + _disassemble_bytes(co.co_code, varnames=co.co_varnames, + names=co.co_names, constants=co.co_consts, + cells=self._cell_names, + linestarts=self._linestarts, + line_offset=self._line_offset, + file=output, + lasti=offset) + return output.getvalue() + + +def _test(): + """Simple test program to disassemble a file.""" + import argparse + + parser = argparse.ArgumentParser() + parser.add_argument('infile', type=argparse.FileType('rb'), nargs='?', default='-') + args = parser.parse_args() + with args.infile as infile: + source = infile.read() + code = compile(source, args.infile.name, "exec") + dis(code) + +if __name__ == "__main__": + _test() diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/enum.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/enum.py new file mode 100644 index 0000000000000000000000000000000000000000..3d0b1797aebe0c0653de386454482d549e7c98c7 --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/enum.py @@ -0,0 +1,1018 @@ +import sys +from types import MappingProxyType, DynamicClassAttribute + + +__all__ = [ + 'EnumMeta', + 'Enum', 'IntEnum', 'Flag', 'IntFlag', + 'auto', 'unique', + ] + + +def _is_descriptor(obj): + """ + Returns True if obj is a descriptor, False otherwise. + """ + return ( + hasattr(obj, '__get__') or + hasattr(obj, '__set__') or + hasattr(obj, '__delete__') + ) + +def _is_dunder(name): + """ + Returns True if a __dunder__ name, False otherwise. + """ + return ( + len(name) > 4 and + name[:2] == name[-2:] == '__' and + name[2] != '_' and + name[-3] != '_' + ) + +def _is_sunder(name): + """ + Returns True if a _sunder_ name, False otherwise. + """ + return ( + len(name) > 2 and + name[0] == name[-1] == '_' and + name[1:2] != '_' and + name[-2:-1] != '_' + ) + +def _make_class_unpicklable(cls): + """ + Make the given class un-picklable. + """ + def _break_on_call_reduce(self, proto): + raise TypeError('%r cannot be pickled' % self) + cls.__reduce_ex__ = _break_on_call_reduce + cls.__module__ = '' + +_auto_null = object() +class auto: + """ + Instances are replaced with an appropriate value in Enum class suites. + """ + value = _auto_null + + +class _EnumDict(dict): + """ + Track enum member order and ensure member names are not reused. + + EnumMeta will use the names found in self._member_names as the + enumeration member names. + """ + def __init__(self): + super().__init__() + self._member_names = [] + self._last_values = [] + self._ignore = [] + self._auto_called = False + + def __setitem__(self, key, value): + """ + Changes anything not dundered or not a descriptor. + + If an enum member name is used twice, an error is raised; duplicate + values are not checked for. + + Single underscore (sunder) names are reserved. + """ + if _is_sunder(key): + if key not in ( + '_order_', '_create_pseudo_member_', + '_generate_next_value_', '_missing_', '_ignore_', + ): + raise ValueError('_names_ are reserved for future Enum use') + if key == '_generate_next_value_': + # check if members already defined as auto() + if self._auto_called: + raise TypeError("_generate_next_value_ must be defined before members") + setattr(self, '_generate_next_value', value) + elif key == '_ignore_': + if isinstance(value, str): + value = value.replace(',',' ').split() + else: + value = list(value) + self._ignore = value + already = set(value) & set(self._member_names) + if already: + raise ValueError( + '_ignore_ cannot specify already set names: %r' + % (already, ) + ) + elif _is_dunder(key): + if key == '__order__': + key = '_order_' + elif key in self._member_names: + # descriptor overwriting an enum? + raise TypeError('Attempted to reuse key: %r' % key) + elif key in self._ignore: + pass + elif not _is_descriptor(value): + if key in self: + # enum overwriting a descriptor? + raise TypeError('%r already defined as: %r' % (key, self[key])) + if isinstance(value, auto): + if value.value == _auto_null: + value.value = self._generate_next_value( + key, + 1, + len(self._member_names), + self._last_values[:], + ) + self._auto_called = True + value = value.value + self._member_names.append(key) + self._last_values.append(value) + super().__setitem__(key, value) + + +# Dummy value for Enum as EnumMeta explicitly checks for it, but of course +# until EnumMeta finishes running the first time the Enum class doesn't exist. +# This is also why there are checks in EnumMeta like `if Enum is not None` +Enum = None + +class EnumMeta(type): + """ + Metaclass for Enum + """ + @classmethod + def __prepare__(metacls, cls, bases): + # check that previous enum members do not exist + metacls._check_for_existing_members(cls, bases) + # create the namespace dict + enum_dict = _EnumDict() + # inherit previous flags and _generate_next_value_ function + member_type, first_enum = metacls._get_mixins_(cls, bases) + if first_enum is not None: + enum_dict['_generate_next_value_'] = getattr( + first_enum, '_generate_next_value_', None, + ) + return enum_dict + + def __new__(metacls, cls, bases, classdict): + # an Enum class is final once enumeration items have been defined; it + # cannot be mixed with other types (int, float, etc.) if it has an + # inherited __new__ unless a new __new__ is defined (or the resulting + # class will fail). + # + # remove any keys listed in _ignore_ + classdict.setdefault('_ignore_', []).append('_ignore_') + ignore = classdict['_ignore_'] + for key in ignore: + classdict.pop(key, None) + member_type, first_enum = metacls._get_mixins_(cls, bases) + __new__, save_new, use_args = metacls._find_new_( + classdict, member_type, first_enum, + ) + + # save enum items into separate mapping so they don't get baked into + # the new class + enum_members = {k: classdict[k] for k in classdict._member_names} + for name in classdict._member_names: + del classdict[name] + + # adjust the sunders + _order_ = classdict.pop('_order_', None) + + # check for illegal enum names (any others?) + invalid_names = set(enum_members) & {'mro', ''} + if invalid_names: + raise ValueError('Invalid enum member name: {0}'.format( + ','.join(invalid_names))) + + # create a default docstring if one has not been provided + if '__doc__' not in classdict: + classdict['__doc__'] = 'An enumeration.' + + # create our new Enum type + enum_class = super().__new__(metacls, cls, bases, classdict) + enum_class._member_names_ = [] # names in definition order + enum_class._member_map_ = {} # name->value map + enum_class._member_type_ = member_type + + # save DynamicClassAttribute attributes from super classes so we know + # if we can take the shortcut of storing members in the class dict + dynamic_attributes = { + k for c in enum_class.mro() + for k, v in c.__dict__.items() + if isinstance(v, DynamicClassAttribute) + } + + # Reverse value->name map for hashable values. + enum_class._value2member_map_ = {} + + # If a custom type is mixed into the Enum, and it does not know how + # to pickle itself, pickle.dumps will succeed but pickle.loads will + # fail. Rather than have the error show up later and possibly far + # from the source, sabotage the pickle protocol for this class so + # that pickle.dumps also fails. + # + # However, if the new class implements its own __reduce_ex__, do not + # sabotage -- it's on them to make sure it works correctly. We use + # __reduce_ex__ instead of any of the others as it is preferred by + # pickle over __reduce__, and it handles all pickle protocols. + if '__reduce_ex__' not in classdict: + if member_type is not object: + methods = ('__getnewargs_ex__', '__getnewargs__', + '__reduce_ex__', '__reduce__') + if not any(m in member_type.__dict__ for m in methods): + _make_class_unpicklable(enum_class) + + # instantiate them, checking for duplicates as we go + # we instantiate first instead of checking for duplicates first in case + # a custom __new__ is doing something funky with the values -- such as + # auto-numbering ;) + for member_name in classdict._member_names: + value = enum_members[member_name] + if not isinstance(value, tuple): + args = (value, ) + else: + args = value + if member_type is tuple: # special case for tuple enums + args = (args, ) # wrap it one more time + if not use_args: + enum_member = __new__(enum_class) + if not hasattr(enum_member, '_value_'): + enum_member._value_ = value + else: + enum_member = __new__(enum_class, *args) + if not hasattr(enum_member, '_value_'): + if member_type is object: + enum_member._value_ = value + else: + enum_member._value_ = member_type(*args) + value = enum_member._value_ + enum_member._name_ = member_name + enum_member.__objclass__ = enum_class + enum_member.__init__(*args) + # If another member with the same value was already defined, the + # new member becomes an alias to the existing one. + for name, canonical_member in enum_class._member_map_.items(): + if canonical_member._value_ == enum_member._value_: + enum_member = canonical_member + break + else: + # Aliases don't appear in member names (only in __members__). + enum_class._member_names_.append(member_name) + # performance boost for any member that would not shadow + # a DynamicClassAttribute + if member_name not in dynamic_attributes: + setattr(enum_class, member_name, enum_member) + # now add to _member_map_ + enum_class._member_map_[member_name] = enum_member + try: + # This may fail if value is not hashable. We can't add the value + # to the map, and by-value lookups for this value will be + # linear. + enum_class._value2member_map_[value] = enum_member + except TypeError: + pass + + # double check that repr and friends are not the mixin's or various + # things break (such as pickle) + # however, if the method is defined in the Enum itself, don't replace + # it + for name in ('__repr__', '__str__', '__format__', '__reduce_ex__'): + if name in classdict: + continue + class_method = getattr(enum_class, name) + obj_method = getattr(member_type, name, None) + enum_method = getattr(first_enum, name, None) + if obj_method is not None and obj_method is class_method: + setattr(enum_class, name, enum_method) + + # replace any other __new__ with our own (as long as Enum is not None, + # anyway) -- again, this is to support pickle + if Enum is not None: + # if the user defined their own __new__, save it before it gets + # clobbered in case they subclass later + if save_new: + enum_class.__new_member__ = __new__ + enum_class.__new__ = Enum.__new__ + + # py3 support for definition order (helps keep py2/py3 code in sync) + if _order_ is not None: + if isinstance(_order_, str): + _order_ = _order_.replace(',', ' ').split() + if _order_ != enum_class._member_names_: + raise TypeError('member order does not match _order_') + + return enum_class + + def __bool__(self): + """ + classes/types should always be True. + """ + return True + + def __call__(cls, value, names=None, *, module=None, qualname=None, type=None, start=1): + """ + Either returns an existing member, or creates a new enum class. + + This method is used both when an enum class is given a value to match + to an enumeration member (i.e. Color(3)) and for the functional API + (i.e. Color = Enum('Color', names='RED GREEN BLUE')). + + When used for the functional API: + + `value` will be the name of the new class. + + `names` should be either a string of white-space/comma delimited names + (values will start at `start`), or an iterator/mapping of name, value pairs. + + `module` should be set to the module this class is being created in; + if it is not set, an attempt to find that module will be made, but if + it fails the class will not be picklable. + + `qualname` should be set to the actual location this class can be found + at in its module; by default it is set to the global scope. If this is + not correct, unpickling will fail in some circumstances. + + `type`, if set, will be mixed in as the first base class. + """ + if names is None: # simple value lookup + return cls.__new__(cls, value) + # otherwise, functional API: we're creating a new Enum type + return cls._create_( + value, + names, + module=module, + qualname=qualname, + type=type, + start=start, + ) + + def __contains__(cls, member): + if not isinstance(member, Enum): + raise TypeError( + "unsupported operand type(s) for 'in': '%s' and '%s'" % ( + type(member).__qualname__, cls.__class__.__qualname__)) + return isinstance(member, cls) and member._name_ in cls._member_map_ + + def __delattr__(cls, attr): + # nicer error message when someone tries to delete an attribute + # (see issue19025). + if attr in cls._member_map_: + raise AttributeError("%s: cannot delete Enum member." % cls.__name__) + super().__delattr__(attr) + + def __dir__(self): + return ( + ['__class__', '__doc__', '__members__', '__module__'] + + self._member_names_ + ) + + def __getattr__(cls, name): + """ + Return the enum member matching `name` + + We use __getattr__ instead of descriptors or inserting into the enum + class' __dict__ in order to support `name` and `value` being both + properties for enum members (which live in the class' __dict__) and + enum members themselves. + """ + if _is_dunder(name): + raise AttributeError(name) + try: + return cls._member_map_[name] + except KeyError: + raise AttributeError(name) from None + + def __getitem__(cls, name): + return cls._member_map_[name] + + def __iter__(cls): + """ + Returns members in definition order. + """ + return (cls._member_map_[name] for name in cls._member_names_) + + def __len__(cls): + return len(cls._member_names_) + + @property + def __members__(cls): + """ + Returns a mapping of member name->value. + + This mapping lists all enum members, including aliases. Note that this + is a read-only view of the internal mapping. + """ + return MappingProxyType(cls._member_map_) + + def __repr__(cls): + return "" % cls.__name__ + + def __reversed__(cls): + """ + Returns members in reverse definition order. + """ + return (cls._member_map_[name] for name in reversed(cls._member_names_)) + + def __setattr__(cls, name, value): + """ + Block attempts to reassign Enum members. + + A simple assignment to the class namespace only changes one of the + several possible ways to get an Enum member from the Enum class, + resulting in an inconsistent Enumeration. + """ + member_map = cls.__dict__.get('_member_map_', {}) + if name in member_map: + raise AttributeError('Cannot reassign members.') + super().__setattr__(name, value) + + def _create_(cls, class_name, names, *, module=None, qualname=None, type=None, start=1): + """ + Convenience method to create a new Enum class. + + `names` can be: + + * A string containing member names, separated either with spaces or + commas. Values are incremented by 1 from `start`. + * An iterable of member names. Values are incremented by 1 from `start`. + * An iterable of (member name, value) pairs. + * A mapping of member name -> value pairs. + """ + metacls = cls.__class__ + bases = (cls, ) if type is None else (type, cls) + _, first_enum = cls._get_mixins_(cls, bases) + classdict = metacls.__prepare__(class_name, bases) + + # special processing needed for names? + if isinstance(names, str): + names = names.replace(',', ' ').split() + if isinstance(names, (tuple, list)) and names and isinstance(names[0], str): + original_names, names = names, [] + last_values = [] + for count, name in enumerate(original_names): + value = first_enum._generate_next_value_(name, start, count, last_values[:]) + last_values.append(value) + names.append((name, value)) + + # Here, names is either an iterable of (name, value) or a mapping. + for item in names: + if isinstance(item, str): + member_name, member_value = item, names[item] + else: + member_name, member_value = item + classdict[member_name] = member_value + enum_class = metacls.__new__(metacls, class_name, bases, classdict) + + # TODO: replace the frame hack if a blessed way to know the calling + # module is ever developed + if module is None: + try: + module = sys._getframe(2).f_globals['__name__'] + except (AttributeError, ValueError, KeyError) as exc: + pass + if module is None: + _make_class_unpicklable(enum_class) + else: + enum_class.__module__ = module + if qualname is not None: + enum_class.__qualname__ = qualname + + return enum_class + + def _convert_(cls, name, module, filter, source=None): + """ + Create a new Enum subclass that replaces a collection of global constants + """ + # convert all constants from source (or module) that pass filter() to + # a new Enum called name, and export the enum and its members back to + # module; + # also, replace the __reduce_ex__ method so unpickling works in + # previous Python versions + module_globals = vars(sys.modules[module]) + if source: + source = vars(source) + else: + source = module_globals + # _value2member_map_ is populated in the same order every time + # for a consistent reverse mapping of number to name when there + # are multiple names for the same number. + members = [ + (name, value) + for name, value in source.items() + if filter(name)] + try: + # sort by value + members.sort(key=lambda t: (t[1], t[0])) + except TypeError: + # unless some values aren't comparable, in which case sort by name + members.sort(key=lambda t: t[0]) + cls = cls(name, members, module=module) + cls.__reduce_ex__ = _reduce_ex_by_name + module_globals.update(cls.__members__) + module_globals[name] = cls + return cls + + def _convert(cls, *args, **kwargs): + import warnings + warnings.warn("_convert is deprecated and will be removed in 3.9, use " + "_convert_ instead.", DeprecationWarning, stacklevel=2) + return cls._convert_(*args, **kwargs) + + @staticmethod + def _check_for_existing_members(class_name, bases): + for chain in bases: + for base in chain.__mro__: + if issubclass(base, Enum) and base._member_names_: + raise TypeError( + "%s: cannot extend enumeration %r" + % (class_name, base.__name__) + ) + + @staticmethod + def _get_mixins_(class_name, bases): + """ + Returns the type for creating enum members, and the first inherited + enum class. + + bases: the tuple of bases that was given to __new__ + """ + if not bases: + return object, Enum + + def _find_data_type(bases): + data_types = [] + for chain in bases: + candidate = None + for base in chain.__mro__: + if base is object: + continue + elif issubclass(base, Enum): + if base._member_type_ is not object: + data_types.append(base._member_type_) + break + elif '__new__' in base.__dict__: + if issubclass(base, Enum): + continue + data_types.append(candidate or base) + break + else: + candidate = base + if len(data_types) > 1: + raise TypeError('%r: too many data types: %r' % (class_name, data_types)) + elif data_types: + return data_types[0] + else: + return None + + # ensure final parent class is an Enum derivative, find any concrete + # data type, and check that Enum has no members + first_enum = bases[-1] + if not issubclass(first_enum, Enum): + raise TypeError("new enumerations should be created as " + "`EnumName([mixin_type, ...] [data_type,] enum_type)`") + member_type = _find_data_type(bases) or object + if first_enum._member_names_: + raise TypeError("Cannot extend enumerations") + return member_type, first_enum + + @staticmethod + def _find_new_(classdict, member_type, first_enum): + """ + Returns the __new__ to be used for creating the enum members. + + classdict: the class dictionary given to __new__ + member_type: the data type whose __new__ will be used by default + first_enum: enumeration to check for an overriding __new__ + """ + # now find the correct __new__, checking to see of one was defined + # by the user; also check earlier enum classes in case a __new__ was + # saved as __new_member__ + __new__ = classdict.get('__new__', None) + + # should __new__ be saved as __new_member__ later? + save_new = __new__ is not None + + if __new__ is None: + # check all possibles for __new_member__ before falling back to + # __new__ + for method in ('__new_member__', '__new__'): + for possible in (member_type, first_enum): + target = getattr(possible, method, None) + if target not in { + None, + None.__new__, + object.__new__, + Enum.__new__, + }: + __new__ = target + break + if __new__ is not None: + break + else: + __new__ = object.__new__ + + # if a non-object.__new__ is used then whatever value/tuple was + # assigned to the enum member name will be passed to __new__ and to the + # new enum member's __init__ + if __new__ is object.__new__: + use_args = False + else: + use_args = True + return __new__, save_new, use_args + + +class Enum(metaclass=EnumMeta): + """ + Generic enumeration. + + Derive from this class to define new enumerations. + """ + def __new__(cls, value): + # all enum instances are actually created during class construction + # without calling this method; this method is called by the metaclass' + # __call__ (i.e. Color(3) ), and by pickle + if type(value) is cls: + # For lookups like Color(Color.RED) + return value + # by-value search for a matching enum member + # see if it's in the reverse mapping (for hashable values) + try: + return cls._value2member_map_[value] + except KeyError: + # Not found, no need to do long O(n) search + pass + except TypeError: + # not there, now do long search -- O(n) behavior + for member in cls._member_map_.values(): + if member._value_ == value: + return member + # still not found -- try _missing_ hook + try: + exc = None + result = cls._missing_(value) + except Exception as e: + exc = e + result = None + try: + if isinstance(result, cls): + return result + else: + ve_exc = ValueError("%r is not a valid %s" % (value, cls.__name__)) + if result is None and exc is None: + raise ve_exc + elif exc is None: + exc = TypeError( + 'error in %s._missing_: returned %r instead of None or a valid member' + % (cls.__name__, result) + ) + exc.__context__ = ve_exc + raise exc + finally: + # ensure all variables that could hold an exception are destroyed + exc = None + ve_exc = None + + def _generate_next_value_(name, start, count, last_values): + """ + Generate the next value when not given. + + name: the name of the member + start: the initial start value or None + count: the number of existing members + last_value: the last value assigned or None + """ + for last_value in reversed(last_values): + try: + return last_value + 1 + except TypeError: + pass + else: + return start + + @classmethod + def _missing_(cls, value): + return None + + def __repr__(self): + return "<%s.%s: %r>" % ( + self.__class__.__name__, self._name_, self._value_) + + def __str__(self): + return "%s.%s" % (self.__class__.__name__, self._name_) + + def __dir__(self): + """ + Returns all members and all public methods + """ + added_behavior = [ + m + for cls in self.__class__.mro() + for m in cls.__dict__ + if m[0] != '_' and m not in self._member_map_ + ] + [m for m in self.__dict__ if m[0] != '_'] + return (['__class__', '__doc__', '__module__'] + added_behavior) + + def __format__(self, format_spec): + """ + Returns format using actual value type unless __str__ has been overridden. + """ + # mixed-in Enums should use the mixed-in type's __format__, otherwise + # we can get strange results with the Enum name showing up instead of + # the value + + # pure Enum branch, or branch with __str__ explicitly overridden + str_overridden = type(self).__str__ not in (Enum.__str__, Flag.__str__) + if self._member_type_ is object or str_overridden: + cls = str + val = str(self) + # mix-in branch + else: + cls = self._member_type_ + val = self._value_ + return cls.__format__(val, format_spec) + + def __hash__(self): + return hash(self._name_) + + def __reduce_ex__(self, proto): + return self.__class__, (self._value_, ) + + # DynamicClassAttribute is used to provide access to the `name` and + # `value` properties of enum members while keeping some measure of + # protection from modification, while still allowing for an enumeration + # to have members named `name` and `value`. This works because enumeration + # members are not set directly on the enum class -- __getattr__ is + # used to look them up. + + @DynamicClassAttribute + def name(self): + """The name of the Enum member.""" + return self._name_ + + @DynamicClassAttribute + def value(self): + """The value of the Enum member.""" + return self._value_ + + +class IntEnum(int, Enum): + """Enum where members are also (and must be) ints""" + + +def _reduce_ex_by_name(self, proto): + return self.name + +class Flag(Enum): + """ + Support for flags + """ + + def _generate_next_value_(name, start, count, last_values): + """ + Generate the next value when not given. + + name: the name of the member + start: the initial start value or None + count: the number of existing members + last_value: the last value assigned or None + """ + if not count: + return start if start is not None else 1 + for last_value in reversed(last_values): + try: + high_bit = _high_bit(last_value) + break + except Exception: + raise TypeError('Invalid Flag value: %r' % last_value) from None + return 2 ** (high_bit+1) + + @classmethod + def _missing_(cls, value): + """ + Returns member (possibly creating it) if one can be found for value. + """ + original_value = value + if value < 0: + value = ~value + possible_member = cls._create_pseudo_member_(value) + if original_value < 0: + possible_member = ~possible_member + return possible_member + + @classmethod + def _create_pseudo_member_(cls, value): + """ + Create a composite member iff value contains only members. + """ + pseudo_member = cls._value2member_map_.get(value, None) + if pseudo_member is None: + # verify all bits are accounted for + _, extra_flags = _decompose(cls, value) + if extra_flags: + raise ValueError("%r is not a valid %s" % (value, cls.__name__)) + # construct a singleton enum pseudo-member + pseudo_member = object.__new__(cls) + pseudo_member._name_ = None + pseudo_member._value_ = value + # use setdefault in case another thread already created a composite + # with this value + pseudo_member = cls._value2member_map_.setdefault(value, pseudo_member) + return pseudo_member + + def __contains__(self, other): + """ + Returns True if self has at least the same flags set as other. + """ + if not isinstance(other, self.__class__): + raise TypeError( + "unsupported operand type(s) for 'in': '%s' and '%s'" % ( + type(other).__qualname__, self.__class__.__qualname__)) + return other._value_ & self._value_ == other._value_ + + def __repr__(self): + cls = self.__class__ + if self._name_ is not None: + return '<%s.%s: %r>' % (cls.__name__, self._name_, self._value_) + members, uncovered = _decompose(cls, self._value_) + return '<%s.%s: %r>' % ( + cls.__name__, + '|'.join([str(m._name_ or m._value_) for m in members]), + self._value_, + ) + + def __str__(self): + cls = self.__class__ + if self._name_ is not None: + return '%s.%s' % (cls.__name__, self._name_) + members, uncovered = _decompose(cls, self._value_) + if len(members) == 1 and members[0]._name_ is None: + return '%s.%r' % (cls.__name__, members[0]._value_) + else: + return '%s.%s' % ( + cls.__name__, + '|'.join([str(m._name_ or m._value_) for m in members]), + ) + + def __bool__(self): + return bool(self._value_) + + def __or__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + return self.__class__(self._value_ | other._value_) + + def __and__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + return self.__class__(self._value_ & other._value_) + + def __xor__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + return self.__class__(self._value_ ^ other._value_) + + def __invert__(self): + members, uncovered = _decompose(self.__class__, self._value_) + inverted = self.__class__(0) + for m in self.__class__: + if m not in members and not (m._value_ & self._value_): + inverted = inverted | m + return self.__class__(inverted) + + +class IntFlag(int, Flag): + """ + Support for integer-based Flags + """ + + @classmethod + def _missing_(cls, value): + """ + Returns member (possibly creating it) if one can be found for value. + """ + if not isinstance(value, int): + raise ValueError("%r is not a valid %s" % (value, cls.__name__)) + new_member = cls._create_pseudo_member_(value) + return new_member + + @classmethod + def _create_pseudo_member_(cls, value): + """ + Create a composite member iff value contains only members. + """ + pseudo_member = cls._value2member_map_.get(value, None) + if pseudo_member is None: + need_to_create = [value] + # get unaccounted for bits + _, extra_flags = _decompose(cls, value) + # timer = 10 + while extra_flags: + # timer -= 1 + bit = _high_bit(extra_flags) + flag_value = 2 ** bit + if (flag_value not in cls._value2member_map_ and + flag_value not in need_to_create + ): + need_to_create.append(flag_value) + if extra_flags == -flag_value: + extra_flags = 0 + else: + extra_flags ^= flag_value + for value in reversed(need_to_create): + # construct singleton pseudo-members + pseudo_member = int.__new__(cls, value) + pseudo_member._name_ = None + pseudo_member._value_ = value + # use setdefault in case another thread already created a composite + # with this value + pseudo_member = cls._value2member_map_.setdefault(value, pseudo_member) + return pseudo_member + + def __or__(self, other): + if not isinstance(other, (self.__class__, int)): + return NotImplemented + result = self.__class__(self._value_ | self.__class__(other)._value_) + return result + + def __and__(self, other): + if not isinstance(other, (self.__class__, int)): + return NotImplemented + return self.__class__(self._value_ & self.__class__(other)._value_) + + def __xor__(self, other): + if not isinstance(other, (self.__class__, int)): + return NotImplemented + return self.__class__(self._value_ ^ self.__class__(other)._value_) + + __ror__ = __or__ + __rand__ = __and__ + __rxor__ = __xor__ + + def __invert__(self): + result = self.__class__(~self._value_) + return result + + +def _high_bit(value): + """ + returns index of highest bit, or -1 if value is zero or negative + """ + return value.bit_length() - 1 + +def unique(enumeration): + """ + Class decorator for enumerations ensuring unique member values. + """ + duplicates = [] + for name, member in enumeration.__members__.items(): + if name != member.name: + duplicates.append((name, member.name)) + if duplicates: + alias_details = ', '.join( + ["%s -> %s" % (alias, name) for (alias, name) in duplicates]) + raise ValueError('duplicate values found in %r: %s' % + (enumeration, alias_details)) + return enumeration + +def _decompose(flag, value): + """ + Extract all members from the value. + """ + # _decompose is only called if the value is not named + not_covered = value + negative = value < 0 + # issue29167: wrap accesses to _value2member_map_ in a list to avoid race + # conditions between iterating over it and having more pseudo- + # members added to it + if negative: + # only check for named flags + flags_to_check = [ + (m, v) + for v, m in list(flag._value2member_map_.items()) + if m.name is not None + ] + else: + # check for named flags and powers-of-two flags + flags_to_check = [ + (m, v) + for v, m in list(flag._value2member_map_.items()) + if m.name is not None or _power_of_two(v) + ] + members = [] + for member, member_value in flags_to_check: + if member_value and member_value & value == member_value: + members.append(member) + not_covered &= ~member_value + if not members and value in flag._value2member_map_: + members.append(flag._value2member_map_[value]) + members.sort(key=lambda m: m._value_, reverse=True) + if len(members) > 1 and members[0].value == value: + # we have the breakdown, don't need the value member itself + members.pop(0) + return members, not_covered + +def _power_of_two(value): + if value < 1: + return False + return value == 2 ** _high_bit(value) diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/filecmp.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/filecmp.py new file mode 100644 index 0000000000000000000000000000000000000000..e5ad8397e4c53956c1503d8abba134015da0a24f --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/filecmp.py @@ -0,0 +1,305 @@ +"""Utilities for comparing files and directories. + +Classes: + dircmp + +Functions: + cmp(f1, f2, shallow=True) -> int + cmpfiles(a, b, common) -> ([], [], []) + clear_cache() + +""" + +import os +import stat +from itertools import filterfalse + +__all__ = ['clear_cache', 'cmp', 'dircmp', 'cmpfiles', 'DEFAULT_IGNORES'] + +_cache = {} +BUFSIZE = 8*1024 + +DEFAULT_IGNORES = [ + 'RCS', 'CVS', 'tags', '.git', '.hg', '.bzr', '_darcs', '__pycache__'] + +def clear_cache(): + """Clear the filecmp cache.""" + _cache.clear() + +def cmp(f1, f2, shallow=True): + """Compare two files. + + Arguments: + + f1 -- First file name + + f2 -- Second file name + + shallow -- Just check stat signature (do not read the files). + defaults to True. + + Return value: + + True if the files are the same, False otherwise. + + This function uses a cache for past comparisons and the results, + with cache entries invalidated if their stat information + changes. The cache may be cleared by calling clear_cache(). + + """ + + s1 = _sig(os.stat(f1)) + s2 = _sig(os.stat(f2)) + if s1[0] != stat.S_IFREG or s2[0] != stat.S_IFREG: + return False + if shallow and s1 == s2: + return True + if s1[1] != s2[1]: + return False + + outcome = _cache.get((f1, f2, s1, s2)) + if outcome is None: + outcome = _do_cmp(f1, f2) + if len(_cache) > 100: # limit the maximum size of the cache + clear_cache() + _cache[f1, f2, s1, s2] = outcome + return outcome + +def _sig(st): + return (stat.S_IFMT(st.st_mode), + st.st_size, + st.st_mtime) + +def _do_cmp(f1, f2): + bufsize = BUFSIZE + with open(f1, 'rb') as fp1, open(f2, 'rb') as fp2: + while True: + b1 = fp1.read(bufsize) + b2 = fp2.read(bufsize) + if b1 != b2: + return False + if not b1: + return True + +# Directory comparison class. +# +class dircmp: + """A class that manages the comparison of 2 directories. + + dircmp(a, b, ignore=None, hide=None) + A and B are directories. + IGNORE is a list of names to ignore, + defaults to DEFAULT_IGNORES. + HIDE is a list of names to hide, + defaults to [os.curdir, os.pardir]. + + High level usage: + x = dircmp(dir1, dir2) + x.report() -> prints a report on the differences between dir1 and dir2 + or + x.report_partial_closure() -> prints report on differences between dir1 + and dir2, and reports on common immediate subdirectories. + x.report_full_closure() -> like report_partial_closure, + but fully recursive. + + Attributes: + left_list, right_list: The files in dir1 and dir2, + filtered by hide and ignore. + common: a list of names in both dir1 and dir2. + left_only, right_only: names only in dir1, dir2. + common_dirs: subdirectories in both dir1 and dir2. + common_files: files in both dir1 and dir2. + common_funny: names in both dir1 and dir2 where the type differs between + dir1 and dir2, or the name is not stat-able. + same_files: list of identical files. + diff_files: list of filenames which differ. + funny_files: list of files which could not be compared. + subdirs: a dictionary of dircmp objects, keyed by names in common_dirs. + """ + + def __init__(self, a, b, ignore=None, hide=None): # Initialize + self.left = a + self.right = b + if hide is None: + self.hide = [os.curdir, os.pardir] # Names never to be shown + else: + self.hide = hide + if ignore is None: + self.ignore = DEFAULT_IGNORES + else: + self.ignore = ignore + + def phase0(self): # Compare everything except common subdirectories + self.left_list = _filter(os.listdir(self.left), + self.hide+self.ignore) + self.right_list = _filter(os.listdir(self.right), + self.hide+self.ignore) + self.left_list.sort() + self.right_list.sort() + + def phase1(self): # Compute common names + a = dict(zip(map(os.path.normcase, self.left_list), self.left_list)) + b = dict(zip(map(os.path.normcase, self.right_list), self.right_list)) + self.common = list(map(a.__getitem__, filter(b.__contains__, a))) + self.left_only = list(map(a.__getitem__, filterfalse(b.__contains__, a))) + self.right_only = list(map(b.__getitem__, filterfalse(a.__contains__, b))) + + def phase2(self): # Distinguish files, directories, funnies + self.common_dirs = [] + self.common_files = [] + self.common_funny = [] + + for x in self.common: + a_path = os.path.join(self.left, x) + b_path = os.path.join(self.right, x) + + ok = 1 + try: + a_stat = os.stat(a_path) + except OSError as why: + # print('Can\'t stat', a_path, ':', why.args[1]) + ok = 0 + try: + b_stat = os.stat(b_path) + except OSError as why: + # print('Can\'t stat', b_path, ':', why.args[1]) + ok = 0 + + if ok: + a_type = stat.S_IFMT(a_stat.st_mode) + b_type = stat.S_IFMT(b_stat.st_mode) + if a_type != b_type: + self.common_funny.append(x) + elif stat.S_ISDIR(a_type): + self.common_dirs.append(x) + elif stat.S_ISREG(a_type): + self.common_files.append(x) + else: + self.common_funny.append(x) + else: + self.common_funny.append(x) + + def phase3(self): # Find out differences between common files + xx = cmpfiles(self.left, self.right, self.common_files) + self.same_files, self.diff_files, self.funny_files = xx + + def phase4(self): # Find out differences between common subdirectories + # A new dircmp object is created for each common subdirectory, + # these are stored in a dictionary indexed by filename. + # The hide and ignore properties are inherited from the parent + self.subdirs = {} + for x in self.common_dirs: + a_x = os.path.join(self.left, x) + b_x = os.path.join(self.right, x) + self.subdirs[x] = dircmp(a_x, b_x, self.ignore, self.hide) + + def phase4_closure(self): # Recursively call phase4() on subdirectories + self.phase4() + for sd in self.subdirs.values(): + sd.phase4_closure() + + def report(self): # Print a report on the differences between a and b + # Output format is purposely lousy + print('diff', self.left, self.right) + if self.left_only: + self.left_only.sort() + print('Only in', self.left, ':', self.left_only) + if self.right_only: + self.right_only.sort() + print('Only in', self.right, ':', self.right_only) + if self.same_files: + self.same_files.sort() + print('Identical files :', self.same_files) + if self.diff_files: + self.diff_files.sort() + print('Differing files :', self.diff_files) + if self.funny_files: + self.funny_files.sort() + print('Trouble with common files :', self.funny_files) + if self.common_dirs: + self.common_dirs.sort() + print('Common subdirectories :', self.common_dirs) + if self.common_funny: + self.common_funny.sort() + print('Common funny cases :', self.common_funny) + + def report_partial_closure(self): # Print reports on self and on subdirs + self.report() + for sd in self.subdirs.values(): + print() + sd.report() + + def report_full_closure(self): # Report on self and subdirs recursively + self.report() + for sd in self.subdirs.values(): + print() + sd.report_full_closure() + + methodmap = dict(subdirs=phase4, + same_files=phase3, diff_files=phase3, funny_files=phase3, + common_dirs = phase2, common_files=phase2, common_funny=phase2, + common=phase1, left_only=phase1, right_only=phase1, + left_list=phase0, right_list=phase0) + + def __getattr__(self, attr): + if attr not in self.methodmap: + raise AttributeError(attr) + self.methodmap[attr](self) + return getattr(self, attr) + +def cmpfiles(a, b, common, shallow=True): + """Compare common files in two directories. + + a, b -- directory names + common -- list of file names found in both directories + shallow -- if true, do comparison based solely on stat() information + + Returns a tuple of three lists: + files that compare equal + files that are different + filenames that aren't regular files. + + """ + res = ([], [], []) + for x in common: + ax = os.path.join(a, x) + bx = os.path.join(b, x) + res[_cmp(ax, bx, shallow)].append(x) + return res + + +# Compare two files. +# Return: +# 0 for equal +# 1 for different +# 2 for funny cases (can't stat, etc.) +# +def _cmp(a, b, sh, abs=abs, cmp=cmp): + try: + return not abs(cmp(a, b, sh)) + except OSError: + return 2 + + +# Return a copy with items that occur in skip removed. +# +def _filter(flist, skip): + return list(filterfalse(skip.__contains__, flist)) + + +# Demonstration and testing. +# +def demo(): + import sys + import getopt + options, args = getopt.getopt(sys.argv[1:], 'r') + if len(args) != 2: + raise getopt.GetoptError('need exactly two args', None) + dd = dircmp(args[0], args[1]) + if ('-r', '') in options: + dd.report_full_closure() + else: + dd.report() + +if __name__ == '__main__': + demo() diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/formatter.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/formatter.py new file mode 100644 index 0000000000000000000000000000000000000000..e2394de8c291952cf960132319d6b59831ea1a18 --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/formatter.py @@ -0,0 +1,452 @@ +"""Generic output formatting. + +Formatter objects transform an abstract flow of formatting events into +specific output events on writer objects. Formatters manage several stack +structures to allow various properties of a writer object to be changed and +restored; writers need not be able to handle relative changes nor any sort +of ``change back'' operation. Specific writer properties which may be +controlled via formatter objects are horizontal alignment, font, and left +margin indentations. A mechanism is provided which supports providing +arbitrary, non-exclusive style settings to a writer as well. Additional +interfaces facilitate formatting events which are not reversible, such as +paragraph separation. + +Writer objects encapsulate device interfaces. Abstract devices, such as +file formats, are supported as well as physical devices. The provided +implementations all work with abstract devices. The interface makes +available mechanisms for setting the properties which formatter objects +manage and inserting data into the output. +""" + +import sys +import warnings +warnings.warn('the formatter module is deprecated', DeprecationWarning, + stacklevel=2) + + +AS_IS = None + + +class NullFormatter: + """A formatter which does nothing. + + If the writer parameter is omitted, a NullWriter instance is created. + No methods of the writer are called by NullFormatter instances. + + Implementations should inherit from this class if implementing a writer + interface but don't need to inherit any implementation. + + """ + + def __init__(self, writer=None): + if writer is None: + writer = NullWriter() + self.writer = writer + def end_paragraph(self, blankline): pass + def add_line_break(self): pass + def add_hor_rule(self, *args, **kw): pass + def add_label_data(self, format, counter, blankline=None): pass + def add_flowing_data(self, data): pass + def add_literal_data(self, data): pass + def flush_softspace(self): pass + def push_alignment(self, align): pass + def pop_alignment(self): pass + def push_font(self, x): pass + def pop_font(self): pass + def push_margin(self, margin): pass + def pop_margin(self): pass + def set_spacing(self, spacing): pass + def push_style(self, *styles): pass + def pop_style(self, n=1): pass + def assert_line_data(self, flag=1): pass + + +class AbstractFormatter: + """The standard formatter. + + This implementation has demonstrated wide applicability to many writers, + and may be used directly in most circumstances. It has been used to + implement a full-featured World Wide Web browser. + + """ + + # Space handling policy: blank spaces at the boundary between elements + # are handled by the outermost context. "Literal" data is not checked + # to determine context, so spaces in literal data are handled directly + # in all circumstances. + + def __init__(self, writer): + self.writer = writer # Output device + self.align = None # Current alignment + self.align_stack = [] # Alignment stack + self.font_stack = [] # Font state + self.margin_stack = [] # Margin state + self.spacing = None # Vertical spacing state + self.style_stack = [] # Other state, e.g. color + self.nospace = 1 # Should leading space be suppressed + self.softspace = 0 # Should a space be inserted + self.para_end = 1 # Just ended a paragraph + self.parskip = 0 # Skipped space between paragraphs? + self.hard_break = 1 # Have a hard break + self.have_label = 0 + + def end_paragraph(self, blankline): + if not self.hard_break: + self.writer.send_line_break() + self.have_label = 0 + if self.parskip < blankline and not self.have_label: + self.writer.send_paragraph(blankline - self.parskip) + self.parskip = blankline + self.have_label = 0 + self.hard_break = self.nospace = self.para_end = 1 + self.softspace = 0 + + def add_line_break(self): + if not (self.hard_break or self.para_end): + self.writer.send_line_break() + self.have_label = self.parskip = 0 + self.hard_break = self.nospace = 1 + self.softspace = 0 + + def add_hor_rule(self, *args, **kw): + if not self.hard_break: + self.writer.send_line_break() + self.writer.send_hor_rule(*args, **kw) + self.hard_break = self.nospace = 1 + self.have_label = self.para_end = self.softspace = self.parskip = 0 + + def add_label_data(self, format, counter, blankline = None): + if self.have_label or not self.hard_break: + self.writer.send_line_break() + if not self.para_end: + self.writer.send_paragraph((blankline and 1) or 0) + if isinstance(format, str): + self.writer.send_label_data(self.format_counter(format, counter)) + else: + self.writer.send_label_data(format) + self.nospace = self.have_label = self.hard_break = self.para_end = 1 + self.softspace = self.parskip = 0 + + def format_counter(self, format, counter): + label = '' + for c in format: + if c == '1': + label = label + ('%d' % counter) + elif c in 'aA': + if counter > 0: + label = label + self.format_letter(c, counter) + elif c in 'iI': + if counter > 0: + label = label + self.format_roman(c, counter) + else: + label = label + c + return label + + def format_letter(self, case, counter): + label = '' + while counter > 0: + counter, x = divmod(counter-1, 26) + # This makes a strong assumption that lowercase letters + # and uppercase letters form two contiguous blocks, with + # letters in order! + s = chr(ord(case) + x) + label = s + label + return label + + def format_roman(self, case, counter): + ones = ['i', 'x', 'c', 'm'] + fives = ['v', 'l', 'd'] + label, index = '', 0 + # This will die of IndexError when counter is too big + while counter > 0: + counter, x = divmod(counter, 10) + if x == 9: + label = ones[index] + ones[index+1] + label + elif x == 4: + label = ones[index] + fives[index] + label + else: + if x >= 5: + s = fives[index] + x = x-5 + else: + s = '' + s = s + ones[index]*x + label = s + label + index = index + 1 + if case == 'I': + return label.upper() + return label + + def add_flowing_data(self, data): + if not data: return + prespace = data[:1].isspace() + postspace = data[-1:].isspace() + data = " ".join(data.split()) + if self.nospace and not data: + return + elif prespace or self.softspace: + if not data: + if not self.nospace: + self.softspace = 1 + self.parskip = 0 + return + if not self.nospace: + data = ' ' + data + self.hard_break = self.nospace = self.para_end = \ + self.parskip = self.have_label = 0 + self.softspace = postspace + self.writer.send_flowing_data(data) + + def add_literal_data(self, data): + if not data: return + if self.softspace: + self.writer.send_flowing_data(" ") + self.hard_break = data[-1:] == '\n' + self.nospace = self.para_end = self.softspace = \ + self.parskip = self.have_label = 0 + self.writer.send_literal_data(data) + + def flush_softspace(self): + if self.softspace: + self.hard_break = self.para_end = self.parskip = \ + self.have_label = self.softspace = 0 + self.nospace = 1 + self.writer.send_flowing_data(' ') + + def push_alignment(self, align): + if align and align != self.align: + self.writer.new_alignment(align) + self.align = align + self.align_stack.append(align) + else: + self.align_stack.append(self.align) + + def pop_alignment(self): + if self.align_stack: + del self.align_stack[-1] + if self.align_stack: + self.align = align = self.align_stack[-1] + self.writer.new_alignment(align) + else: + self.align = None + self.writer.new_alignment(None) + + def push_font(self, font): + size, i, b, tt = font + if self.softspace: + self.hard_break = self.para_end = self.softspace = 0 + self.nospace = 1 + self.writer.send_flowing_data(' ') + if self.font_stack: + csize, ci, cb, ctt = self.font_stack[-1] + if size is AS_IS: size = csize + if i is AS_IS: i = ci + if b is AS_IS: b = cb + if tt is AS_IS: tt = ctt + font = (size, i, b, tt) + self.font_stack.append(font) + self.writer.new_font(font) + + def pop_font(self): + if self.font_stack: + del self.font_stack[-1] + if self.font_stack: + font = self.font_stack[-1] + else: + font = None + self.writer.new_font(font) + + def push_margin(self, margin): + self.margin_stack.append(margin) + fstack = [m for m in self.margin_stack if m] + if not margin and fstack: + margin = fstack[-1] + self.writer.new_margin(margin, len(fstack)) + + def pop_margin(self): + if self.margin_stack: + del self.margin_stack[-1] + fstack = [m for m in self.margin_stack if m] + if fstack: + margin = fstack[-1] + else: + margin = None + self.writer.new_margin(margin, len(fstack)) + + def set_spacing(self, spacing): + self.spacing = spacing + self.writer.new_spacing(spacing) + + def push_style(self, *styles): + if self.softspace: + self.hard_break = self.para_end = self.softspace = 0 + self.nospace = 1 + self.writer.send_flowing_data(' ') + for style in styles: + self.style_stack.append(style) + self.writer.new_styles(tuple(self.style_stack)) + + def pop_style(self, n=1): + del self.style_stack[-n:] + self.writer.new_styles(tuple(self.style_stack)) + + def assert_line_data(self, flag=1): + self.nospace = self.hard_break = not flag + self.para_end = self.parskip = self.have_label = 0 + + +class NullWriter: + """Minimal writer interface to use in testing & inheritance. + + A writer which only provides the interface definition; no actions are + taken on any methods. This should be the base class for all writers + which do not need to inherit any implementation methods. + + """ + def __init__(self): pass + def flush(self): pass + def new_alignment(self, align): pass + def new_font(self, font): pass + def new_margin(self, margin, level): pass + def new_spacing(self, spacing): pass + def new_styles(self, styles): pass + def send_paragraph(self, blankline): pass + def send_line_break(self): pass + def send_hor_rule(self, *args, **kw): pass + def send_label_data(self, data): pass + def send_flowing_data(self, data): pass + def send_literal_data(self, data): pass + + +class AbstractWriter(NullWriter): + """A writer which can be used in debugging formatters, but not much else. + + Each method simply announces itself by printing its name and + arguments on standard output. + + """ + + def new_alignment(self, align): + print("new_alignment(%r)" % (align,)) + + def new_font(self, font): + print("new_font(%r)" % (font,)) + + def new_margin(self, margin, level): + print("new_margin(%r, %d)" % (margin, level)) + + def new_spacing(self, spacing): + print("new_spacing(%r)" % (spacing,)) + + def new_styles(self, styles): + print("new_styles(%r)" % (styles,)) + + def send_paragraph(self, blankline): + print("send_paragraph(%r)" % (blankline,)) + + def send_line_break(self): + print("send_line_break()") + + def send_hor_rule(self, *args, **kw): + print("send_hor_rule()") + + def send_label_data(self, data): + print("send_label_data(%r)" % (data,)) + + def send_flowing_data(self, data): + print("send_flowing_data(%r)" % (data,)) + + def send_literal_data(self, data): + print("send_literal_data(%r)" % (data,)) + + +class DumbWriter(NullWriter): + """Simple writer class which writes output on the file object passed in + as the file parameter or, if file is omitted, on standard output. The + output is simply word-wrapped to the number of columns specified by + the maxcol parameter. This class is suitable for reflowing a sequence + of paragraphs. + + """ + + def __init__(self, file=None, maxcol=72): + self.file = file or sys.stdout + self.maxcol = maxcol + NullWriter.__init__(self) + self.reset() + + def reset(self): + self.col = 0 + self.atbreak = 0 + + def send_paragraph(self, blankline): + self.file.write('\n'*blankline) + self.col = 0 + self.atbreak = 0 + + def send_line_break(self): + self.file.write('\n') + self.col = 0 + self.atbreak = 0 + + def send_hor_rule(self, *args, **kw): + self.file.write('\n') + self.file.write('-'*self.maxcol) + self.file.write('\n') + self.col = 0 + self.atbreak = 0 + + def send_literal_data(self, data): + self.file.write(data) + i = data.rfind('\n') + if i >= 0: + self.col = 0 + data = data[i+1:] + data = data.expandtabs() + self.col = self.col + len(data) + self.atbreak = 0 + + def send_flowing_data(self, data): + if not data: return + atbreak = self.atbreak or data[0].isspace() + col = self.col + maxcol = self.maxcol + write = self.file.write + for word in data.split(): + if atbreak: + if col + len(word) >= maxcol: + write('\n') + col = 0 + else: + write(' ') + col = col + 1 + write(word) + col = col + len(word) + atbreak = 1 + self.col = col + self.atbreak = data[-1].isspace() + + +def test(file = None): + w = DumbWriter() + f = AbstractFormatter(w) + if file is not None: + fp = open(file) + elif sys.argv[1:]: + fp = open(sys.argv[1]) + else: + fp = sys.stdin + try: + for line in fp: + if line == '\n': + f.end_paragraph(1) + else: + f.add_flowing_data(line) + finally: + if fp is not sys.stdin: + fp.close() + f.end_paragraph(0) + + +if __name__ == '__main__': + test() diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/functools.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/functools.py new file mode 100644 index 0000000000000000000000000000000000000000..4cde5f590cf29043e8a15ad582001025eb27cf96 --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/functools.py @@ -0,0 +1,976 @@ +"""functools.py - Tools for working with functions and callable objects +""" +# Python module wrapper for _functools C module +# to allow utilities written in Python to be added +# to the functools module. +# Written by Nick Coghlan , +# Raymond Hettinger , +# and Łukasz Langa . +# Copyright (C) 2006-2013 Python Software Foundation. +# See C source code for _functools credits/copyright + +__all__ = ['update_wrapper', 'wraps', 'WRAPPER_ASSIGNMENTS', 'WRAPPER_UPDATES', + 'total_ordering', 'cmp_to_key', 'lru_cache', 'reduce', 'partial', + 'partialmethod', 'singledispatch', 'singledispatchmethod', + "cached_property"] + +from abc import get_cache_token +from collections import namedtuple +# import types, weakref # Deferred to single_dispatch() +from reprlib import recursive_repr +from _thread import RLock + + +################################################################################ +### update_wrapper() and wraps() decorator +################################################################################ + +# update_wrapper() and wraps() are tools to help write +# wrapper functions that can handle naive introspection + +WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__qualname__', '__doc__', + '__annotations__') +WRAPPER_UPDATES = ('__dict__',) +def update_wrapper(wrapper, + wrapped, + assigned = WRAPPER_ASSIGNMENTS, + updated = WRAPPER_UPDATES): + """Update a wrapper function to look like the wrapped function + + wrapper is the function to be updated + wrapped is the original function + assigned is a tuple naming the attributes assigned directly + from the wrapped function to the wrapper function (defaults to + functools.WRAPPER_ASSIGNMENTS) + updated is a tuple naming the attributes of the wrapper that + are updated with the corresponding attribute from the wrapped + function (defaults to functools.WRAPPER_UPDATES) + """ + for attr in assigned: + try: + value = getattr(wrapped, attr) + except AttributeError: + pass + else: + setattr(wrapper, attr, value) + for attr in updated: + getattr(wrapper, attr).update(getattr(wrapped, attr, {})) + # Issue #17482: set __wrapped__ last so we don't inadvertently copy it + # from the wrapped function when updating __dict__ + wrapper.__wrapped__ = wrapped + # Return the wrapper so this can be used as a decorator via partial() + return wrapper + +def wraps(wrapped, + assigned = WRAPPER_ASSIGNMENTS, + updated = WRAPPER_UPDATES): + """Decorator factory to apply update_wrapper() to a wrapper function + + Returns a decorator that invokes update_wrapper() with the decorated + function as the wrapper argument and the arguments to wraps() as the + remaining arguments. Default arguments are as for update_wrapper(). + This is a convenience function to simplify applying partial() to + update_wrapper(). + """ + return partial(update_wrapper, wrapped=wrapped, + assigned=assigned, updated=updated) + + +################################################################################ +### total_ordering class decorator +################################################################################ + +# The total ordering functions all invoke the root magic method directly +# rather than using the corresponding operator. This avoids possible +# infinite recursion that could occur when the operator dispatch logic +# detects a NotImplemented result and then calls a reflected method. + +def _gt_from_lt(self, other, NotImplemented=NotImplemented): + 'Return a > b. Computed by @total_ordering from (not a < b) and (a != b).' + op_result = self.__lt__(other) + if op_result is NotImplemented: + return op_result + return not op_result and self != other + +def _le_from_lt(self, other, NotImplemented=NotImplemented): + 'Return a <= b. Computed by @total_ordering from (a < b) or (a == b).' + op_result = self.__lt__(other) + return op_result or self == other + +def _ge_from_lt(self, other, NotImplemented=NotImplemented): + 'Return a >= b. Computed by @total_ordering from (not a < b).' + op_result = self.__lt__(other) + if op_result is NotImplemented: + return op_result + return not op_result + +def _ge_from_le(self, other, NotImplemented=NotImplemented): + 'Return a >= b. Computed by @total_ordering from (not a <= b) or (a == b).' + op_result = self.__le__(other) + if op_result is NotImplemented: + return op_result + return not op_result or self == other + +def _lt_from_le(self, other, NotImplemented=NotImplemented): + 'Return a < b. Computed by @total_ordering from (a <= b) and (a != b).' + op_result = self.__le__(other) + if op_result is NotImplemented: + return op_result + return op_result and self != other + +def _gt_from_le(self, other, NotImplemented=NotImplemented): + 'Return a > b. Computed by @total_ordering from (not a <= b).' + op_result = self.__le__(other) + if op_result is NotImplemented: + return op_result + return not op_result + +def _lt_from_gt(self, other, NotImplemented=NotImplemented): + 'Return a < b. Computed by @total_ordering from (not a > b) and (a != b).' + op_result = self.__gt__(other) + if op_result is NotImplemented: + return op_result + return not op_result and self != other + +def _ge_from_gt(self, other, NotImplemented=NotImplemented): + 'Return a >= b. Computed by @total_ordering from (a > b) or (a == b).' + op_result = self.__gt__(other) + return op_result or self == other + +def _le_from_gt(self, other, NotImplemented=NotImplemented): + 'Return a <= b. Computed by @total_ordering from (not a > b).' + op_result = self.__gt__(other) + if op_result is NotImplemented: + return op_result + return not op_result + +def _le_from_ge(self, other, NotImplemented=NotImplemented): + 'Return a <= b. Computed by @total_ordering from (not a >= b) or (a == b).' + op_result = self.__ge__(other) + if op_result is NotImplemented: + return op_result + return not op_result or self == other + +def _gt_from_ge(self, other, NotImplemented=NotImplemented): + 'Return a > b. Computed by @total_ordering from (a >= b) and (a != b).' + op_result = self.__ge__(other) + if op_result is NotImplemented: + return op_result + return op_result and self != other + +def _lt_from_ge(self, other, NotImplemented=NotImplemented): + 'Return a < b. Computed by @total_ordering from (not a >= b).' + op_result = self.__ge__(other) + if op_result is NotImplemented: + return op_result + return not op_result + +_convert = { + '__lt__': [('__gt__', _gt_from_lt), + ('__le__', _le_from_lt), + ('__ge__', _ge_from_lt)], + '__le__': [('__ge__', _ge_from_le), + ('__lt__', _lt_from_le), + ('__gt__', _gt_from_le)], + '__gt__': [('__lt__', _lt_from_gt), + ('__ge__', _ge_from_gt), + ('__le__', _le_from_gt)], + '__ge__': [('__le__', _le_from_ge), + ('__gt__', _gt_from_ge), + ('__lt__', _lt_from_ge)] +} + +def total_ordering(cls): + """Class decorator that fills in missing ordering methods""" + # Find user-defined comparisons (not those inherited from object). + roots = {op for op in _convert if getattr(cls, op, None) is not getattr(object, op, None)} + if not roots: + raise ValueError('must define at least one ordering operation: < > <= >=') + root = max(roots) # prefer __lt__ to __le__ to __gt__ to __ge__ + for opname, opfunc in _convert[root]: + if opname not in roots: + opfunc.__name__ = opname + setattr(cls, opname, opfunc) + return cls + + +################################################################################ +### cmp_to_key() function converter +################################################################################ + +def cmp_to_key(mycmp): + """Convert a cmp= function into a key= function""" + class K(object): + __slots__ = ['obj'] + def __init__(self, obj): + self.obj = obj + def __lt__(self, other): + return mycmp(self.obj, other.obj) < 0 + def __gt__(self, other): + return mycmp(self.obj, other.obj) > 0 + def __eq__(self, other): + return mycmp(self.obj, other.obj) == 0 + def __le__(self, other): + return mycmp(self.obj, other.obj) <= 0 + def __ge__(self, other): + return mycmp(self.obj, other.obj) >= 0 + __hash__ = None + return K + +try: + from _functools import cmp_to_key +except ImportError: + pass + + +################################################################################ +### reduce() sequence to a single item +################################################################################ + +_initial_missing = object() + +def reduce(function, sequence, initial=_initial_missing): + """ + reduce(function, sequence[, initial]) -> value + + Apply a function of two arguments cumulatively to the items of a sequence, + from left to right, so as to reduce the sequence to a single value. + For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) calculates + ((((1+2)+3)+4)+5). If initial is present, it is placed before the items + of the sequence in the calculation, and serves as a default when the + sequence is empty. + """ + + it = iter(sequence) + + if initial is _initial_missing: + try: + value = next(it) + except StopIteration: + raise TypeError("reduce() of empty sequence with no initial value") from None + else: + value = initial + + for element in it: + value = function(value, element) + + return value + +try: + from _functools import reduce +except ImportError: + pass + + +################################################################################ +### partial() argument application +################################################################################ + +# Purely functional, no descriptor behaviour +class partial: + """New function with partial application of the given arguments + and keywords. + """ + + __slots__ = "func", "args", "keywords", "__dict__", "__weakref__" + + def __new__(cls, func, /, *args, **keywords): + if not callable(func): + raise TypeError("the first argument must be callable") + + if hasattr(func, "func"): + args = func.args + args + keywords = {**func.keywords, **keywords} + func = func.func + + self = super(partial, cls).__new__(cls) + + self.func = func + self.args = args + self.keywords = keywords + return self + + def __call__(self, /, *args, **keywords): + keywords = {**self.keywords, **keywords} + return self.func(*self.args, *args, **keywords) + + @recursive_repr() + def __repr__(self): + qualname = type(self).__qualname__ + args = [repr(self.func)] + args.extend(repr(x) for x in self.args) + args.extend(f"{k}={v!r}" for (k, v) in self.keywords.items()) + if type(self).__module__ == "functools": + return f"functools.{qualname}({', '.join(args)})" + return f"{qualname}({', '.join(args)})" + + def __reduce__(self): + return type(self), (self.func,), (self.func, self.args, + self.keywords or None, self.__dict__ or None) + + def __setstate__(self, state): + if not isinstance(state, tuple): + raise TypeError("argument to __setstate__ must be a tuple") + if len(state) != 4: + raise TypeError(f"expected 4 items in state, got {len(state)}") + func, args, kwds, namespace = state + if (not callable(func) or not isinstance(args, tuple) or + (kwds is not None and not isinstance(kwds, dict)) or + (namespace is not None and not isinstance(namespace, dict))): + raise TypeError("invalid partial state") + + args = tuple(args) # just in case it's a subclass + if kwds is None: + kwds = {} + elif type(kwds) is not dict: # XXX does it need to be *exactly* dict? + kwds = dict(kwds) + if namespace is None: + namespace = {} + + self.__dict__ = namespace + self.func = func + self.args = args + self.keywords = kwds + +try: + from _functools import partial +except ImportError: + pass + +# Descriptor version +class partialmethod(object): + """Method descriptor with partial application of the given arguments + and keywords. + + Supports wrapping existing descriptors and handles non-descriptor + callables as instance methods. + """ + + def __init__(*args, **keywords): + if len(args) >= 2: + self, func, *args = args + elif not args: + raise TypeError("descriptor '__init__' of partialmethod " + "needs an argument") + elif 'func' in keywords: + func = keywords.pop('func') + self, *args = args + import warnings + warnings.warn("Passing 'func' as keyword argument is deprecated", + DeprecationWarning, stacklevel=2) + else: + raise TypeError("type 'partialmethod' takes at least one argument, " + "got %d" % (len(args)-1)) + args = tuple(args) + + if not callable(func) and not hasattr(func, "__get__"): + raise TypeError("{!r} is not callable or a descriptor" + .format(func)) + + # func could be a descriptor like classmethod which isn't callable, + # so we can't inherit from partial (it verifies func is callable) + if isinstance(func, partialmethod): + # flattening is mandatory in order to place cls/self before all + # other arguments + # it's also more efficient since only one function will be called + self.func = func.func + self.args = func.args + args + self.keywords = {**func.keywords, **keywords} + else: + self.func = func + self.args = args + self.keywords = keywords + __init__.__text_signature__ = '($self, func, /, *args, **keywords)' + + def __repr__(self): + args = ", ".join(map(repr, self.args)) + keywords = ", ".join("{}={!r}".format(k, v) + for k, v in self.keywords.items()) + format_string = "{module}.{cls}({func}, {args}, {keywords})" + return format_string.format(module=self.__class__.__module__, + cls=self.__class__.__qualname__, + func=self.func, + args=args, + keywords=keywords) + + def _make_unbound_method(self): + def _method(cls_or_self, /, *args, **keywords): + keywords = {**self.keywords, **keywords} + return self.func(cls_or_self, *self.args, *args, **keywords) + _method.__isabstractmethod__ = self.__isabstractmethod__ + _method._partialmethod = self + return _method + + def __get__(self, obj, cls=None): + get = getattr(self.func, "__get__", None) + result = None + if get is not None: + new_func = get(obj, cls) + if new_func is not self.func: + # Assume __get__ returning something new indicates the + # creation of an appropriate callable + result = partial(new_func, *self.args, **self.keywords) + try: + result.__self__ = new_func.__self__ + except AttributeError: + pass + if result is None: + # If the underlying descriptor didn't do anything, treat this + # like an instance method + result = self._make_unbound_method().__get__(obj, cls) + return result + + @property + def __isabstractmethod__(self): + return getattr(self.func, "__isabstractmethod__", False) + +# Helper functions + +def _unwrap_partial(func): + while isinstance(func, partial): + func = func.func + return func + +################################################################################ +### LRU Cache function decorator +################################################################################ + +_CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"]) + +class _HashedSeq(list): + """ This class guarantees that hash() will be called no more than once + per element. This is important because the lru_cache() will hash + the key multiple times on a cache miss. + + """ + + __slots__ = 'hashvalue' + + def __init__(self, tup, hash=hash): + self[:] = tup + self.hashvalue = hash(tup) + + def __hash__(self): + return self.hashvalue + +def _make_key(args, kwds, typed, + kwd_mark = (object(),), + fasttypes = {int, str}, + tuple=tuple, type=type, len=len): + """Make a cache key from optionally typed positional and keyword arguments + + The key is constructed in a way that is flat as possible rather than + as a nested structure that would take more memory. + + If there is only a single argument and its data type is known to cache + its hash value, then that argument is returned without a wrapper. This + saves space and improves lookup speed. + + """ + # All of code below relies on kwds preserving the order input by the user. + # Formerly, we sorted() the kwds before looping. The new way is *much* + # faster; however, it means that f(x=1, y=2) will now be treated as a + # distinct call from f(y=2, x=1) which will be cached separately. + key = args + if kwds: + key += kwd_mark + for item in kwds.items(): + key += item + if typed: + key += tuple(type(v) for v in args) + if kwds: + key += tuple(type(v) for v in kwds.values()) + elif len(key) == 1 and type(key[0]) in fasttypes: + return key[0] + return _HashedSeq(key) + +def lru_cache(maxsize=128, typed=False): + """Least-recently-used cache decorator. + + If *maxsize* is set to None, the LRU features are disabled and the cache + can grow without bound. + + If *typed* is True, arguments of different types will be cached separately. + For example, f(3.0) and f(3) will be treated as distinct calls with + distinct results. + + Arguments to the cached function must be hashable. + + View the cache statistics named tuple (hits, misses, maxsize, currsize) + with f.cache_info(). Clear the cache and statistics with f.cache_clear(). + Access the underlying function with f.__wrapped__. + + See: http://en.wikipedia.org/wiki/Cache_replacement_policies#Least_recently_used_(LRU) + + """ + + # Users should only access the lru_cache through its public API: + # cache_info, cache_clear, and f.__wrapped__ + # The internals of the lru_cache are encapsulated for thread safety and + # to allow the implementation to change (including a possible C version). + + if isinstance(maxsize, int): + # Negative maxsize is treated as 0 + if maxsize < 0: + maxsize = 0 + elif callable(maxsize) and isinstance(typed, bool): + # The user_function was passed in directly via the maxsize argument + user_function, maxsize = maxsize, 128 + wrapper = _lru_cache_wrapper(user_function, maxsize, typed, _CacheInfo) + return update_wrapper(wrapper, user_function) + elif maxsize is not None: + raise TypeError( + 'Expected first argument to be an integer, a callable, or None') + + def decorating_function(user_function): + wrapper = _lru_cache_wrapper(user_function, maxsize, typed, _CacheInfo) + return update_wrapper(wrapper, user_function) + + return decorating_function + +def _lru_cache_wrapper(user_function, maxsize, typed, _CacheInfo): + # Constants shared by all lru cache instances: + sentinel = object() # unique object used to signal cache misses + make_key = _make_key # build a key from the function arguments + PREV, NEXT, KEY, RESULT = 0, 1, 2, 3 # names for the link fields + + cache = {} + hits = misses = 0 + full = False + cache_get = cache.get # bound method to lookup a key or return None + cache_len = cache.__len__ # get cache size without calling len() + lock = RLock() # because linkedlist updates aren't threadsafe + root = [] # root of the circular doubly linked list + root[:] = [root, root, None, None] # initialize by pointing to self + + if maxsize == 0: + + def wrapper(*args, **kwds): + # No caching -- just a statistics update + nonlocal misses + misses += 1 + result = user_function(*args, **kwds) + return result + + elif maxsize is None: + + def wrapper(*args, **kwds): + # Simple caching without ordering or size limit + nonlocal hits, misses + key = make_key(args, kwds, typed) + result = cache_get(key, sentinel) + if result is not sentinel: + hits += 1 + return result + misses += 1 + result = user_function(*args, **kwds) + cache[key] = result + return result + + else: + + def wrapper(*args, **kwds): + # Size limited caching that tracks accesses by recency + nonlocal root, hits, misses, full + key = make_key(args, kwds, typed) + with lock: + link = cache_get(key) + if link is not None: + # Move the link to the front of the circular queue + link_prev, link_next, _key, result = link + link_prev[NEXT] = link_next + link_next[PREV] = link_prev + last = root[PREV] + last[NEXT] = root[PREV] = link + link[PREV] = last + link[NEXT] = root + hits += 1 + return result + misses += 1 + result = user_function(*args, **kwds) + with lock: + if key in cache: + # Getting here means that this same key was added to the + # cache while the lock was released. Since the link + # update is already done, we need only return the + # computed result and update the count of misses. + pass + elif full: + # Use the old root to store the new key and result. + oldroot = root + oldroot[KEY] = key + oldroot[RESULT] = result + # Empty the oldest link and make it the new root. + # Keep a reference to the old key and old result to + # prevent their ref counts from going to zero during the + # update. That will prevent potentially arbitrary object + # clean-up code (i.e. __del__) from running while we're + # still adjusting the links. + root = oldroot[NEXT] + oldkey = root[KEY] + oldresult = root[RESULT] + root[KEY] = root[RESULT] = None + # Now update the cache dictionary. + del cache[oldkey] + # Save the potentially reentrant cache[key] assignment + # for last, after the root and links have been put in + # a consistent state. + cache[key] = oldroot + else: + # Put result in a new link at the front of the queue. + last = root[PREV] + link = [last, root, key, result] + last[NEXT] = root[PREV] = cache[key] = link + # Use the cache_len bound method instead of the len() function + # which could potentially be wrapped in an lru_cache itself. + full = (cache_len() >= maxsize) + return result + + def cache_info(): + """Report cache statistics""" + with lock: + return _CacheInfo(hits, misses, maxsize, cache_len()) + + def cache_clear(): + """Clear the cache and cache statistics""" + nonlocal hits, misses, full + with lock: + cache.clear() + root[:] = [root, root, None, None] + hits = misses = 0 + full = False + + wrapper.cache_info = cache_info + wrapper.cache_clear = cache_clear + return wrapper + +try: + from _functools import _lru_cache_wrapper +except ImportError: + pass + + +################################################################################ +### singledispatch() - single-dispatch generic function decorator +################################################################################ + +def _c3_merge(sequences): + """Merges MROs in *sequences* to a single MRO using the C3 algorithm. + + Adapted from http://www.python.org/download/releases/2.3/mro/. + + """ + result = [] + while True: + sequences = [s for s in sequences if s] # purge empty sequences + if not sequences: + return result + for s1 in sequences: # find merge candidates among seq heads + candidate = s1[0] + for s2 in sequences: + if candidate in s2[1:]: + candidate = None + break # reject the current head, it appears later + else: + break + if candidate is None: + raise RuntimeError("Inconsistent hierarchy") + result.append(candidate) + # remove the chosen candidate + for seq in sequences: + if seq[0] == candidate: + del seq[0] + +def _c3_mro(cls, abcs=None): + """Computes the method resolution order using extended C3 linearization. + + If no *abcs* are given, the algorithm works exactly like the built-in C3 + linearization used for method resolution. + + If given, *abcs* is a list of abstract base classes that should be inserted + into the resulting MRO. Unrelated ABCs are ignored and don't end up in the + result. The algorithm inserts ABCs where their functionality is introduced, + i.e. issubclass(cls, abc) returns True for the class itself but returns + False for all its direct base classes. Implicit ABCs for a given class + (either registered or inferred from the presence of a special method like + __len__) are inserted directly after the last ABC explicitly listed in the + MRO of said class. If two implicit ABCs end up next to each other in the + resulting MRO, their ordering depends on the order of types in *abcs*. + + """ + for i, base in enumerate(reversed(cls.__bases__)): + if hasattr(base, '__abstractmethods__'): + boundary = len(cls.__bases__) - i + break # Bases up to the last explicit ABC are considered first. + else: + boundary = 0 + abcs = list(abcs) if abcs else [] + explicit_bases = list(cls.__bases__[:boundary]) + abstract_bases = [] + other_bases = list(cls.__bases__[boundary:]) + for base in abcs: + if issubclass(cls, base) and not any( + issubclass(b, base) for b in cls.__bases__ + ): + # If *cls* is the class that introduces behaviour described by + # an ABC *base*, insert said ABC to its MRO. + abstract_bases.append(base) + for base in abstract_bases: + abcs.remove(base) + explicit_c3_mros = [_c3_mro(base, abcs=abcs) for base in explicit_bases] + abstract_c3_mros = [_c3_mro(base, abcs=abcs) for base in abstract_bases] + other_c3_mros = [_c3_mro(base, abcs=abcs) for base in other_bases] + return _c3_merge( + [[cls]] + + explicit_c3_mros + abstract_c3_mros + other_c3_mros + + [explicit_bases] + [abstract_bases] + [other_bases] + ) + +def _compose_mro(cls, types): + """Calculates the method resolution order for a given class *cls*. + + Includes relevant abstract base classes (with their respective bases) from + the *types* iterable. Uses a modified C3 linearization algorithm. + + """ + bases = set(cls.__mro__) + # Remove entries which are already present in the __mro__ or unrelated. + def is_related(typ): + return (typ not in bases and hasattr(typ, '__mro__') + and issubclass(cls, typ)) + types = [n for n in types if is_related(n)] + # Remove entries which are strict bases of other entries (they will end up + # in the MRO anyway. + def is_strict_base(typ): + for other in types: + if typ != other and typ in other.__mro__: + return True + return False + types = [n for n in types if not is_strict_base(n)] + # Subclasses of the ABCs in *types* which are also implemented by + # *cls* can be used to stabilize ABC ordering. + type_set = set(types) + mro = [] + for typ in types: + found = [] + for sub in typ.__subclasses__(): + if sub not in bases and issubclass(cls, sub): + found.append([s for s in sub.__mro__ if s in type_set]) + if not found: + mro.append(typ) + continue + # Favor subclasses with the biggest number of useful bases + found.sort(key=len, reverse=True) + for sub in found: + for subcls in sub: + if subcls not in mro: + mro.append(subcls) + return _c3_mro(cls, abcs=mro) + +def _find_impl(cls, registry): + """Returns the best matching implementation from *registry* for type *cls*. + + Where there is no registered implementation for a specific type, its method + resolution order is used to find a more generic implementation. + + Note: if *registry* does not contain an implementation for the base + *object* type, this function may return None. + + """ + mro = _compose_mro(cls, registry.keys()) + match = None + for t in mro: + if match is not None: + # If *match* is an implicit ABC but there is another unrelated, + # equally matching implicit ABC, refuse the temptation to guess. + if (t in registry and t not in cls.__mro__ + and match not in cls.__mro__ + and not issubclass(match, t)): + raise RuntimeError("Ambiguous dispatch: {} or {}".format( + match, t)) + break + if t in registry: + match = t + return registry.get(match) + +def singledispatch(func): + """Single-dispatch generic function decorator. + + Transforms a function into a generic function, which can have different + behaviours depending upon the type of its first argument. The decorated + function acts as the default implementation, and additional + implementations can be registered using the register() attribute of the + generic function. + """ + # There are many programs that use functools without singledispatch, so we + # trade-off making singledispatch marginally slower for the benefit of + # making start-up of such applications slightly faster. + import types, weakref + + registry = {} + dispatch_cache = weakref.WeakKeyDictionary() + cache_token = None + + def dispatch(cls): + """generic_func.dispatch(cls) -> + + Runs the dispatch algorithm to return the best available implementation + for the given *cls* registered on *generic_func*. + + """ + nonlocal cache_token + if cache_token is not None: + current_token = get_cache_token() + if cache_token != current_token: + dispatch_cache.clear() + cache_token = current_token + try: + impl = dispatch_cache[cls] + except KeyError: + try: + impl = registry[cls] + except KeyError: + impl = _find_impl(cls, registry) + dispatch_cache[cls] = impl + return impl + + def register(cls, func=None): + """generic_func.register(cls, func) -> func + + Registers a new implementation for the given *cls* on a *generic_func*. + + """ + nonlocal cache_token + if func is None: + if isinstance(cls, type): + return lambda f: register(cls, f) + ann = getattr(cls, '__annotations__', {}) + if not ann: + raise TypeError( + f"Invalid first argument to `register()`: {cls!r}. " + f"Use either `@register(some_class)` or plain `@register` " + f"on an annotated function." + ) + func = cls + + # only import typing if annotation parsing is necessary + from typing import get_type_hints + argname, cls = next(iter(get_type_hints(func).items())) + if not isinstance(cls, type): + raise TypeError( + f"Invalid annotation for {argname!r}. " + f"{cls!r} is not a class." + ) + registry[cls] = func + if cache_token is None and hasattr(cls, '__abstractmethods__'): + cache_token = get_cache_token() + dispatch_cache.clear() + return func + + def wrapper(*args, **kw): + if not args: + raise TypeError(f'{funcname} requires at least ' + '1 positional argument') + + return dispatch(args[0].__class__)(*args, **kw) + + funcname = getattr(func, '__name__', 'singledispatch function') + registry[object] = func + wrapper.register = register + wrapper.dispatch = dispatch + wrapper.registry = types.MappingProxyType(registry) + wrapper._clear_cache = dispatch_cache.clear + update_wrapper(wrapper, func) + return wrapper + + +# Descriptor version +class singledispatchmethod: + """Single-dispatch generic method descriptor. + + Supports wrapping existing descriptors and handles non-descriptor + callables as instance methods. + """ + + def __init__(self, func): + if not callable(func) and not hasattr(func, "__get__"): + raise TypeError(f"{func!r} is not callable or a descriptor") + + self.dispatcher = singledispatch(func) + self.func = func + + def register(self, cls, method=None): + """generic_method.register(cls, func) -> func + + Registers a new implementation for the given *cls* on a *generic_method*. + """ + return self.dispatcher.register(cls, func=method) + + def __get__(self, obj, cls=None): + def _method(*args, **kwargs): + method = self.dispatcher.dispatch(args[0].__class__) + return method.__get__(obj, cls)(*args, **kwargs) + + _method.__isabstractmethod__ = self.__isabstractmethod__ + _method.register = self.register + update_wrapper(_method, self.func) + return _method + + @property + def __isabstractmethod__(self): + return getattr(self.func, '__isabstractmethod__', False) + + +################################################################################ +### cached_property() - computed once per instance, cached as attribute +################################################################################ + +_NOT_FOUND = object() + + +class cached_property: + def __init__(self, func): + self.func = func + self.attrname = None + self.__doc__ = func.__doc__ + self.lock = RLock() + + def __set_name__(self, owner, name): + if self.attrname is None: + self.attrname = name + elif name != self.attrname: + raise TypeError( + "Cannot assign the same cached_property to two different names " + f"({self.attrname!r} and {name!r})." + ) + + def __get__(self, instance, owner=None): + if instance is None: + return self + if self.attrname is None: + raise TypeError( + "Cannot use cached_property instance without calling __set_name__ on it.") + try: + cache = instance.__dict__ + except AttributeError: # not all objects have __dict__ (e.g. class defines slots) + msg = ( + f"No '__dict__' attribute on {type(instance).__name__!r} " + f"instance to cache {self.attrname!r} property." + ) + raise TypeError(msg) from None + val = cache.get(self.attrname, _NOT_FOUND) + if val is _NOT_FOUND: + with self.lock: + # check if another thread filled cache while we awaited lock + val = cache.get(self.attrname, _NOT_FOUND) + if val is _NOT_FOUND: + val = self.func(instance) + try: + cache[self.attrname] = val + except TypeError: + msg = ( + f"The '__dict__' attribute on {type(instance).__name__!r} instance " + f"does not support item assignment for caching {self.attrname!r} property." + ) + raise TypeError(msg) from None + return val diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/getpass.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/getpass.py new file mode 100644 index 0000000000000000000000000000000000000000..36e17e4cb6965db548f445e8e9f156da27d2109b --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/getpass.py @@ -0,0 +1,185 @@ +"""Utilities to get a password and/or the current user name. + +getpass(prompt[, stream]) - Prompt for a password, with echo turned off. +getuser() - Get the user name from the environment or password database. + +GetPassWarning - This UserWarning is issued when getpass() cannot prevent + echoing of the password contents while reading. + +On Windows, the msvcrt module will be used. + +""" + +# Authors: Piers Lauder (original) +# Guido van Rossum (Windows support and cleanup) +# Gregory P. Smith (tty support & GetPassWarning) + +import contextlib +import io +import os +import sys +import warnings + +__all__ = ["getpass","getuser","GetPassWarning"] + + +class GetPassWarning(UserWarning): pass + + +def unix_getpass(prompt='Password: ', stream=None): + """Prompt for a password, with echo turned off. + + Args: + prompt: Written on stream to ask for the input. Default: 'Password: ' + stream: A writable file object to display the prompt. Defaults to + the tty. If no tty is available defaults to sys.stderr. + Returns: + The seKr3t input. + Raises: + EOFError: If our input tty or stdin was closed. + GetPassWarning: When we were unable to turn echo off on the input. + + Always restores terminal settings before returning. + """ + passwd = None + with contextlib.ExitStack() as stack: + try: + # Always try reading and writing directly on the tty first. + fd = os.open('/dev/tty', os.O_RDWR|os.O_NOCTTY) + tty = io.FileIO(fd, 'w+') + stack.enter_context(tty) + input = io.TextIOWrapper(tty) + stack.enter_context(input) + if not stream: + stream = input + except OSError as e: + # If that fails, see if stdin can be controlled. + stack.close() + try: + fd = sys.stdin.fileno() + except (AttributeError, ValueError): + fd = None + passwd = fallback_getpass(prompt, stream) + input = sys.stdin + if not stream: + stream = sys.stderr + + if fd is not None: + try: + old = termios.tcgetattr(fd) # a copy to save + new = old[:] + new[3] &= ~termios.ECHO # 3 == 'lflags' + tcsetattr_flags = termios.TCSAFLUSH + if hasattr(termios, 'TCSASOFT'): + tcsetattr_flags |= termios.TCSASOFT + try: + termios.tcsetattr(fd, tcsetattr_flags, new) + passwd = _raw_input(prompt, stream, input=input) + finally: + termios.tcsetattr(fd, tcsetattr_flags, old) + stream.flush() # issue7208 + except termios.error: + if passwd is not None: + # _raw_input succeeded. The final tcsetattr failed. Reraise + # instead of leaving the terminal in an unknown state. + raise + # We can't control the tty or stdin. Give up and use normal IO. + # fallback_getpass() raises an appropriate warning. + if stream is not input: + # clean up unused file objects before blocking + stack.close() + passwd = fallback_getpass(prompt, stream) + + stream.write('\n') + return passwd + + +def win_getpass(prompt='Password: ', stream=None): + """Prompt for password with echo off, using Windows getch().""" + if sys.stdin is not sys.__stdin__: + return fallback_getpass(prompt, stream) + + for c in prompt: + msvcrt.putwch(c) + pw = "" + while 1: + c = msvcrt.getwch() + if c == '\r' or c == '\n': + break + if c == '\003': + raise KeyboardInterrupt + if c == '\b': + pw = pw[:-1] + else: + pw = pw + c + msvcrt.putwch('\r') + msvcrt.putwch('\n') + return pw + + +def fallback_getpass(prompt='Password: ', stream=None): + warnings.warn("Can not control echo on the terminal.", GetPassWarning, + stacklevel=2) + if not stream: + stream = sys.stderr + print("Warning: Password input may be echoed.", file=stream) + return _raw_input(prompt, stream) + + +def _raw_input(prompt="", stream=None, input=None): + # This doesn't save the string in the GNU readline history. + if not stream: + stream = sys.stderr + if not input: + input = sys.stdin + prompt = str(prompt) + if prompt: + try: + stream.write(prompt) + except UnicodeEncodeError: + # Use replace error handler to get as much as possible printed. + prompt = prompt.encode(stream.encoding, 'replace') + prompt = prompt.decode(stream.encoding) + stream.write(prompt) + stream.flush() + # NOTE: The Python C API calls flockfile() (and unlock) during readline. + line = input.readline() + if not line: + raise EOFError + if line[-1] == '\n': + line = line[:-1] + return line + + +def getuser(): + """Get the username from the environment or password database. + + First try various environment variables, then the password + database. This works on Windows as long as USERNAME is set. + + """ + + for name in ('LOGNAME', 'USER', 'LNAME', 'USERNAME'): + user = os.environ.get(name) + if user: + return user + + # If this fails, the exception will "explain" why + import pwd + return pwd.getpwuid(os.getuid())[0] + +# Bind the name getpass to the appropriate function +try: + import termios + # it's possible there is an incompatible termios from the + # McMillan Installer, make sure we have a UNIX-compatible termios + termios.tcgetattr, termios.tcsetattr +except (ImportError, AttributeError): + try: + import msvcrt + except ImportError: + getpass = fallback_getpass + else: + getpass = win_getpass +else: + getpass = unix_getpass diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/gettext.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/gettext.py new file mode 100644 index 0000000000000000000000000000000000000000..b98f501884b75a4e7d14ca124ad672938a99e541 --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/gettext.py @@ -0,0 +1,782 @@ +"""Internationalization and localization support. + +This module provides internationalization (I18N) and localization (L10N) +support for your Python programs by providing an interface to the GNU gettext +message catalog library. + +I18N refers to the operation by which a program is made aware of multiple +languages. L10N refers to the adaptation of your program, once +internationalized, to the local language and cultural habits. + +""" + +# This module represents the integration of work, contributions, feedback, and +# suggestions from the following people: +# +# Martin von Loewis, who wrote the initial implementation of the underlying +# C-based libintlmodule (later renamed _gettext), along with a skeletal +# gettext.py implementation. +# +# Peter Funk, who wrote fintl.py, a fairly complete wrapper around intlmodule, +# which also included a pure-Python implementation to read .mo files if +# intlmodule wasn't available. +# +# James Henstridge, who also wrote a gettext.py module, which has some +# interesting, but currently unsupported experimental features: the notion of +# a Catalog class and instances, and the ability to add to a catalog file via +# a Python API. +# +# Barry Warsaw integrated these modules, wrote the .install() API and code, +# and conformed all C and Python code to Python's coding standards. +# +# Francois Pinard and Marc-Andre Lemburg also contributed valuably to this +# module. +# +# J. David Ibanez implemented plural forms. Bruno Haible fixed some bugs. +# +# TODO: +# - Lazy loading of .mo files. Currently the entire catalog is loaded into +# memory, but that's probably bad for large translated programs. Instead, +# the lexical sort of original strings in GNU .mo files should be exploited +# to do binary searches and lazy initializations. Or you might want to use +# the undocumented double-hash algorithm for .mo files with hash tables, but +# you'll need to study the GNU gettext code to do this. +# +# - Support Solaris .mo file formats. Unfortunately, we've been unable to +# find this format documented anywhere. + + +import locale +import os +import re +import sys + + +__all__ = ['NullTranslations', 'GNUTranslations', 'Catalog', + 'find', 'translation', 'install', 'textdomain', 'bindtextdomain', + 'bind_textdomain_codeset', + 'dgettext', 'dngettext', 'gettext', 'lgettext', 'ldgettext', + 'ldngettext', 'lngettext', 'ngettext', + 'pgettext', 'dpgettext', 'npgettext', 'dnpgettext', + ] + +_default_localedir = os.path.join(sys.base_prefix, 'share', 'locale') + +# Expression parsing for plural form selection. +# +# The gettext library supports a small subset of C syntax. The only +# incompatible difference is that integer literals starting with zero are +# decimal. +# +# https://www.gnu.org/software/gettext/manual/gettext.html#Plural-forms +# http://git.savannah.gnu.org/cgit/gettext.git/tree/gettext-runtime/intl/plural.y + +_token_pattern = re.compile(r""" + (?P[ \t]+) | # spaces and horizontal tabs + (?P[0-9]+\b) | # decimal integer + (?Pn\b) | # only n is allowed + (?P[()]) | + (?P[-*/%+?:]|[>, + # <=, >=, ==, !=, &&, ||, + # ? : + # unary and bitwise ops + # not allowed + (?P\w+|.) # invalid token + """, re.VERBOSE|re.DOTALL) + +def _tokenize(plural): + for mo in re.finditer(_token_pattern, plural): + kind = mo.lastgroup + if kind == 'WHITESPACES': + continue + value = mo.group(kind) + if kind == 'INVALID': + raise ValueError('invalid token in plural form: %s' % value) + yield value + yield '' + +def _error(value): + if value: + return ValueError('unexpected token in plural form: %s' % value) + else: + return ValueError('unexpected end of plural form') + +_binary_ops = ( + ('||',), + ('&&',), + ('==', '!='), + ('<', '>', '<=', '>='), + ('+', '-'), + ('*', '/', '%'), +) +_binary_ops = {op: i for i, ops in enumerate(_binary_ops, 1) for op in ops} +_c2py_ops = {'||': 'or', '&&': 'and', '/': '//'} + +def _parse(tokens, priority=-1): + result = '' + nexttok = next(tokens) + while nexttok == '!': + result += 'not ' + nexttok = next(tokens) + + if nexttok == '(': + sub, nexttok = _parse(tokens) + result = '%s(%s)' % (result, sub) + if nexttok != ')': + raise ValueError('unbalanced parenthesis in plural form') + elif nexttok == 'n': + result = '%s%s' % (result, nexttok) + else: + try: + value = int(nexttok, 10) + except ValueError: + raise _error(nexttok) from None + result = '%s%d' % (result, value) + nexttok = next(tokens) + + j = 100 + while nexttok in _binary_ops: + i = _binary_ops[nexttok] + if i < priority: + break + # Break chained comparisons + if i in (3, 4) and j in (3, 4): # '==', '!=', '<', '>', '<=', '>=' + result = '(%s)' % result + # Replace some C operators by their Python equivalents + op = _c2py_ops.get(nexttok, nexttok) + right, nexttok = _parse(tokens, i + 1) + result = '%s %s %s' % (result, op, right) + j = i + if j == priority == 4: # '<', '>', '<=', '>=' + result = '(%s)' % result + + if nexttok == '?' and priority <= 0: + if_true, nexttok = _parse(tokens, 0) + if nexttok != ':': + raise _error(nexttok) + if_false, nexttok = _parse(tokens) + result = '%s if %s else %s' % (if_true, result, if_false) + if priority == 0: + result = '(%s)' % result + + return result, nexttok + +def _as_int(n): + try: + i = round(n) + except TypeError: + raise TypeError('Plural value must be an integer, got %s' % + (n.__class__.__name__,)) from None + import warnings + warnings.warn('Plural value must be an integer, got %s' % + (n.__class__.__name__,), + DeprecationWarning, 4) + return n + +def c2py(plural): + """Gets a C expression as used in PO files for plural forms and returns a + Python function that implements an equivalent expression. + """ + + if len(plural) > 1000: + raise ValueError('plural form expression is too long') + try: + result, nexttok = _parse(_tokenize(plural)) + if nexttok: + raise _error(nexttok) + + depth = 0 + for c in result: + if c == '(': + depth += 1 + if depth > 20: + # Python compiler limit is about 90. + # The most complex example has 2. + raise ValueError('plural form expression is too complex') + elif c == ')': + depth -= 1 + + ns = {'_as_int': _as_int} + exec('''if True: + def func(n): + if not isinstance(n, int): + n = _as_int(n) + return int(%s) + ''' % result, ns) + return ns['func'] + except RecursionError: + # Recursion error can be raised in _parse() or exec(). + raise ValueError('plural form expression is too complex') + + +def _expand_lang(loc): + loc = locale.normalize(loc) + COMPONENT_CODESET = 1 << 0 + COMPONENT_TERRITORY = 1 << 1 + COMPONENT_MODIFIER = 1 << 2 + # split up the locale into its base components + mask = 0 + pos = loc.find('@') + if pos >= 0: + modifier = loc[pos:] + loc = loc[:pos] + mask |= COMPONENT_MODIFIER + else: + modifier = '' + pos = loc.find('.') + if pos >= 0: + codeset = loc[pos:] + loc = loc[:pos] + mask |= COMPONENT_CODESET + else: + codeset = '' + pos = loc.find('_') + if pos >= 0: + territory = loc[pos:] + loc = loc[:pos] + mask |= COMPONENT_TERRITORY + else: + territory = '' + language = loc + ret = [] + for i in range(mask+1): + if not (i & ~mask): # if all components for this combo exist ... + val = language + if i & COMPONENT_TERRITORY: val += territory + if i & COMPONENT_CODESET: val += codeset + if i & COMPONENT_MODIFIER: val += modifier + ret.append(val) + ret.reverse() + return ret + + + +class NullTranslations: + def __init__(self, fp=None): + self._info = {} + self._charset = None + self._output_charset = None + self._fallback = None + if fp is not None: + self._parse(fp) + + def _parse(self, fp): + pass + + def add_fallback(self, fallback): + if self._fallback: + self._fallback.add_fallback(fallback) + else: + self._fallback = fallback + + def gettext(self, message): + if self._fallback: + return self._fallback.gettext(message) + return message + + def lgettext(self, message): + import warnings + warnings.warn('lgettext() is deprecated, use gettext() instead', + DeprecationWarning, 2) + if self._fallback: + with warnings.catch_warnings(): + warnings.filterwarnings('ignore', r'.*\blgettext\b.*', + DeprecationWarning) + return self._fallback.lgettext(message) + if self._output_charset: + return message.encode(self._output_charset) + return message.encode(locale.getpreferredencoding()) + + def ngettext(self, msgid1, msgid2, n): + if self._fallback: + return self._fallback.ngettext(msgid1, msgid2, n) + if n == 1: + return msgid1 + else: + return msgid2 + + def lngettext(self, msgid1, msgid2, n): + import warnings + warnings.warn('lngettext() is deprecated, use ngettext() instead', + DeprecationWarning, 2) + if self._fallback: + with warnings.catch_warnings(): + warnings.filterwarnings('ignore', r'.*\blngettext\b.*', + DeprecationWarning) + return self._fallback.lngettext(msgid1, msgid2, n) + if n == 1: + tmsg = msgid1 + else: + tmsg = msgid2 + if self._output_charset: + return tmsg.encode(self._output_charset) + return tmsg.encode(locale.getpreferredencoding()) + + def pgettext(self, context, message): + if self._fallback: + return self._fallback.pgettext(context, message) + return message + + def npgettext(self, context, msgid1, msgid2, n): + if self._fallback: + return self._fallback.npgettext(context, msgid1, msgid2, n) + if n == 1: + return msgid1 + else: + return msgid2 + + def info(self): + return self._info + + def charset(self): + return self._charset + + def output_charset(self): + import warnings + warnings.warn('output_charset() is deprecated', + DeprecationWarning, 2) + return self._output_charset + + def set_output_charset(self, charset): + import warnings + warnings.warn('set_output_charset() is deprecated', + DeprecationWarning, 2) + self._output_charset = charset + + def install(self, names=None): + import builtins + builtins.__dict__['_'] = self.gettext + if names is not None: + allowed = {'gettext', 'lgettext', 'lngettext', + 'ngettext', 'npgettext', 'pgettext'} + for name in allowed & set(names): + builtins.__dict__[name] = getattr(self, name) + + +class GNUTranslations(NullTranslations): + # Magic number of .mo files + LE_MAGIC = 0x950412de + BE_MAGIC = 0xde120495 + + # The encoding of a msgctxt and a msgid in a .mo file is + # msgctxt + "\x04" + msgid (gettext version >= 0.15) + CONTEXT = "%s\x04%s" + + # Acceptable .mo versions + VERSIONS = (0, 1) + + def _get_versions(self, version): + """Returns a tuple of major version, minor version""" + return (version >> 16, version & 0xffff) + + def _parse(self, fp): + """Override this method to support alternative .mo formats.""" + # Delay struct import for speeding up gettext import when .mo files + # are not used. + from struct import unpack + filename = getattr(fp, 'name', '') + # Parse the .mo file header, which consists of 5 little endian 32 + # bit words. + self._catalog = catalog = {} + self.plural = lambda n: int(n != 1) # germanic plural by default + buf = fp.read() + buflen = len(buf) + # Are we big endian or little endian? + magic = unpack('4I', buf[4:20]) + ii = '>II' + else: + raise OSError(0, 'Bad magic number', filename) + + major_version, minor_version = self._get_versions(version) + + if major_version not in self.VERSIONS: + raise OSError(0, 'Bad version number ' + str(major_version), filename) + + # Now put all messages from the .mo file buffer into the catalog + # dictionary. + for i in range(0, msgcount): + mlen, moff = unpack(ii, buf[masteridx:masteridx+8]) + mend = moff + mlen + tlen, toff = unpack(ii, buf[transidx:transidx+8]) + tend = toff + tlen + if mend < buflen and tend < buflen: + msg = buf[moff:mend] + tmsg = buf[toff:tend] + else: + raise OSError(0, 'File is corrupt', filename) + # See if we're looking at GNU .mo conventions for metadata + if mlen == 0: + # Catalog description + lastk = None + for b_item in tmsg.split(b'\n'): + item = b_item.decode().strip() + if not item: + continue + # Skip over comment lines: + if item.startswith('#-#-#-#-#') and item.endswith('#-#-#-#-#'): + continue + k = v = None + if ':' in item: + k, v = item.split(':', 1) + k = k.strip().lower() + v = v.strip() + self._info[k] = v + lastk = k + elif lastk: + self._info[lastk] += '\n' + item + if k == 'content-type': + self._charset = v.split('charset=')[1] + elif k == 'plural-forms': + v = v.split(';') + plural = v[1].split('plural=')[1] + self.plural = c2py(plural) + # Note: we unconditionally convert both msgids and msgstrs to + # Unicode using the character encoding specified in the charset + # parameter of the Content-Type header. The gettext documentation + # strongly encourages msgids to be us-ascii, but some applications + # require alternative encodings (e.g. Zope's ZCML and ZPT). For + # traditional gettext applications, the msgid conversion will + # cause no problems since us-ascii should always be a subset of + # the charset encoding. We may want to fall back to 8-bit msgids + # if the Unicode conversion fails. + charset = self._charset or 'ascii' + if b'\x00' in msg: + # Plural forms + msgid1, msgid2 = msg.split(b'\x00') + tmsg = tmsg.split(b'\x00') + msgid1 = str(msgid1, charset) + for i, x in enumerate(tmsg): + catalog[(msgid1, i)] = str(x, charset) + else: + catalog[str(msg, charset)] = str(tmsg, charset) + # advance to next entry in the seek tables + masteridx += 8 + transidx += 8 + + def lgettext(self, message): + import warnings + warnings.warn('lgettext() is deprecated, use gettext() instead', + DeprecationWarning, 2) + missing = object() + tmsg = self._catalog.get(message, missing) + if tmsg is missing: + if self._fallback: + return self._fallback.lgettext(message) + tmsg = message + if self._output_charset: + return tmsg.encode(self._output_charset) + return tmsg.encode(locale.getpreferredencoding()) + + def lngettext(self, msgid1, msgid2, n): + import warnings + warnings.warn('lngettext() is deprecated, use ngettext() instead', + DeprecationWarning, 2) + try: + tmsg = self._catalog[(msgid1, self.plural(n))] + except KeyError: + if self._fallback: + return self._fallback.lngettext(msgid1, msgid2, n) + if n == 1: + tmsg = msgid1 + else: + tmsg = msgid2 + if self._output_charset: + return tmsg.encode(self._output_charset) + return tmsg.encode(locale.getpreferredencoding()) + + def gettext(self, message): + missing = object() + tmsg = self._catalog.get(message, missing) + if tmsg is missing: + if self._fallback: + return self._fallback.gettext(message) + return message + return tmsg + + def ngettext(self, msgid1, msgid2, n): + try: + tmsg = self._catalog[(msgid1, self.plural(n))] + except KeyError: + if self._fallback: + return self._fallback.ngettext(msgid1, msgid2, n) + if n == 1: + tmsg = msgid1 + else: + tmsg = msgid2 + return tmsg + + def pgettext(self, context, message): + ctxt_msg_id = self.CONTEXT % (context, message) + missing = object() + tmsg = self._catalog.get(ctxt_msg_id, missing) + if tmsg is missing: + if self._fallback: + return self._fallback.pgettext(context, message) + return message + return tmsg + + def npgettext(self, context, msgid1, msgid2, n): + ctxt_msg_id = self.CONTEXT % (context, msgid1) + try: + tmsg = self._catalog[ctxt_msg_id, self.plural(n)] + except KeyError: + if self._fallback: + return self._fallback.npgettext(context, msgid1, msgid2, n) + if n == 1: + tmsg = msgid1 + else: + tmsg = msgid2 + return tmsg + + +# Locate a .mo file using the gettext strategy +def find(domain, localedir=None, languages=None, all=False): + # Get some reasonable defaults for arguments that were not supplied + if localedir is None: + localedir = _default_localedir + if languages is None: + languages = [] + for envar in ('LANGUAGE', 'LC_ALL', 'LC_MESSAGES', 'LANG'): + val = os.environ.get(envar) + if val: + languages = val.split(':') + break + if 'C' not in languages: + languages.append('C') + # now normalize and expand the languages + nelangs = [] + for lang in languages: + for nelang in _expand_lang(lang): + if nelang not in nelangs: + nelangs.append(nelang) + # select a language + if all: + result = [] + else: + result = None + for lang in nelangs: + if lang == 'C': + break + mofile = os.path.join(localedir, lang, 'LC_MESSAGES', '%s.mo' % domain) + if os.path.exists(mofile): + if all: + result.append(mofile) + else: + return mofile + return result + + + +# a mapping between absolute .mo file path and Translation object +_translations = {} +_unspecified = ['unspecified'] + +def translation(domain, localedir=None, languages=None, + class_=None, fallback=False, codeset=_unspecified): + if class_ is None: + class_ = GNUTranslations + mofiles = find(domain, localedir, languages, all=True) + if not mofiles: + if fallback: + return NullTranslations() + from errno import ENOENT + raise FileNotFoundError(ENOENT, + 'No translation file found for domain', domain) + # Avoid opening, reading, and parsing the .mo file after it's been done + # once. + result = None + for mofile in mofiles: + key = (class_, os.path.abspath(mofile)) + t = _translations.get(key) + if t is None: + with open(mofile, 'rb') as fp: + t = _translations.setdefault(key, class_(fp)) + # Copy the translation object to allow setting fallbacks and + # output charset. All other instance data is shared with the + # cached object. + # Delay copy import for speeding up gettext import when .mo files + # are not used. + import copy + t = copy.copy(t) + if codeset is not _unspecified: + import warnings + warnings.warn('parameter codeset is deprecated', + DeprecationWarning, 2) + if codeset: + with warnings.catch_warnings(): + warnings.filterwarnings('ignore', r'.*\bset_output_charset\b.*', + DeprecationWarning) + t.set_output_charset(codeset) + if result is None: + result = t + else: + result.add_fallback(t) + return result + + +def install(domain, localedir=None, codeset=_unspecified, names=None): + t = translation(domain, localedir, fallback=True, codeset=codeset) + t.install(names) + + + +# a mapping b/w domains and locale directories +_localedirs = {} +# a mapping b/w domains and codesets +_localecodesets = {} +# current global domain, `messages' used for compatibility w/ GNU gettext +_current_domain = 'messages' + + +def textdomain(domain=None): + global _current_domain + if domain is not None: + _current_domain = domain + return _current_domain + + +def bindtextdomain(domain, localedir=None): + global _localedirs + if localedir is not None: + _localedirs[domain] = localedir + return _localedirs.get(domain, _default_localedir) + + +def bind_textdomain_codeset(domain, codeset=None): + import warnings + warnings.warn('bind_textdomain_codeset() is deprecated', + DeprecationWarning, 2) + global _localecodesets + if codeset is not None: + _localecodesets[domain] = codeset + return _localecodesets.get(domain) + + +def dgettext(domain, message): + try: + t = translation(domain, _localedirs.get(domain, None)) + except OSError: + return message + return t.gettext(message) + +def ldgettext(domain, message): + import warnings + warnings.warn('ldgettext() is deprecated, use dgettext() instead', + DeprecationWarning, 2) + codeset = _localecodesets.get(domain) + try: + with warnings.catch_warnings(): + warnings.filterwarnings('ignore', r'.*\bparameter codeset\b.*', + DeprecationWarning) + t = translation(domain, _localedirs.get(domain, None), codeset=codeset) + except OSError: + return message.encode(codeset or locale.getpreferredencoding()) + with warnings.catch_warnings(): + warnings.filterwarnings('ignore', r'.*\blgettext\b.*', + DeprecationWarning) + return t.lgettext(message) + +def dngettext(domain, msgid1, msgid2, n): + try: + t = translation(domain, _localedirs.get(domain, None)) + except OSError: + if n == 1: + return msgid1 + else: + return msgid2 + return t.ngettext(msgid1, msgid2, n) + +def ldngettext(domain, msgid1, msgid2, n): + import warnings + warnings.warn('ldngettext() is deprecated, use dngettext() instead', + DeprecationWarning, 2) + codeset = _localecodesets.get(domain) + try: + with warnings.catch_warnings(): + warnings.filterwarnings('ignore', r'.*\bparameter codeset\b.*', + DeprecationWarning) + t = translation(domain, _localedirs.get(domain, None), codeset=codeset) + except OSError: + if n == 1: + tmsg = msgid1 + else: + tmsg = msgid2 + return tmsg.encode(codeset or locale.getpreferredencoding()) + with warnings.catch_warnings(): + warnings.filterwarnings('ignore', r'.*\blngettext\b.*', + DeprecationWarning) + return t.lngettext(msgid1, msgid2, n) + + +def dpgettext(domain, context, message): + try: + t = translation(domain, _localedirs.get(domain, None)) + except OSError: + return message + return t.pgettext(context, message) + + +def dnpgettext(domain, context, msgid1, msgid2, n): + try: + t = translation(domain, _localedirs.get(domain, None)) + except OSError: + if n == 1: + return msgid1 + else: + return msgid2 + return t.npgettext(context, msgid1, msgid2, n) + + +def gettext(message): + return dgettext(_current_domain, message) + +def lgettext(message): + import warnings + warnings.warn('lgettext() is deprecated, use gettext() instead', + DeprecationWarning, 2) + with warnings.catch_warnings(): + warnings.filterwarnings('ignore', r'.*\bldgettext\b.*', + DeprecationWarning) + return ldgettext(_current_domain, message) + +def ngettext(msgid1, msgid2, n): + return dngettext(_current_domain, msgid1, msgid2, n) + +def lngettext(msgid1, msgid2, n): + import warnings + warnings.warn('lngettext() is deprecated, use ngettext() instead', + DeprecationWarning, 2) + with warnings.catch_warnings(): + warnings.filterwarnings('ignore', r'.*\bldngettext\b.*', + DeprecationWarning) + return ldngettext(_current_domain, msgid1, msgid2, n) + + +def pgettext(context, message): + return dpgettext(_current_domain, context, message) + + +def npgettext(context, msgid1, msgid2, n): + return dnpgettext(_current_domain, context, msgid1, msgid2, n) + + +# dcgettext() has been deemed unnecessary and is not implemented. + +# James Henstridge's Catalog constructor from GNOME gettext. Documented usage +# was: +# +# import gettext +# cat = gettext.Catalog(PACKAGE, localedir=LOCALEDIR) +# _ = cat.gettext +# print _('Hello World') + +# The resulting catalog object currently don't support access through a +# dictionary API, which was supported (but apparently unused) in GNOME +# gettext. + +Catalog = translation diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/gzip.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/gzip.py new file mode 100644 index 0000000000000000000000000000000000000000..1101d35a5f1bd7d89c9e349402b973d96d8275cd --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/gzip.py @@ -0,0 +1,600 @@ +"""Functions that read and write gzipped files. + +The user of the file doesn't have to worry about the compression, +but random access is not allowed.""" + +# based on Andrew Kuchling's minigzip.py distributed with the zlib module + +import struct, sys, time, os +import zlib +import builtins +import io +import _compression + +__all__ = ["BadGzipFile", "GzipFile", "open", "compress", "decompress"] + +FTEXT, FHCRC, FEXTRA, FNAME, FCOMMENT = 1, 2, 4, 8, 16 + +READ, WRITE = 1, 2 + +_COMPRESS_LEVEL_FAST = 1 +_COMPRESS_LEVEL_TRADEOFF = 6 +_COMPRESS_LEVEL_BEST = 9 + + +def open(filename, mode="rb", compresslevel=_COMPRESS_LEVEL_BEST, + encoding=None, errors=None, newline=None): + """Open a gzip-compressed file in binary or text mode. + + The filename argument can be an actual filename (a str or bytes object), or + an existing file object to read from or write to. + + The mode argument can be "r", "rb", "w", "wb", "x", "xb", "a" or "ab" for + binary mode, or "rt", "wt", "xt" or "at" for text mode. The default mode is + "rb", and the default compresslevel is 9. + + For binary mode, this function is equivalent to the GzipFile constructor: + GzipFile(filename, mode, compresslevel). In this case, the encoding, errors + and newline arguments must not be provided. + + For text mode, a GzipFile object is created, and wrapped in an + io.TextIOWrapper instance with the specified encoding, error handling + behavior, and line ending(s). + + """ + if "t" in mode: + if "b" in mode: + raise ValueError("Invalid mode: %r" % (mode,)) + else: + if encoding is not None: + raise ValueError("Argument 'encoding' not supported in binary mode") + if errors is not None: + raise ValueError("Argument 'errors' not supported in binary mode") + if newline is not None: + raise ValueError("Argument 'newline' not supported in binary mode") + + gz_mode = mode.replace("t", "") + if isinstance(filename, (str, bytes, os.PathLike)): + binary_file = GzipFile(filename, gz_mode, compresslevel) + elif hasattr(filename, "read") or hasattr(filename, "write"): + binary_file = GzipFile(None, gz_mode, compresslevel, filename) + else: + raise TypeError("filename must be a str or bytes object, or a file") + + if "t" in mode: + return io.TextIOWrapper(binary_file, encoding, errors, newline) + else: + return binary_file + +def write32u(output, value): + # The L format writes the bit pattern correctly whether signed + # or unsigned. + output.write(struct.pack("' + + def _init_write(self, filename): + self.name = filename + self.crc = zlib.crc32(b"") + self.size = 0 + self.writebuf = [] + self.bufsize = 0 + self.offset = 0 # Current file offset for seek(), tell(), etc + + def _write_gzip_header(self, compresslevel): + self.fileobj.write(b'\037\213') # magic header + self.fileobj.write(b'\010') # compression method + try: + # RFC 1952 requires the FNAME field to be Latin-1. Do not + # include filenames that cannot be represented that way. + fname = os.path.basename(self.name) + if not isinstance(fname, bytes): + fname = fname.encode('latin-1') + if fname.endswith(b'.gz'): + fname = fname[:-3] + except UnicodeEncodeError: + fname = b'' + flags = 0 + if fname: + flags = FNAME + self.fileobj.write(chr(flags).encode('latin-1')) + mtime = self._write_mtime + if mtime is None: + mtime = time.time() + write32u(self.fileobj, int(mtime)) + if compresslevel == _COMPRESS_LEVEL_BEST: + xfl = b'\002' + elif compresslevel == _COMPRESS_LEVEL_FAST: + xfl = b'\004' + else: + xfl = b'\000' + self.fileobj.write(xfl) + self.fileobj.write(b'\377') + if fname: + self.fileobj.write(fname + b'\000') + + def write(self,data): + self._check_not_closed() + if self.mode != WRITE: + import errno + raise OSError(errno.EBADF, "write() on read-only GzipFile object") + + if self.fileobj is None: + raise ValueError("write() on closed GzipFile object") + + if isinstance(data, bytes): + length = len(data) + else: + # accept any data that supports the buffer protocol + data = memoryview(data) + length = data.nbytes + + if length > 0: + self.fileobj.write(self.compress.compress(data)) + self.size += length + self.crc = zlib.crc32(data, self.crc) + self.offset += length + + return length + + def read(self, size=-1): + self._check_not_closed() + if self.mode != READ: + import errno + raise OSError(errno.EBADF, "read() on write-only GzipFile object") + return self._buffer.read(size) + + def read1(self, size=-1): + """Implements BufferedIOBase.read1() + + Reads up to a buffer's worth of data if size is negative.""" + self._check_not_closed() + if self.mode != READ: + import errno + raise OSError(errno.EBADF, "read1() on write-only GzipFile object") + + if size < 0: + size = io.DEFAULT_BUFFER_SIZE + return self._buffer.read1(size) + + def peek(self, n): + self._check_not_closed() + if self.mode != READ: + import errno + raise OSError(errno.EBADF, "peek() on write-only GzipFile object") + return self._buffer.peek(n) + + @property + def closed(self): + return self.fileobj is None + + def close(self): + fileobj = self.fileobj + if fileobj is None: + return + self.fileobj = None + try: + if self.mode == WRITE: + fileobj.write(self.compress.flush()) + write32u(fileobj, self.crc) + # self.size may exceed 2 GiB, or even 4 GiB + write32u(fileobj, self.size & 0xffffffff) + elif self.mode == READ: + self._buffer.close() + finally: + myfileobj = self.myfileobj + if myfileobj: + self.myfileobj = None + myfileobj.close() + + def flush(self,zlib_mode=zlib.Z_SYNC_FLUSH): + self._check_not_closed() + if self.mode == WRITE: + # Ensure the compressor's buffer is flushed + self.fileobj.write(self.compress.flush(zlib_mode)) + self.fileobj.flush() + + def fileno(self): + """Invoke the underlying file object's fileno() method. + + This will raise AttributeError if the underlying file object + doesn't support fileno(). + """ + return self.fileobj.fileno() + + def rewind(self): + '''Return the uncompressed stream file position indicator to the + beginning of the file''' + if self.mode != READ: + raise OSError("Can't rewind in write mode") + self._buffer.seek(0) + + def readable(self): + return self.mode == READ + + def writable(self): + return self.mode == WRITE + + def seekable(self): + return True + + def seek(self, offset, whence=io.SEEK_SET): + if self.mode == WRITE: + if whence != io.SEEK_SET: + if whence == io.SEEK_CUR: + offset = self.offset + offset + else: + raise ValueError('Seek from end not supported') + if offset < self.offset: + raise OSError('Negative seek in write mode') + count = offset - self.offset + chunk = b'\0' * 1024 + for i in range(count // 1024): + self.write(chunk) + self.write(b'\0' * (count % 1024)) + elif self.mode == READ: + self._check_not_closed() + return self._buffer.seek(offset, whence) + + return self.offset + + def readline(self, size=-1): + self._check_not_closed() + return self._buffer.readline(size) + + +class _GzipReader(_compression.DecompressReader): + def __init__(self, fp): + super().__init__(_PaddedFile(fp), zlib.decompressobj, + wbits=-zlib.MAX_WBITS) + # Set flag indicating start of a new member + self._new_member = True + self._last_mtime = None + + def _init_read(self): + self._crc = zlib.crc32(b"") + self._stream_size = 0 # Decompressed size of unconcatenated stream + + def _read_exact(self, n): + '''Read exactly *n* bytes from `self._fp` + + This method is required because self._fp may be unbuffered, + i.e. return short reads. + ''' + + data = self._fp.read(n) + while len(data) < n: + b = self._fp.read(n - len(data)) + if not b: + raise EOFError("Compressed file ended before the " + "end-of-stream marker was reached") + data += b + return data + + def _read_gzip_header(self): + magic = self._fp.read(2) + if magic == b'': + return False + + if magic != b'\037\213': + raise BadGzipFile('Not a gzipped file (%r)' % magic) + + (method, flag, + self._last_mtime) = struct.unpack(">> import hashlib + >>> m = hashlib.md5() + >>> m.update(b"Nobody inspects") + >>> m.update(b" the spammish repetition") + >>> m.digest() + b'\\xbbd\\x9c\\x83\\xdd\\x1e\\xa5\\xc9\\xd9\\xde\\xc9\\xa1\\x8d\\xf0\\xff\\xe9' + +More condensed: + + >>> hashlib.sha224(b"Nobody inspects the spammish repetition").hexdigest() + 'a4337bc45a8fc544c03f52dc550cd6e1e87021bc896588bd79e901e2' + +""" + +# This tuple and __get_builtin_constructor() must be modified if a new +# always available algorithm is added. +__always_supported = ('md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512', + 'blake2b', 'blake2s', + 'sha3_224', 'sha3_256', 'sha3_384', 'sha3_512', + 'shake_128', 'shake_256') + + +algorithms_guaranteed = set(__always_supported) +algorithms_available = set(__always_supported) + +__all__ = __always_supported + ('new', 'algorithms_guaranteed', + 'algorithms_available', 'pbkdf2_hmac') + + +__builtin_constructor_cache = {} + +__block_openssl_constructor = { + 'sha3_224', 'sha3_256', 'sha3_384', 'sha3_512', + 'shake_128', 'shake_256', + 'blake2b', 'blake2s', +} + +def __get_builtin_constructor(name): + cache = __builtin_constructor_cache + constructor = cache.get(name) + if constructor is not None: + return constructor + try: + if name in {'SHA1', 'sha1'}: + import _sha1 + cache['SHA1'] = cache['sha1'] = _sha1.sha1 + elif name in {'MD5', 'md5'}: + import _md5 + cache['MD5'] = cache['md5'] = _md5.md5 + elif name in {'SHA256', 'sha256', 'SHA224', 'sha224'}: + import _sha256 + cache['SHA224'] = cache['sha224'] = _sha256.sha224 + cache['SHA256'] = cache['sha256'] = _sha256.sha256 + elif name in {'SHA512', 'sha512', 'SHA384', 'sha384'}: + import _sha512 + cache['SHA384'] = cache['sha384'] = _sha512.sha384 + cache['SHA512'] = cache['sha512'] = _sha512.sha512 + elif name in {'blake2b', 'blake2s'}: + import _blake2 + cache['blake2b'] = _blake2.blake2b + cache['blake2s'] = _blake2.blake2s + elif name in {'sha3_224', 'sha3_256', 'sha3_384', 'sha3_512'}: + import _sha3 + cache['sha3_224'] = _sha3.sha3_224 + cache['sha3_256'] = _sha3.sha3_256 + cache['sha3_384'] = _sha3.sha3_384 + cache['sha3_512'] = _sha3.sha3_512 + elif name in {'shake_128', 'shake_256'}: + import _sha3 + cache['shake_128'] = _sha3.shake_128 + cache['shake_256'] = _sha3.shake_256 + except ImportError: + pass # no extension module, this hash is unsupported. + + constructor = cache.get(name) + if constructor is not None: + return constructor + + raise ValueError('unsupported hash type ' + name) + + +def __get_openssl_constructor(name): + if name in __block_openssl_constructor: + # Prefer our blake2 and sha3 implementation. + return __get_builtin_constructor(name) + try: + f = getattr(_hashlib, 'openssl_' + name) + # Allow the C module to raise ValueError. The function will be + # defined but the hash not actually available thanks to OpenSSL. + f() + # Use the C function directly (very fast) + return f + except (AttributeError, ValueError): + return __get_builtin_constructor(name) + + +def __py_new(name, data=b'', **kwargs): + """new(name, data=b'', **kwargs) - Return a new hashing object using the + named algorithm; optionally initialized with data (which must be + a bytes-like object). + """ + return __get_builtin_constructor(name)(data, **kwargs) + + +def __hash_new(name, data=b'', **kwargs): + """new(name, data=b'') - Return a new hashing object using the named algorithm; + optionally initialized with data (which must be a bytes-like object). + """ + if name in __block_openssl_constructor: + # Prefer our blake2 and sha3 implementation + # OpenSSL 1.1.0 comes with a limited implementation of blake2b/s. + # It does neither support keyed blake2 nor advanced features like + # salt, personal, tree hashing or SSE. + return __get_builtin_constructor(name)(data, **kwargs) + try: + return _hashlib.new(name, data) + except ValueError: + # If the _hashlib module (OpenSSL) doesn't support the named + # hash, try using our builtin implementations. + # This allows for SHA224/256 and SHA384/512 support even though + # the OpenSSL library prior to 0.9.8 doesn't provide them. + return __get_builtin_constructor(name)(data) + + +try: + import _hashlib + new = __hash_new + __get_hash = __get_openssl_constructor + algorithms_available = algorithms_available.union( + _hashlib.openssl_md_meth_names) +except ImportError: + new = __py_new + __get_hash = __get_builtin_constructor + +try: + # OpenSSL's PKCS5_PBKDF2_HMAC requires OpenSSL 1.0+ with HMAC and SHA + from _hashlib import pbkdf2_hmac +except ImportError: + _trans_5C = bytes((x ^ 0x5C) for x in range(256)) + _trans_36 = bytes((x ^ 0x36) for x in range(256)) + + def pbkdf2_hmac(hash_name, password, salt, iterations, dklen=None): + """Password based key derivation function 2 (PKCS #5 v2.0) + + This Python implementations based on the hmac module about as fast + as OpenSSL's PKCS5_PBKDF2_HMAC for short passwords and much faster + for long passwords. + """ + if not isinstance(hash_name, str): + raise TypeError(hash_name) + + if not isinstance(password, (bytes, bytearray)): + password = bytes(memoryview(password)) + if not isinstance(salt, (bytes, bytearray)): + salt = bytes(memoryview(salt)) + + # Fast inline HMAC implementation + inner = new(hash_name) + outer = new(hash_name) + blocksize = getattr(inner, 'block_size', 64) + if len(password) > blocksize: + password = new(hash_name, password).digest() + password = password + b'\x00' * (blocksize - len(password)) + inner.update(password.translate(_trans_36)) + outer.update(password.translate(_trans_5C)) + + def prf(msg, inner=inner, outer=outer): + # PBKDF2_HMAC uses the password as key. We can re-use the same + # digest objects and just update copies to skip initialization. + icpy = inner.copy() + ocpy = outer.copy() + icpy.update(msg) + ocpy.update(icpy.digest()) + return ocpy.digest() + + if iterations < 1: + raise ValueError(iterations) + if dklen is None: + dklen = outer.digest_size + if dklen < 1: + raise ValueError(dklen) + + dkey = b'' + loop = 1 + from_bytes = int.from_bytes + while len(dkey) < dklen: + prev = prf(salt + loop.to_bytes(4, 'big')) + # endianness doesn't matter here as long to / from use the same + rkey = int.from_bytes(prev, 'big') + for i in range(iterations - 1): + prev = prf(prev) + # rkey = rkey ^ prev + rkey ^= from_bytes(prev, 'big') + loop += 1 + dkey += rkey.to_bytes(inner.digest_size, 'big') + + return dkey[:dklen] + +try: + # OpenSSL's scrypt requires OpenSSL 1.1+ + from _hashlib import scrypt +except ImportError: + pass + + +for __func_name in __always_supported: + # try them all, some may not work due to the OpenSSL + # version not supporting that algorithm. + try: + globals()[__func_name] = __get_hash(__func_name) + except ValueError: + import logging + logging.exception('code for hash %s was not found.', __func_name) + + +# Cleanup locals() +del __always_supported, __func_name, __get_hash +del __py_new, __hash_new, __get_openssl_constructor diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/imp.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/imp.py new file mode 100644 index 0000000000000000000000000000000000000000..31f8c766381adc3c125fedcc704277b1f6fa7968 --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/imp.py @@ -0,0 +1,345 @@ +"""This module provides the components needed to build your own __import__ +function. Undocumented functions are obsolete. + +In most cases it is preferred you consider using the importlib module's +functionality over this module. + +""" +# (Probably) need to stay in _imp +from _imp import (lock_held, acquire_lock, release_lock, + get_frozen_object, is_frozen_package, + init_frozen, is_builtin, is_frozen, + _fix_co_filename) +try: + from _imp import create_dynamic +except ImportError: + # Platform doesn't support dynamic loading. + create_dynamic = None + +from importlib._bootstrap import _ERR_MSG, _exec, _load, _builtin_from_name +from importlib._bootstrap_external import SourcelessFileLoader + +from importlib import machinery +from importlib import util +import importlib +import os +import sys +import tokenize +import types +import warnings + +warnings.warn("the imp module is deprecated in favour of importlib; " + "see the module's documentation for alternative uses", + DeprecationWarning, stacklevel=2) + +# DEPRECATED +SEARCH_ERROR = 0 +PY_SOURCE = 1 +PY_COMPILED = 2 +C_EXTENSION = 3 +PY_RESOURCE = 4 +PKG_DIRECTORY = 5 +C_BUILTIN = 6 +PY_FROZEN = 7 +PY_CODERESOURCE = 8 +IMP_HOOK = 9 + + +def new_module(name): + """**DEPRECATED** + + Create a new module. + + The module is not entered into sys.modules. + + """ + return types.ModuleType(name) + + +def get_magic(): + """**DEPRECATED** + + Return the magic number for .pyc files. + """ + return util.MAGIC_NUMBER + + +def get_tag(): + """Return the magic tag for .pyc files.""" + return sys.implementation.cache_tag + + +def cache_from_source(path, debug_override=None): + """**DEPRECATED** + + Given the path to a .py file, return the path to its .pyc file. + + The .py file does not need to exist; this simply returns the path to the + .pyc file calculated as if the .py file were imported. + + If debug_override is not None, then it must be a boolean and is used in + place of sys.flags.optimize. + + If sys.implementation.cache_tag is None then NotImplementedError is raised. + + """ + with warnings.catch_warnings(): + warnings.simplefilter('ignore') + return util.cache_from_source(path, debug_override) + + +def source_from_cache(path): + """**DEPRECATED** + + Given the path to a .pyc. file, return the path to its .py file. + + The .pyc file does not need to exist; this simply returns the path to + the .py file calculated to correspond to the .pyc file. If path does + not conform to PEP 3147 format, ValueError will be raised. If + sys.implementation.cache_tag is None then NotImplementedError is raised. + + """ + return util.source_from_cache(path) + + +def get_suffixes(): + """**DEPRECATED**""" + extensions = [(s, 'rb', C_EXTENSION) for s in machinery.EXTENSION_SUFFIXES] + source = [(s, 'r', PY_SOURCE) for s in machinery.SOURCE_SUFFIXES] + bytecode = [(s, 'rb', PY_COMPILED) for s in machinery.BYTECODE_SUFFIXES] + + return extensions + source + bytecode + + +class NullImporter: + + """**DEPRECATED** + + Null import object. + + """ + + def __init__(self, path): + if path == '': + raise ImportError('empty pathname', path='') + elif os.path.isdir(path): + raise ImportError('existing directory', path=path) + + def find_module(self, fullname): + """Always returns None.""" + return None + + +class _HackedGetData: + + """Compatibility support for 'file' arguments of various load_*() + functions.""" + + def __init__(self, fullname, path, file=None): + super().__init__(fullname, path) + self.file = file + + def get_data(self, path): + """Gross hack to contort loader to deal w/ load_*()'s bad API.""" + if self.file and path == self.path: + # The contract of get_data() requires us to return bytes. Reopen the + # file in binary mode if needed. + if not self.file.closed: + file = self.file + if 'b' not in file.mode: + file.close() + if self.file.closed: + self.file = file = open(self.path, 'rb') + + with file: + return file.read() + else: + return super().get_data(path) + + +class _LoadSourceCompatibility(_HackedGetData, machinery.SourceFileLoader): + + """Compatibility support for implementing load_source().""" + + +def load_source(name, pathname, file=None): + loader = _LoadSourceCompatibility(name, pathname, file) + spec = util.spec_from_file_location(name, pathname, loader=loader) + if name in sys.modules: + module = _exec(spec, sys.modules[name]) + else: + module = _load(spec) + # To allow reloading to potentially work, use a non-hacked loader which + # won't rely on a now-closed file object. + module.__loader__ = machinery.SourceFileLoader(name, pathname) + module.__spec__.loader = module.__loader__ + return module + + +class _LoadCompiledCompatibility(_HackedGetData, SourcelessFileLoader): + + """Compatibility support for implementing load_compiled().""" + + +def load_compiled(name, pathname, file=None): + """**DEPRECATED**""" + loader = _LoadCompiledCompatibility(name, pathname, file) + spec = util.spec_from_file_location(name, pathname, loader=loader) + if name in sys.modules: + module = _exec(spec, sys.modules[name]) + else: + module = _load(spec) + # To allow reloading to potentially work, use a non-hacked loader which + # won't rely on a now-closed file object. + module.__loader__ = SourcelessFileLoader(name, pathname) + module.__spec__.loader = module.__loader__ + return module + + +def load_package(name, path): + """**DEPRECATED**""" + if os.path.isdir(path): + extensions = (machinery.SOURCE_SUFFIXES[:] + + machinery.BYTECODE_SUFFIXES[:]) + for extension in extensions: + init_path = os.path.join(path, '__init__' + extension) + if os.path.exists(init_path): + path = init_path + break + else: + raise ValueError('{!r} is not a package'.format(path)) + spec = util.spec_from_file_location(name, path, + submodule_search_locations=[]) + if name in sys.modules: + return _exec(spec, sys.modules[name]) + else: + return _load(spec) + + +def load_module(name, file, filename, details): + """**DEPRECATED** + + Load a module, given information returned by find_module(). + + The module name must include the full package name, if any. + + """ + suffix, mode, type_ = details + if mode and (not mode.startswith(('r', 'U')) or '+' in mode): + raise ValueError('invalid file open mode {!r}'.format(mode)) + elif file is None and type_ in {PY_SOURCE, PY_COMPILED}: + msg = 'file object required for import (type code {})'.format(type_) + raise ValueError(msg) + elif type_ == PY_SOURCE: + return load_source(name, filename, file) + elif type_ == PY_COMPILED: + return load_compiled(name, filename, file) + elif type_ == C_EXTENSION and load_dynamic is not None: + if file is None: + with open(filename, 'rb') as opened_file: + return load_dynamic(name, filename, opened_file) + else: + return load_dynamic(name, filename, file) + elif type_ == PKG_DIRECTORY: + return load_package(name, filename) + elif type_ == C_BUILTIN: + return init_builtin(name) + elif type_ == PY_FROZEN: + return init_frozen(name) + else: + msg = "Don't know how to import {} (type code {})".format(name, type_) + raise ImportError(msg, name=name) + + +def find_module(name, path=None): + """**DEPRECATED** + + Search for a module. + + If path is omitted or None, search for a built-in, frozen or special + module and continue search in sys.path. The module name cannot + contain '.'; to search for a submodule of a package, pass the + submodule name and the package's __path__. + + """ + if not isinstance(name, str): + raise TypeError("'name' must be a str, not {}".format(type(name))) + elif not isinstance(path, (type(None), list)): + # Backwards-compatibility + raise RuntimeError("'path' must be None or a list, " + "not {}".format(type(path))) + + if path is None: + if is_builtin(name): + return None, None, ('', '', C_BUILTIN) + elif is_frozen(name): + return None, None, ('', '', PY_FROZEN) + else: + path = sys.path + + for entry in path: + package_directory = os.path.join(entry, name) + for suffix in ['.py', machinery.BYTECODE_SUFFIXES[0]]: + package_file_name = '__init__' + suffix + file_path = os.path.join(package_directory, package_file_name) + if os.path.isfile(file_path): + return None, package_directory, ('', '', PKG_DIRECTORY) + for suffix, mode, type_ in get_suffixes(): + file_name = name + suffix + file_path = os.path.join(entry, file_name) + if os.path.isfile(file_path): + break + else: + continue + break # Break out of outer loop when breaking out of inner loop. + else: + raise ImportError(_ERR_MSG.format(name), name=name) + + encoding = None + if 'b' not in mode: + with open(file_path, 'rb') as file: + encoding = tokenize.detect_encoding(file.readline)[0] + file = open(file_path, mode, encoding=encoding) + return file, file_path, (suffix, mode, type_) + + +def reload(module): + """**DEPRECATED** + + Reload the module and return it. + + The module must have been successfully imported before. + + """ + return importlib.reload(module) + + +def init_builtin(name): + """**DEPRECATED** + + Load and return a built-in module by name, or None is such module doesn't + exist + """ + try: + return _builtin_from_name(name) + except ImportError: + return None + + +if create_dynamic: + def load_dynamic(name, path, file=None): + """**DEPRECATED** + + Load an extension module. + """ + import importlib.machinery + loader = importlib.machinery.ExtensionFileLoader(name, path) + + # Issue #24748: Skip the sys.modules check in _load_module_shim; + # always load new extension + spec = importlib.machinery.ModuleSpec( + name=name, loader=loader, origin=path) + return _load(spec) + +else: + load_dynamic = None diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/inspect.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/inspect.py new file mode 100644 index 0000000000000000000000000000000000000000..7ffc7d31bd8a6a9b64c24f6057092f82a1fada29 --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/inspect.py @@ -0,0 +1,3167 @@ +"""Get useful information from live Python objects. + +This module encapsulates the interface provided by the internal special +attributes (co_*, im_*, tb_*, etc.) in a friendlier fashion. +It also provides some help for examining source code and class layout. + +Here are some of the useful functions provided by this module: + + ismodule(), isclass(), ismethod(), isfunction(), isgeneratorfunction(), + isgenerator(), istraceback(), isframe(), iscode(), isbuiltin(), + isroutine() - check object types + getmembers() - get members of an object that satisfy a given condition + + getfile(), getsourcefile(), getsource() - find an object's source code + getdoc(), getcomments() - get documentation on an object + getmodule() - determine the module that an object came from + getclasstree() - arrange classes so as to represent their hierarchy + + getargvalues(), getcallargs() - get info about function arguments + getfullargspec() - same, with support for Python 3 features + formatargvalues() - format an argument spec + getouterframes(), getinnerframes() - get info about frames + currentframe() - get the current stack frame + stack(), trace() - get info about frames on the stack or in a traceback + + signature() - get a Signature object for the callable +""" + +# This module is in the public domain. No warranties. + +__author__ = ('Ka-Ping Yee ', + 'Yury Selivanov ') + +import abc +import dis +import collections.abc +import enum +import importlib.machinery +import itertools +import linecache +import os +import re +import sys +import tokenize +import token +import types +import warnings +import functools +import builtins +from operator import attrgetter +from collections import namedtuple, OrderedDict + +# Create constants for the compiler flags in Include/code.h +# We try to get them from dis to avoid duplication +mod_dict = globals() +for k, v in dis.COMPILER_FLAG_NAMES.items(): + mod_dict["CO_" + v] = k + +# See Include/object.h +TPFLAGS_IS_ABSTRACT = 1 << 20 + +# ----------------------------------------------------------- type-checking +def ismodule(object): + """Return true if the object is a module. + + Module objects provide these attributes: + __cached__ pathname to byte compiled file + __doc__ documentation string + __file__ filename (missing for built-in modules)""" + return isinstance(object, types.ModuleType) + +def isclass(object): + """Return true if the object is a class. + + Class objects provide these attributes: + __doc__ documentation string + __module__ name of module in which this class was defined""" + return isinstance(object, type) + +def ismethod(object): + """Return true if the object is an instance method. + + Instance method objects provide these attributes: + __doc__ documentation string + __name__ name with which this method was defined + __func__ function object containing implementation of method + __self__ instance to which this method is bound""" + return isinstance(object, types.MethodType) + +def ismethoddescriptor(object): + """Return true if the object is a method descriptor. + + But not if ismethod() or isclass() or isfunction() are true. + + This is new in Python 2.2, and, for example, is true of int.__add__. + An object passing this test has a __get__ attribute but not a __set__ + attribute, but beyond that the set of attributes varies. __name__ is + usually sensible, and __doc__ often is. + + Methods implemented via descriptors that also pass one of the other + tests return false from the ismethoddescriptor() test, simply because + the other tests promise more -- you can, e.g., count on having the + __func__ attribute (etc) when an object passes ismethod().""" + if isclass(object) or ismethod(object) or isfunction(object): + # mutual exclusion + return False + tp = type(object) + return hasattr(tp, "__get__") and not hasattr(tp, "__set__") + +def isdatadescriptor(object): + """Return true if the object is a data descriptor. + + Data descriptors have a __set__ or a __delete__ attribute. Examples are + properties (defined in Python) and getsets and members (defined in C). + Typically, data descriptors will also have __name__ and __doc__ attributes + (properties, getsets, and members have both of these attributes), but this + is not guaranteed.""" + if isclass(object) or ismethod(object) or isfunction(object): + # mutual exclusion + return False + tp = type(object) + return hasattr(tp, "__set__") or hasattr(tp, "__delete__") + +if hasattr(types, 'MemberDescriptorType'): + # CPython and equivalent + def ismemberdescriptor(object): + """Return true if the object is a member descriptor. + + Member descriptors are specialized descriptors defined in extension + modules.""" + return isinstance(object, types.MemberDescriptorType) +else: + # Other implementations + def ismemberdescriptor(object): + """Return true if the object is a member descriptor. + + Member descriptors are specialized descriptors defined in extension + modules.""" + return False + +if hasattr(types, 'GetSetDescriptorType'): + # CPython and equivalent + def isgetsetdescriptor(object): + """Return true if the object is a getset descriptor. + + getset descriptors are specialized descriptors defined in extension + modules.""" + return isinstance(object, types.GetSetDescriptorType) +else: + # Other implementations + def isgetsetdescriptor(object): + """Return true if the object is a getset descriptor. + + getset descriptors are specialized descriptors defined in extension + modules.""" + return False + +def isfunction(object): + """Return true if the object is a user-defined function. + + Function objects provide these attributes: + __doc__ documentation string + __name__ name with which this function was defined + __code__ code object containing compiled function bytecode + __defaults__ tuple of any default values for arguments + __globals__ global namespace in which this function was defined + __annotations__ dict of parameter annotations + __kwdefaults__ dict of keyword only parameters with defaults""" + return isinstance(object, types.FunctionType) + +def _has_code_flag(f, flag): + """Return true if ``f`` is a function (or a method or functools.partial + wrapper wrapping a function) whose code object has the given ``flag`` + set in its flags.""" + while ismethod(f): + f = f.__func__ + f = functools._unwrap_partial(f) + if not isfunction(f): + return False + return bool(f.__code__.co_flags & flag) + +def isgeneratorfunction(obj): + """Return true if the object is a user-defined generator function. + + Generator function objects provide the same attributes as functions. + See help(isfunction) for a list of attributes.""" + return _has_code_flag(obj, CO_GENERATOR) + +def iscoroutinefunction(obj): + """Return true if the object is a coroutine function. + + Coroutine functions are defined with "async def" syntax. + """ + return _has_code_flag(obj, CO_COROUTINE) + +def isasyncgenfunction(obj): + """Return true if the object is an asynchronous generator function. + + Asynchronous generator functions are defined with "async def" + syntax and have "yield" expressions in their body. + """ + return _has_code_flag(obj, CO_ASYNC_GENERATOR) + +def isasyncgen(object): + """Return true if the object is an asynchronous generator.""" + return isinstance(object, types.AsyncGeneratorType) + +def isgenerator(object): + """Return true if the object is a generator. + + Generator objects provide these attributes: + __iter__ defined to support iteration over container + close raises a new GeneratorExit exception inside the + generator to terminate the iteration + gi_code code object + gi_frame frame object or possibly None once the generator has + been exhausted + gi_running set to 1 when generator is executing, 0 otherwise + next return the next item from the container + send resumes the generator and "sends" a value that becomes + the result of the current yield-expression + throw used to raise an exception inside the generator""" + return isinstance(object, types.GeneratorType) + +def iscoroutine(object): + """Return true if the object is a coroutine.""" + return isinstance(object, types.CoroutineType) + +def isawaitable(object): + """Return true if object can be passed to an ``await`` expression.""" + return (isinstance(object, types.CoroutineType) or + isinstance(object, types.GeneratorType) and + bool(object.gi_code.co_flags & CO_ITERABLE_COROUTINE) or + isinstance(object, collections.abc.Awaitable)) + +def istraceback(object): + """Return true if the object is a traceback. + + Traceback objects provide these attributes: + tb_frame frame object at this level + tb_lasti index of last attempted instruction in bytecode + tb_lineno current line number in Python source code + tb_next next inner traceback object (called by this level)""" + return isinstance(object, types.TracebackType) + +def isframe(object): + """Return true if the object is a frame object. + + Frame objects provide these attributes: + f_back next outer frame object (this frame's caller) + f_builtins built-in namespace seen by this frame + f_code code object being executed in this frame + f_globals global namespace seen by this frame + f_lasti index of last attempted instruction in bytecode + f_lineno current line number in Python source code + f_locals local namespace seen by this frame + f_trace tracing function for this frame, or None""" + return isinstance(object, types.FrameType) + +def iscode(object): + """Return true if the object is a code object. + + Code objects provide these attributes: + co_argcount number of arguments (not including *, ** args + or keyword only arguments) + co_code string of raw compiled bytecode + co_cellvars tuple of names of cell variables + co_consts tuple of constants used in the bytecode + co_filename name of file in which this code object was created + co_firstlineno number of first line in Python source code + co_flags bitmap: 1=optimized | 2=newlocals | 4=*arg | 8=**arg + | 16=nested | 32=generator | 64=nofree | 128=coroutine + | 256=iterable_coroutine | 512=async_generator + co_freevars tuple of names of free variables + co_posonlyargcount number of positional only arguments + co_kwonlyargcount number of keyword only arguments (not including ** arg) + co_lnotab encoded mapping of line numbers to bytecode indices + co_name name with which this code object was defined + co_names tuple of names of local variables + co_nlocals number of local variables + co_stacksize virtual machine stack space required + co_varnames tuple of names of arguments and local variables""" + return isinstance(object, types.CodeType) + +def isbuiltin(object): + """Return true if the object is a built-in function or method. + + Built-in functions and methods provide these attributes: + __doc__ documentation string + __name__ original name of this function or method + __self__ instance to which a method is bound, or None""" + return isinstance(object, types.BuiltinFunctionType) + +def isroutine(object): + """Return true if the object is any kind of function or method.""" + return (isbuiltin(object) + or isfunction(object) + or ismethod(object) + or ismethoddescriptor(object)) + +def isabstract(object): + """Return true if the object is an abstract base class (ABC).""" + if not isinstance(object, type): + return False + if object.__flags__ & TPFLAGS_IS_ABSTRACT: + return True + if not issubclass(type(object), abc.ABCMeta): + return False + if hasattr(object, '__abstractmethods__'): + # It looks like ABCMeta.__new__ has finished running; + # TPFLAGS_IS_ABSTRACT should have been accurate. + return False + # It looks like ABCMeta.__new__ has not finished running yet; we're + # probably in __init_subclass__. We'll look for abstractmethods manually. + for name, value in object.__dict__.items(): + if getattr(value, "__isabstractmethod__", False): + return True + for base in object.__bases__: + for name in getattr(base, "__abstractmethods__", ()): + value = getattr(object, name, None) + if getattr(value, "__isabstractmethod__", False): + return True + return False + +def getmembers(object, predicate=None): + """Return all members of an object as (name, value) pairs sorted by name. + Optionally, only return members that satisfy a given predicate.""" + if isclass(object): + mro = (object,) + getmro(object) + else: + mro = () + results = [] + processed = set() + names = dir(object) + # :dd any DynamicClassAttributes to the list of names if object is a class; + # this may result in duplicate entries if, for example, a virtual + # attribute with the same name as a DynamicClassAttribute exists + try: + for base in object.__bases__: + for k, v in base.__dict__.items(): + if isinstance(v, types.DynamicClassAttribute): + names.append(k) + except AttributeError: + pass + for key in names: + # First try to get the value via getattr. Some descriptors don't + # like calling their __get__ (see bug #1785), so fall back to + # looking in the __dict__. + try: + value = getattr(object, key) + # handle the duplicate key + if key in processed: + raise AttributeError + except AttributeError: + for base in mro: + if key in base.__dict__: + value = base.__dict__[key] + break + else: + # could be a (currently) missing slot member, or a buggy + # __dir__; discard and move on + continue + if not predicate or predicate(value): + results.append((key, value)) + processed.add(key) + results.sort(key=lambda pair: pair[0]) + return results + +Attribute = namedtuple('Attribute', 'name kind defining_class object') + +def classify_class_attrs(cls): + """Return list of attribute-descriptor tuples. + + For each name in dir(cls), the return list contains a 4-tuple + with these elements: + + 0. The name (a string). + + 1. The kind of attribute this is, one of these strings: + 'class method' created via classmethod() + 'static method' created via staticmethod() + 'property' created via property() + 'method' any other flavor of method or descriptor + 'data' not a method + + 2. The class which defined this attribute (a class). + + 3. The object as obtained by calling getattr; if this fails, or if the + resulting object does not live anywhere in the class' mro (including + metaclasses) then the object is looked up in the defining class's + dict (found by walking the mro). + + If one of the items in dir(cls) is stored in the metaclass it will now + be discovered and not have None be listed as the class in which it was + defined. Any items whose home class cannot be discovered are skipped. + """ + + mro = getmro(cls) + metamro = getmro(type(cls)) # for attributes stored in the metaclass + metamro = tuple(cls for cls in metamro if cls not in (type, object)) + class_bases = (cls,) + mro + all_bases = class_bases + metamro + names = dir(cls) + # :dd any DynamicClassAttributes to the list of names; + # this may result in duplicate entries if, for example, a virtual + # attribute with the same name as a DynamicClassAttribute exists. + for base in mro: + for k, v in base.__dict__.items(): + if isinstance(v, types.DynamicClassAttribute): + names.append(k) + result = [] + processed = set() + + for name in names: + # Get the object associated with the name, and where it was defined. + # Normal objects will be looked up with both getattr and directly in + # its class' dict (in case getattr fails [bug #1785], and also to look + # for a docstring). + # For DynamicClassAttributes on the second pass we only look in the + # class's dict. + # + # Getting an obj from the __dict__ sometimes reveals more than + # using getattr. Static and class methods are dramatic examples. + homecls = None + get_obj = None + dict_obj = None + if name not in processed: + try: + if name == '__dict__': + raise Exception("__dict__ is special, don't want the proxy") + get_obj = getattr(cls, name) + except Exception as exc: + pass + else: + homecls = getattr(get_obj, "__objclass__", homecls) + if homecls not in class_bases: + # if the resulting object does not live somewhere in the + # mro, drop it and search the mro manually + homecls = None + last_cls = None + # first look in the classes + for srch_cls in class_bases: + srch_obj = getattr(srch_cls, name, None) + if srch_obj is get_obj: + last_cls = srch_cls + # then check the metaclasses + for srch_cls in metamro: + try: + srch_obj = srch_cls.__getattr__(cls, name) + except AttributeError: + continue + if srch_obj is get_obj: + last_cls = srch_cls + if last_cls is not None: + homecls = last_cls + for base in all_bases: + if name in base.__dict__: + dict_obj = base.__dict__[name] + if homecls not in metamro: + homecls = base + break + if homecls is None: + # unable to locate the attribute anywhere, most likely due to + # buggy custom __dir__; discard and move on + continue + obj = get_obj if get_obj is not None else dict_obj + # Classify the object or its descriptor. + if isinstance(dict_obj, (staticmethod, types.BuiltinMethodType)): + kind = "static method" + obj = dict_obj + elif isinstance(dict_obj, (classmethod, types.ClassMethodDescriptorType)): + kind = "class method" + obj = dict_obj + elif isinstance(dict_obj, property): + kind = "property" + obj = dict_obj + elif isroutine(obj): + kind = "method" + else: + kind = "data" + result.append(Attribute(name, kind, homecls, obj)) + processed.add(name) + return result + +# ----------------------------------------------------------- class helpers + +def getmro(cls): + "Return tuple of base classes (including cls) in method resolution order." + return cls.__mro__ + +# -------------------------------------------------------- function helpers + +def unwrap(func, *, stop=None): + """Get the object wrapped by *func*. + + Follows the chain of :attr:`__wrapped__` attributes returning the last + object in the chain. + + *stop* is an optional callback accepting an object in the wrapper chain + as its sole argument that allows the unwrapping to be terminated early if + the callback returns a true value. If the callback never returns a true + value, the last object in the chain is returned as usual. For example, + :func:`signature` uses this to stop unwrapping if any object in the + chain has a ``__signature__`` attribute defined. + + :exc:`ValueError` is raised if a cycle is encountered. + + """ + if stop is None: + def _is_wrapper(f): + return hasattr(f, '__wrapped__') + else: + def _is_wrapper(f): + return hasattr(f, '__wrapped__') and not stop(f) + f = func # remember the original func for error reporting + # Memoise by id to tolerate non-hashable objects, but store objects to + # ensure they aren't destroyed, which would allow their IDs to be reused. + memo = {id(f): f} + recursion_limit = sys.getrecursionlimit() + while _is_wrapper(func): + func = func.__wrapped__ + id_func = id(func) + if (id_func in memo) or (len(memo) >= recursion_limit): + raise ValueError('wrapper loop when unwrapping {!r}'.format(f)) + memo[id_func] = func + return func + +# -------------------------------------------------- source code extraction +def indentsize(line): + """Return the indent size, in spaces, at the start of a line of text.""" + expline = line.expandtabs() + return len(expline) - len(expline.lstrip()) + +def _findclass(func): + cls = sys.modules.get(func.__module__) + if cls is None: + return None + for name in func.__qualname__.split('.')[:-1]: + cls = getattr(cls, name) + if not isclass(cls): + return None + return cls + +def _finddoc(obj): + if isclass(obj): + for base in obj.__mro__: + if base is not object: + try: + doc = base.__doc__ + except AttributeError: + continue + if doc is not None: + return doc + return None + + if ismethod(obj): + name = obj.__func__.__name__ + self = obj.__self__ + if (isclass(self) and + getattr(getattr(self, name, None), '__func__') is obj.__func__): + # classmethod + cls = self + else: + cls = self.__class__ + elif isfunction(obj): + name = obj.__name__ + cls = _findclass(obj) + if cls is None or getattr(cls, name) is not obj: + return None + elif isbuiltin(obj): + name = obj.__name__ + self = obj.__self__ + if (isclass(self) and + self.__qualname__ + '.' + name == obj.__qualname__): + # classmethod + cls = self + else: + cls = self.__class__ + # Should be tested before isdatadescriptor(). + elif isinstance(obj, property): + func = obj.fget + name = func.__name__ + cls = _findclass(func) + if cls is None or getattr(cls, name) is not obj: + return None + elif ismethoddescriptor(obj) or isdatadescriptor(obj): + name = obj.__name__ + cls = obj.__objclass__ + if getattr(cls, name) is not obj: + return None + if ismemberdescriptor(obj): + slots = getattr(cls, '__slots__', None) + if isinstance(slots, dict) and name in slots: + return slots[name] + else: + return None + for base in cls.__mro__: + try: + doc = getattr(base, name).__doc__ + except AttributeError: + continue + if doc is not None: + return doc + return None + +def getdoc(object): + """Get the documentation string for an object. + + All tabs are expanded to spaces. To clean up docstrings that are + indented to line up with blocks of code, any whitespace than can be + uniformly removed from the second line onwards is removed.""" + try: + doc = object.__doc__ + except AttributeError: + return None + if doc is None: + try: + doc = _finddoc(object) + except (AttributeError, TypeError): + return None + if not isinstance(doc, str): + return None + return cleandoc(doc) + +def cleandoc(doc): + """Clean up indentation from docstrings. + + Any whitespace that can be uniformly removed from the second line + onwards is removed.""" + try: + lines = doc.expandtabs().split('\n') + except UnicodeError: + return None + else: + # Find minimum indentation of any non-blank lines after first line. + margin = sys.maxsize + for line in lines[1:]: + content = len(line.lstrip()) + if content: + indent = len(line) - content + margin = min(margin, indent) + # Remove indentation. + if lines: + lines[0] = lines[0].lstrip() + if margin < sys.maxsize: + for i in range(1, len(lines)): lines[i] = lines[i][margin:] + # Remove any trailing or leading blank lines. + while lines and not lines[-1]: + lines.pop() + while lines and not lines[0]: + lines.pop(0) + return '\n'.join(lines) + +def getfile(object): + """Work out which source or compiled file an object was defined in.""" + if ismodule(object): + if getattr(object, '__file__', None): + return object.__file__ + raise TypeError('{!r} is a built-in module'.format(object)) + if isclass(object): + if hasattr(object, '__module__'): + module = sys.modules.get(object.__module__) + if getattr(module, '__file__', None): + return module.__file__ + raise TypeError('{!r} is a built-in class'.format(object)) + if ismethod(object): + object = object.__func__ + if isfunction(object): + object = object.__code__ + if istraceback(object): + object = object.tb_frame + if isframe(object): + object = object.f_code + if iscode(object): + return object.co_filename + raise TypeError('module, class, method, function, traceback, frame, or ' + 'code object was expected, got {}'.format( + type(object).__name__)) + +def getmodulename(path): + """Return the module name for a given file, or None.""" + fname = os.path.basename(path) + # Check for paths that look like an actual module file + suffixes = [(-len(suffix), suffix) + for suffix in importlib.machinery.all_suffixes()] + suffixes.sort() # try longest suffixes first, in case they overlap + for neglen, suffix in suffixes: + if fname.endswith(suffix): + return fname[:neglen] + return None + +def getsourcefile(object): + """Return the filename that can be used to locate an object's source. + Return None if no way can be identified to get the source. + """ + filename = getfile(object) + all_bytecode_suffixes = importlib.machinery.DEBUG_BYTECODE_SUFFIXES[:] + all_bytecode_suffixes += importlib.machinery.OPTIMIZED_BYTECODE_SUFFIXES[:] + if any(filename.endswith(s) for s in all_bytecode_suffixes): + filename = (os.path.splitext(filename)[0] + + importlib.machinery.SOURCE_SUFFIXES[0]) + elif any(filename.endswith(s) for s in + importlib.machinery.EXTENSION_SUFFIXES): + return None + if os.path.exists(filename): + return filename + # only return a non-existent filename if the module has a PEP 302 loader + if getattr(getmodule(object, filename), '__loader__', None) is not None: + return filename + # or it is in the linecache + if filename in linecache.cache: + return filename + +def getabsfile(object, _filename=None): + """Return an absolute path to the source or compiled file for an object. + + The idea is for each object to have a unique origin, so this routine + normalizes the result as much as possible.""" + if _filename is None: + _filename = getsourcefile(object) or getfile(object) + return os.path.normcase(os.path.abspath(_filename)) + +modulesbyfile = {} +_filesbymodname = {} + +def getmodule(object, _filename=None): + """Return the module an object was defined in, or None if not found.""" + if ismodule(object): + return object + if hasattr(object, '__module__'): + return sys.modules.get(object.__module__) + # Try the filename to modulename cache + if _filename is not None and _filename in modulesbyfile: + return sys.modules.get(modulesbyfile[_filename]) + # Try the cache again with the absolute file name + try: + file = getabsfile(object, _filename) + except TypeError: + return None + if file in modulesbyfile: + return sys.modules.get(modulesbyfile[file]) + # Update the filename to module name cache and check yet again + # Copy sys.modules in order to cope with changes while iterating + for modname, module in sys.modules.copy().items(): + if ismodule(module) and hasattr(module, '__file__'): + f = module.__file__ + if f == _filesbymodname.get(modname, None): + # Have already mapped this module, so skip it + continue + _filesbymodname[modname] = f + f = getabsfile(module) + # Always map to the name the module knows itself by + modulesbyfile[f] = modulesbyfile[ + os.path.realpath(f)] = module.__name__ + if file in modulesbyfile: + return sys.modules.get(modulesbyfile[file]) + # Check the main module + main = sys.modules['__main__'] + if not hasattr(object, '__name__'): + return None + if hasattr(main, object.__name__): + mainobject = getattr(main, object.__name__) + if mainobject is object: + return main + # Check builtins + builtin = sys.modules['builtins'] + if hasattr(builtin, object.__name__): + builtinobject = getattr(builtin, object.__name__) + if builtinobject is object: + return builtin + +def findsource(object): + """Return the entire source file and starting line number for an object. + + The argument may be a module, class, method, function, traceback, frame, + or code object. The source code is returned as a list of all the lines + in the file and the line number indexes a line in that list. An OSError + is raised if the source code cannot be retrieved.""" + + file = getsourcefile(object) + if file: + # Invalidate cache if needed. + linecache.checkcache(file) + else: + file = getfile(object) + # Allow filenames in form of "" to pass through. + # `doctest` monkeypatches `linecache` module to enable + # inspection, so let `linecache.getlines` to be called. + if not (file.startswith('<') and file.endswith('>')): + raise OSError('source code not available') + + module = getmodule(object, file) + if module: + lines = linecache.getlines(file, module.__dict__) + else: + lines = linecache.getlines(file) + if not lines: + raise OSError('could not get source code') + + if ismodule(object): + return lines, 0 + + if isclass(object): + name = object.__name__ + pat = re.compile(r'^(\s*)class\s*' + name + r'\b') + # make some effort to find the best matching class definition: + # use the one with the least indentation, which is the one + # that's most probably not inside a function definition. + candidates = [] + for i in range(len(lines)): + match = pat.match(lines[i]) + if match: + # if it's at toplevel, it's already the best one + if lines[i][0] == 'c': + return lines, i + # else add whitespace to candidate list + candidates.append((match.group(1), i)) + if candidates: + # this will sort by whitespace, and by line number, + # less whitespace first + candidates.sort() + return lines, candidates[0][1] + else: + raise OSError('could not find class definition') + + if ismethod(object): + object = object.__func__ + if isfunction(object): + object = object.__code__ + if istraceback(object): + object = object.tb_frame + if isframe(object): + object = object.f_code + if iscode(object): + if not hasattr(object, 'co_firstlineno'): + raise OSError('could not find function definition') + lnum = object.co_firstlineno - 1 + pat = re.compile(r'^(\s*def\s)|(\s*async\s+def\s)|(.*(? 0: + try: + line = lines[lnum] + except IndexError: + raise OSError('lineno is out of bounds') + if pat.match(line): + break + lnum = lnum - 1 + return lines, lnum + raise OSError('could not find code object') + +def getcomments(object): + """Get lines of comments immediately preceding an object's source code. + + Returns None when source can't be found. + """ + try: + lines, lnum = findsource(object) + except (OSError, TypeError): + return None + + if ismodule(object): + # Look for a comment block at the top of the file. + start = 0 + if lines and lines[0][:2] == '#!': start = 1 + while start < len(lines) and lines[start].strip() in ('', '#'): + start = start + 1 + if start < len(lines) and lines[start][:1] == '#': + comments = [] + end = start + while end < len(lines) and lines[end][:1] == '#': + comments.append(lines[end].expandtabs()) + end = end + 1 + return ''.join(comments) + + # Look for a preceding block of comments at the same indentation. + elif lnum > 0: + indent = indentsize(lines[lnum]) + end = lnum - 1 + if end >= 0 and lines[end].lstrip()[:1] == '#' and \ + indentsize(lines[end]) == indent: + comments = [lines[end].expandtabs().lstrip()] + if end > 0: + end = end - 1 + comment = lines[end].expandtabs().lstrip() + while comment[:1] == '#' and indentsize(lines[end]) == indent: + comments[:0] = [comment] + end = end - 1 + if end < 0: break + comment = lines[end].expandtabs().lstrip() + while comments and comments[0].strip() == '#': + comments[:1] = [] + while comments and comments[-1].strip() == '#': + comments[-1:] = [] + return ''.join(comments) + +class EndOfBlock(Exception): pass + +class BlockFinder: + """Provide a tokeneater() method to detect the end of a code block.""" + def __init__(self): + self.indent = 0 + self.islambda = False + self.started = False + self.passline = False + self.indecorator = False + self.decoratorhasargs = False + self.last = 1 + self.body_col0 = None + + def tokeneater(self, type, token, srowcol, erowcol, line): + if not self.started and not self.indecorator: + # skip any decorators + if token == "@": + self.indecorator = True + # look for the first "def", "class" or "lambda" + elif token in ("def", "class", "lambda"): + if token == "lambda": + self.islambda = True + self.started = True + self.passline = True # skip to the end of the line + elif token == "(": + if self.indecorator: + self.decoratorhasargs = True + elif token == ")": + if self.indecorator: + self.indecorator = False + self.decoratorhasargs = False + elif type == tokenize.NEWLINE: + self.passline = False # stop skipping when a NEWLINE is seen + self.last = srowcol[0] + if self.islambda: # lambdas always end at the first NEWLINE + raise EndOfBlock + # hitting a NEWLINE when in a decorator without args + # ends the decorator + if self.indecorator and not self.decoratorhasargs: + self.indecorator = False + elif self.passline: + pass + elif type == tokenize.INDENT: + if self.body_col0 is None and self.started: + self.body_col0 = erowcol[1] + self.indent = self.indent + 1 + self.passline = True + elif type == tokenize.DEDENT: + self.indent = self.indent - 1 + # the end of matching indent/dedent pairs end a block + # (note that this only works for "def"/"class" blocks, + # not e.g. for "if: else:" or "try: finally:" blocks) + if self.indent <= 0: + raise EndOfBlock + elif type == tokenize.COMMENT: + if self.body_col0 is not None and srowcol[1] >= self.body_col0: + # Include comments if indented at least as much as the block + self.last = srowcol[0] + elif self.indent == 0 and type not in (tokenize.COMMENT, tokenize.NL): + # any other token on the same indentation level end the previous + # block as well, except the pseudo-tokens COMMENT and NL. + raise EndOfBlock + +def getblock(lines): + """Extract the block of code at the top of the given list of lines.""" + blockfinder = BlockFinder() + try: + tokens = tokenize.generate_tokens(iter(lines).__next__) + for _token in tokens: + blockfinder.tokeneater(*_token) + except (EndOfBlock, IndentationError): + pass + return lines[:blockfinder.last] + +def getsourcelines(object): + """Return a list of source lines and starting line number for an object. + + The argument may be a module, class, method, function, traceback, frame, + or code object. The source code is returned as a list of the lines + corresponding to the object and the line number indicates where in the + original source file the first line of code was found. An OSError is + raised if the source code cannot be retrieved.""" + object = unwrap(object) + lines, lnum = findsource(object) + + if istraceback(object): + object = object.tb_frame + + # for module or frame that corresponds to module, return all source lines + if (ismodule(object) or + (isframe(object) and object.f_code.co_name == "")): + return lines, 0 + else: + return getblock(lines[lnum:]), lnum + 1 + +def getsource(object): + """Return the text of the source code for an object. + + The argument may be a module, class, method, function, traceback, frame, + or code object. The source code is returned as a single string. An + OSError is raised if the source code cannot be retrieved.""" + lines, lnum = getsourcelines(object) + return ''.join(lines) + +# --------------------------------------------------- class tree extraction +def walktree(classes, children, parent): + """Recursive helper function for getclasstree().""" + results = [] + classes.sort(key=attrgetter('__module__', '__name__')) + for c in classes: + results.append((c, c.__bases__)) + if c in children: + results.append(walktree(children[c], children, c)) + return results + +def getclasstree(classes, unique=False): + """Arrange the given list of classes into a hierarchy of nested lists. + + Where a nested list appears, it contains classes derived from the class + whose entry immediately precedes the list. Each entry is a 2-tuple + containing a class and a tuple of its base classes. If the 'unique' + argument is true, exactly one entry appears in the returned structure + for each class in the given list. Otherwise, classes using multiple + inheritance and their descendants will appear multiple times.""" + children = {} + roots = [] + for c in classes: + if c.__bases__: + for parent in c.__bases__: + if parent not in children: + children[parent] = [] + if c not in children[parent]: + children[parent].append(c) + if unique and parent in classes: break + elif c not in roots: + roots.append(c) + for parent in children: + if parent not in classes: + roots.append(parent) + return walktree(roots, children, None) + +# ------------------------------------------------ argument list extraction +Arguments = namedtuple('Arguments', 'args, varargs, varkw') + +def getargs(co): + """Get information about the arguments accepted by a code object. + + Three things are returned: (args, varargs, varkw), where + 'args' is the list of argument names. Keyword-only arguments are + appended. 'varargs' and 'varkw' are the names of the * and ** + arguments or None.""" + if not iscode(co): + raise TypeError('{!r} is not a code object'.format(co)) + + names = co.co_varnames + nargs = co.co_argcount + nkwargs = co.co_kwonlyargcount + args = list(names[:nargs]) + kwonlyargs = list(names[nargs:nargs+nkwargs]) + step = 0 + + nargs += nkwargs + varargs = None + if co.co_flags & CO_VARARGS: + varargs = co.co_varnames[nargs] + nargs = nargs + 1 + varkw = None + if co.co_flags & CO_VARKEYWORDS: + varkw = co.co_varnames[nargs] + return Arguments(args + kwonlyargs, varargs, varkw) + +ArgSpec = namedtuple('ArgSpec', 'args varargs keywords defaults') + +def getargspec(func): + """Get the names and default values of a function's parameters. + + A tuple of four things is returned: (args, varargs, keywords, defaults). + 'args' is a list of the argument names, including keyword-only argument names. + 'varargs' and 'keywords' are the names of the * and ** parameters or None. + 'defaults' is an n-tuple of the default values of the last n parameters. + + This function is deprecated, as it does not support annotations or + keyword-only parameters and will raise ValueError if either is present + on the supplied callable. + + For a more structured introspection API, use inspect.signature() instead. + + Alternatively, use getfullargspec() for an API with a similar namedtuple + based interface, but full support for annotations and keyword-only + parameters. + + Deprecated since Python 3.5, use `inspect.getfullargspec()`. + """ + warnings.warn("inspect.getargspec() is deprecated since Python 3.0, " + "use inspect.signature() or inspect.getfullargspec()", + DeprecationWarning, stacklevel=2) + args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults, ann = \ + getfullargspec(func) + if kwonlyargs or ann: + raise ValueError("Function has keyword-only parameters or annotations" + ", use inspect.signature() API which can support them") + return ArgSpec(args, varargs, varkw, defaults) + +FullArgSpec = namedtuple('FullArgSpec', + 'args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults, annotations') + +def getfullargspec(func): + """Get the names and default values of a callable object's parameters. + + A tuple of seven things is returned: + (args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults, annotations). + 'args' is a list of the parameter names. + 'varargs' and 'varkw' are the names of the * and ** parameters or None. + 'defaults' is an n-tuple of the default values of the last n parameters. + 'kwonlyargs' is a list of keyword-only parameter names. + 'kwonlydefaults' is a dictionary mapping names from kwonlyargs to defaults. + 'annotations' is a dictionary mapping parameter names to annotations. + + Notable differences from inspect.signature(): + - the "self" parameter is always reported, even for bound methods + - wrapper chains defined by __wrapped__ *not* unwrapped automatically + """ + try: + # Re: `skip_bound_arg=False` + # + # There is a notable difference in behaviour between getfullargspec + # and Signature: the former always returns 'self' parameter for bound + # methods, whereas the Signature always shows the actual calling + # signature of the passed object. + # + # To simulate this behaviour, we "unbind" bound methods, to trick + # inspect.signature to always return their first parameter ("self", + # usually) + + # Re: `follow_wrapper_chains=False` + # + # getfullargspec() historically ignored __wrapped__ attributes, + # so we ensure that remains the case in 3.3+ + + sig = _signature_from_callable(func, + follow_wrapper_chains=False, + skip_bound_arg=False, + sigcls=Signature) + except Exception as ex: + # Most of the times 'signature' will raise ValueError. + # But, it can also raise AttributeError, and, maybe something + # else. So to be fully backwards compatible, we catch all + # possible exceptions here, and reraise a TypeError. + raise TypeError('unsupported callable') from ex + + args = [] + varargs = None + varkw = None + posonlyargs = [] + kwonlyargs = [] + defaults = () + annotations = {} + defaults = () + kwdefaults = {} + + if sig.return_annotation is not sig.empty: + annotations['return'] = sig.return_annotation + + for param in sig.parameters.values(): + kind = param.kind + name = param.name + + if kind is _POSITIONAL_ONLY: + posonlyargs.append(name) + if param.default is not param.empty: + defaults += (param.default,) + elif kind is _POSITIONAL_OR_KEYWORD: + args.append(name) + if param.default is not param.empty: + defaults += (param.default,) + elif kind is _VAR_POSITIONAL: + varargs = name + elif kind is _KEYWORD_ONLY: + kwonlyargs.append(name) + if param.default is not param.empty: + kwdefaults[name] = param.default + elif kind is _VAR_KEYWORD: + varkw = name + + if param.annotation is not param.empty: + annotations[name] = param.annotation + + if not kwdefaults: + # compatibility with 'func.__kwdefaults__' + kwdefaults = None + + if not defaults: + # compatibility with 'func.__defaults__' + defaults = None + + return FullArgSpec(posonlyargs + args, varargs, varkw, defaults, + kwonlyargs, kwdefaults, annotations) + + +ArgInfo = namedtuple('ArgInfo', 'args varargs keywords locals') + +def getargvalues(frame): + """Get information about arguments passed into a particular frame. + + A tuple of four things is returned: (args, varargs, varkw, locals). + 'args' is a list of the argument names. + 'varargs' and 'varkw' are the names of the * and ** arguments or None. + 'locals' is the locals dictionary of the given frame.""" + args, varargs, varkw = getargs(frame.f_code) + return ArgInfo(args, varargs, varkw, frame.f_locals) + +def formatannotation(annotation, base_module=None): + if getattr(annotation, '__module__', None) == 'typing': + return repr(annotation).replace('typing.', '') + if isinstance(annotation, type): + if annotation.__module__ in ('builtins', base_module): + return annotation.__qualname__ + return annotation.__module__+'.'+annotation.__qualname__ + return repr(annotation) + +def formatannotationrelativeto(object): + module = getattr(object, '__module__', None) + def _formatannotation(annotation): + return formatannotation(annotation, module) + return _formatannotation + +def formatargspec(args, varargs=None, varkw=None, defaults=None, + kwonlyargs=(), kwonlydefaults={}, annotations={}, + formatarg=str, + formatvarargs=lambda name: '*' + name, + formatvarkw=lambda name: '**' + name, + formatvalue=lambda value: '=' + repr(value), + formatreturns=lambda text: ' -> ' + text, + formatannotation=formatannotation): + """Format an argument spec from the values returned by getfullargspec. + + The first seven arguments are (args, varargs, varkw, defaults, + kwonlyargs, kwonlydefaults, annotations). The other five arguments + are the corresponding optional formatting functions that are called to + turn names and values into strings. The last argument is an optional + function to format the sequence of arguments. + + Deprecated since Python 3.5: use the `signature` function and `Signature` + objects. + """ + + from warnings import warn + + warn("`formatargspec` is deprecated since Python 3.5. Use `signature` and " + "the `Signature` object directly", + DeprecationWarning, + stacklevel=2) + + def formatargandannotation(arg): + result = formatarg(arg) + if arg in annotations: + result += ': ' + formatannotation(annotations[arg]) + return result + specs = [] + if defaults: + firstdefault = len(args) - len(defaults) + for i, arg in enumerate(args): + spec = formatargandannotation(arg) + if defaults and i >= firstdefault: + spec = spec + formatvalue(defaults[i - firstdefault]) + specs.append(spec) + if varargs is not None: + specs.append(formatvarargs(formatargandannotation(varargs))) + else: + if kwonlyargs: + specs.append('*') + if kwonlyargs: + for kwonlyarg in kwonlyargs: + spec = formatargandannotation(kwonlyarg) + if kwonlydefaults and kwonlyarg in kwonlydefaults: + spec += formatvalue(kwonlydefaults[kwonlyarg]) + specs.append(spec) + if varkw is not None: + specs.append(formatvarkw(formatargandannotation(varkw))) + result = '(' + ', '.join(specs) + ')' + if 'return' in annotations: + result += formatreturns(formatannotation(annotations['return'])) + return result + +def formatargvalues(args, varargs, varkw, locals, + formatarg=str, + formatvarargs=lambda name: '*' + name, + formatvarkw=lambda name: '**' + name, + formatvalue=lambda value: '=' + repr(value)): + """Format an argument spec from the 4 values returned by getargvalues. + + The first four arguments are (args, varargs, varkw, locals). The + next four arguments are the corresponding optional formatting functions + that are called to turn names and values into strings. The ninth + argument is an optional function to format the sequence of arguments.""" + def convert(name, locals=locals, + formatarg=formatarg, formatvalue=formatvalue): + return formatarg(name) + formatvalue(locals[name]) + specs = [] + for i in range(len(args)): + specs.append(convert(args[i])) + if varargs: + specs.append(formatvarargs(varargs) + formatvalue(locals[varargs])) + if varkw: + specs.append(formatvarkw(varkw) + formatvalue(locals[varkw])) + return '(' + ', '.join(specs) + ')' + +def _missing_arguments(f_name, argnames, pos, values): + names = [repr(name) for name in argnames if name not in values] + missing = len(names) + if missing == 1: + s = names[0] + elif missing == 2: + s = "{} and {}".format(*names) + else: + tail = ", {} and {}".format(*names[-2:]) + del names[-2:] + s = ", ".join(names) + tail + raise TypeError("%s() missing %i required %s argument%s: %s" % + (f_name, missing, + "positional" if pos else "keyword-only", + "" if missing == 1 else "s", s)) + +def _too_many(f_name, args, kwonly, varargs, defcount, given, values): + atleast = len(args) - defcount + kwonly_given = len([arg for arg in kwonly if arg in values]) + if varargs: + plural = atleast != 1 + sig = "at least %d" % (atleast,) + elif defcount: + plural = True + sig = "from %d to %d" % (atleast, len(args)) + else: + plural = len(args) != 1 + sig = str(len(args)) + kwonly_sig = "" + if kwonly_given: + msg = " positional argument%s (and %d keyword-only argument%s)" + kwonly_sig = (msg % ("s" if given != 1 else "", kwonly_given, + "s" if kwonly_given != 1 else "")) + raise TypeError("%s() takes %s positional argument%s but %d%s %s given" % + (f_name, sig, "s" if plural else "", given, kwonly_sig, + "was" if given == 1 and not kwonly_given else "were")) + +def getcallargs(func, /, *positional, **named): + """Get the mapping of arguments to values. + + A dict is returned, with keys the function argument names (including the + names of the * and ** arguments, if any), and values the respective bound + values from 'positional' and 'named'.""" + spec = getfullargspec(func) + args, varargs, varkw, defaults, kwonlyargs, kwonlydefaults, ann = spec + f_name = func.__name__ + arg2value = {} + + + if ismethod(func) and func.__self__ is not None: + # implicit 'self' (or 'cls' for classmethods) argument + positional = (func.__self__,) + positional + num_pos = len(positional) + num_args = len(args) + num_defaults = len(defaults) if defaults else 0 + + n = min(num_pos, num_args) + for i in range(n): + arg2value[args[i]] = positional[i] + if varargs: + arg2value[varargs] = tuple(positional[n:]) + possible_kwargs = set(args + kwonlyargs) + if varkw: + arg2value[varkw] = {} + for kw, value in named.items(): + if kw not in possible_kwargs: + if not varkw: + raise TypeError("%s() got an unexpected keyword argument %r" % + (f_name, kw)) + arg2value[varkw][kw] = value + continue + if kw in arg2value: + raise TypeError("%s() got multiple values for argument %r" % + (f_name, kw)) + arg2value[kw] = value + if num_pos > num_args and not varargs: + _too_many(f_name, args, kwonlyargs, varargs, num_defaults, + num_pos, arg2value) + if num_pos < num_args: + req = args[:num_args - num_defaults] + for arg in req: + if arg not in arg2value: + _missing_arguments(f_name, req, True, arg2value) + for i, arg in enumerate(args[num_args - num_defaults:]): + if arg not in arg2value: + arg2value[arg] = defaults[i] + missing = 0 + for kwarg in kwonlyargs: + if kwarg not in arg2value: + if kwonlydefaults and kwarg in kwonlydefaults: + arg2value[kwarg] = kwonlydefaults[kwarg] + else: + missing += 1 + if missing: + _missing_arguments(f_name, kwonlyargs, False, arg2value) + return arg2value + +ClosureVars = namedtuple('ClosureVars', 'nonlocals globals builtins unbound') + +def getclosurevars(func): + """ + Get the mapping of free variables to their current values. + + Returns a named tuple of dicts mapping the current nonlocal, global + and builtin references as seen by the body of the function. A final + set of unbound names that could not be resolved is also provided. + """ + + if ismethod(func): + func = func.__func__ + + if not isfunction(func): + raise TypeError("{!r} is not a Python function".format(func)) + + code = func.__code__ + # Nonlocal references are named in co_freevars and resolved + # by looking them up in __closure__ by positional index + if func.__closure__ is None: + nonlocal_vars = {} + else: + nonlocal_vars = { + var : cell.cell_contents + for var, cell in zip(code.co_freevars, func.__closure__) + } + + # Global and builtin references are named in co_names and resolved + # by looking them up in __globals__ or __builtins__ + global_ns = func.__globals__ + builtin_ns = global_ns.get("__builtins__", builtins.__dict__) + if ismodule(builtin_ns): + builtin_ns = builtin_ns.__dict__ + global_vars = {} + builtin_vars = {} + unbound_names = set() + for name in code.co_names: + if name in ("None", "True", "False"): + # Because these used to be builtins instead of keywords, they + # may still show up as name references. We ignore them. + continue + try: + global_vars[name] = global_ns[name] + except KeyError: + try: + builtin_vars[name] = builtin_ns[name] + except KeyError: + unbound_names.add(name) + + return ClosureVars(nonlocal_vars, global_vars, + builtin_vars, unbound_names) + +# -------------------------------------------------- stack frame extraction + +Traceback = namedtuple('Traceback', 'filename lineno function code_context index') + +def getframeinfo(frame, context=1): + """Get information about a frame or traceback object. + + A tuple of five things is returned: the filename, the line number of + the current line, the function name, a list of lines of context from + the source code, and the index of the current line within that list. + The optional second argument specifies the number of lines of context + to return, which are centered around the current line.""" + if istraceback(frame): + lineno = frame.tb_lineno + frame = frame.tb_frame + else: + lineno = frame.f_lineno + if not isframe(frame): + raise TypeError('{!r} is not a frame or traceback object'.format(frame)) + + filename = getsourcefile(frame) or getfile(frame) + if context > 0: + start = lineno - 1 - context//2 + try: + lines, lnum = findsource(frame) + except OSError: + lines = index = None + else: + start = max(0, min(start, len(lines) - context)) + lines = lines[start:start+context] + index = lineno - 1 - start + else: + lines = index = None + + return Traceback(filename, lineno, frame.f_code.co_name, lines, index) + +def getlineno(frame): + """Get the line number from a frame object, allowing for optimization.""" + # FrameType.f_lineno is now a descriptor that grovels co_lnotab + return frame.f_lineno + +FrameInfo = namedtuple('FrameInfo', ('frame',) + Traceback._fields) + +def getouterframes(frame, context=1): + """Get a list of records for a frame and all higher (calling) frames. + + Each record contains a frame object, filename, line number, function + name, a list of lines of context, and index within the context.""" + framelist = [] + while frame: + frameinfo = (frame,) + getframeinfo(frame, context) + framelist.append(FrameInfo(*frameinfo)) + frame = frame.f_back + return framelist + +def getinnerframes(tb, context=1): + """Get a list of records for a traceback's frame and all lower frames. + + Each record contains a frame object, filename, line number, function + name, a list of lines of context, and index within the context.""" + framelist = [] + while tb: + frameinfo = (tb.tb_frame,) + getframeinfo(tb, context) + framelist.append(FrameInfo(*frameinfo)) + tb = tb.tb_next + return framelist + +def currentframe(): + """Return the frame of the caller or None if this is not possible.""" + return sys._getframe(1) if hasattr(sys, "_getframe") else None + +def stack(context=1): + """Return a list of records for the stack above the caller's frame.""" + return getouterframes(sys._getframe(1), context) + +def trace(context=1): + """Return a list of records for the stack below the current exception.""" + return getinnerframes(sys.exc_info()[2], context) + + +# ------------------------------------------------ static version of getattr + +_sentinel = object() + +def _static_getmro(klass): + return type.__dict__['__mro__'].__get__(klass) + +def _check_instance(obj, attr): + instance_dict = {} + try: + instance_dict = object.__getattribute__(obj, "__dict__") + except AttributeError: + pass + return dict.get(instance_dict, attr, _sentinel) + + +def _check_class(klass, attr): + for entry in _static_getmro(klass): + if _shadowed_dict(type(entry)) is _sentinel: + try: + return entry.__dict__[attr] + except KeyError: + pass + return _sentinel + +def _is_type(obj): + try: + _static_getmro(obj) + except TypeError: + return False + return True + +def _shadowed_dict(klass): + dict_attr = type.__dict__["__dict__"] + for entry in _static_getmro(klass): + try: + class_dict = dict_attr.__get__(entry)["__dict__"] + except KeyError: + pass + else: + if not (type(class_dict) is types.GetSetDescriptorType and + class_dict.__name__ == "__dict__" and + class_dict.__objclass__ is entry): + return class_dict + return _sentinel + +def getattr_static(obj, attr, default=_sentinel): + """Retrieve attributes without triggering dynamic lookup via the + descriptor protocol, __getattr__ or __getattribute__. + + Note: this function may not be able to retrieve all attributes + that getattr can fetch (like dynamically created attributes) + and may find attributes that getattr can't (like descriptors + that raise AttributeError). It can also return descriptor objects + instead of instance members in some cases. See the + documentation for details. + """ + instance_result = _sentinel + if not _is_type(obj): + klass = type(obj) + dict_attr = _shadowed_dict(klass) + if (dict_attr is _sentinel or + type(dict_attr) is types.MemberDescriptorType): + instance_result = _check_instance(obj, attr) + else: + klass = obj + + klass_result = _check_class(klass, attr) + + if instance_result is not _sentinel and klass_result is not _sentinel: + if (_check_class(type(klass_result), '__get__') is not _sentinel and + _check_class(type(klass_result), '__set__') is not _sentinel): + return klass_result + + if instance_result is not _sentinel: + return instance_result + if klass_result is not _sentinel: + return klass_result + + if obj is klass: + # for types we check the metaclass too + for entry in _static_getmro(type(klass)): + if _shadowed_dict(type(entry)) is _sentinel: + try: + return entry.__dict__[attr] + except KeyError: + pass + if default is not _sentinel: + return default + raise AttributeError(attr) + + +# ------------------------------------------------ generator introspection + +GEN_CREATED = 'GEN_CREATED' +GEN_RUNNING = 'GEN_RUNNING' +GEN_SUSPENDED = 'GEN_SUSPENDED' +GEN_CLOSED = 'GEN_CLOSED' + +def getgeneratorstate(generator): + """Get current state of a generator-iterator. + + Possible states are: + GEN_CREATED: Waiting to start execution. + GEN_RUNNING: Currently being executed by the interpreter. + GEN_SUSPENDED: Currently suspended at a yield expression. + GEN_CLOSED: Execution has completed. + """ + if generator.gi_running: + return GEN_RUNNING + if generator.gi_frame is None: + return GEN_CLOSED + if generator.gi_frame.f_lasti == -1: + return GEN_CREATED + return GEN_SUSPENDED + + +def getgeneratorlocals(generator): + """ + Get the mapping of generator local variables to their current values. + + A dict is returned, with the keys the local variable names and values the + bound values.""" + + if not isgenerator(generator): + raise TypeError("{!r} is not a Python generator".format(generator)) + + frame = getattr(generator, "gi_frame", None) + if frame is not None: + return generator.gi_frame.f_locals + else: + return {} + + +# ------------------------------------------------ coroutine introspection + +CORO_CREATED = 'CORO_CREATED' +CORO_RUNNING = 'CORO_RUNNING' +CORO_SUSPENDED = 'CORO_SUSPENDED' +CORO_CLOSED = 'CORO_CLOSED' + +def getcoroutinestate(coroutine): + """Get current state of a coroutine object. + + Possible states are: + CORO_CREATED: Waiting to start execution. + CORO_RUNNING: Currently being executed by the interpreter. + CORO_SUSPENDED: Currently suspended at an await expression. + CORO_CLOSED: Execution has completed. + """ + if coroutine.cr_running: + return CORO_RUNNING + if coroutine.cr_frame is None: + return CORO_CLOSED + if coroutine.cr_frame.f_lasti == -1: + return CORO_CREATED + return CORO_SUSPENDED + + +def getcoroutinelocals(coroutine): + """ + Get the mapping of coroutine local variables to their current values. + + A dict is returned, with the keys the local variable names and values the + bound values.""" + frame = getattr(coroutine, "cr_frame", None) + if frame is not None: + return frame.f_locals + else: + return {} + + +############################################################################### +### Function Signature Object (PEP 362) +############################################################################### + + +_WrapperDescriptor = type(type.__call__) +_MethodWrapper = type(all.__call__) +_ClassMethodWrapper = type(int.__dict__['from_bytes']) + +_NonUserDefinedCallables = (_WrapperDescriptor, + _MethodWrapper, + _ClassMethodWrapper, + types.BuiltinFunctionType) + + +def _signature_get_user_defined_method(cls, method_name): + """Private helper. Checks if ``cls`` has an attribute + named ``method_name`` and returns it only if it is a + pure python function. + """ + try: + meth = getattr(cls, method_name) + except AttributeError: + return + else: + if not isinstance(meth, _NonUserDefinedCallables): + # Once '__signature__' will be added to 'C'-level + # callables, this check won't be necessary + return meth + + +def _signature_get_partial(wrapped_sig, partial, extra_args=()): + """Private helper to calculate how 'wrapped_sig' signature will + look like after applying a 'functools.partial' object (or alike) + on it. + """ + + old_params = wrapped_sig.parameters + new_params = OrderedDict(old_params.items()) + + partial_args = partial.args or () + partial_keywords = partial.keywords or {} + + if extra_args: + partial_args = extra_args + partial_args + + try: + ba = wrapped_sig.bind_partial(*partial_args, **partial_keywords) + except TypeError as ex: + msg = 'partial object {!r} has incorrect arguments'.format(partial) + raise ValueError(msg) from ex + + + transform_to_kwonly = False + for param_name, param in old_params.items(): + try: + arg_value = ba.arguments[param_name] + except KeyError: + pass + else: + if param.kind is _POSITIONAL_ONLY: + # If positional-only parameter is bound by partial, + # it effectively disappears from the signature + new_params.pop(param_name) + continue + + if param.kind is _POSITIONAL_OR_KEYWORD: + if param_name in partial_keywords: + # This means that this parameter, and all parameters + # after it should be keyword-only (and var-positional + # should be removed). Here's why. Consider the following + # function: + # foo(a, b, *args, c): + # pass + # + # "partial(foo, a='spam')" will have the following + # signature: "(*, a='spam', b, c)". Because attempting + # to call that partial with "(10, 20)" arguments will + # raise a TypeError, saying that "a" argument received + # multiple values. + transform_to_kwonly = True + # Set the new default value + new_params[param_name] = param.replace(default=arg_value) + else: + # was passed as a positional argument + new_params.pop(param.name) + continue + + if param.kind is _KEYWORD_ONLY: + # Set the new default value + new_params[param_name] = param.replace(default=arg_value) + + if transform_to_kwonly: + assert param.kind is not _POSITIONAL_ONLY + + if param.kind is _POSITIONAL_OR_KEYWORD: + new_param = new_params[param_name].replace(kind=_KEYWORD_ONLY) + new_params[param_name] = new_param + new_params.move_to_end(param_name) + elif param.kind in (_KEYWORD_ONLY, _VAR_KEYWORD): + new_params.move_to_end(param_name) + elif param.kind is _VAR_POSITIONAL: + new_params.pop(param.name) + + return wrapped_sig.replace(parameters=new_params.values()) + + +def _signature_bound_method(sig): + """Private helper to transform signatures for unbound + functions to bound methods. + """ + + params = tuple(sig.parameters.values()) + + if not params or params[0].kind in (_VAR_KEYWORD, _KEYWORD_ONLY): + raise ValueError('invalid method signature') + + kind = params[0].kind + if kind in (_POSITIONAL_OR_KEYWORD, _POSITIONAL_ONLY): + # Drop first parameter: + # '(p1, p2[, ...])' -> '(p2[, ...])' + params = params[1:] + else: + if kind is not _VAR_POSITIONAL: + # Unless we add a new parameter type we never + # get here + raise ValueError('invalid argument type') + # It's a var-positional parameter. + # Do nothing. '(*args[, ...])' -> '(*args[, ...])' + + return sig.replace(parameters=params) + + +def _signature_is_builtin(obj): + """Private helper to test if `obj` is a callable that might + support Argument Clinic's __text_signature__ protocol. + """ + return (isbuiltin(obj) or + ismethoddescriptor(obj) or + isinstance(obj, _NonUserDefinedCallables) or + # Can't test 'isinstance(type)' here, as it would + # also be True for regular python classes + obj in (type, object)) + + +def _signature_is_functionlike(obj): + """Private helper to test if `obj` is a duck type of FunctionType. + A good example of such objects are functions compiled with + Cython, which have all attributes that a pure Python function + would have, but have their code statically compiled. + """ + + if not callable(obj) or isclass(obj): + # All function-like objects are obviously callables, + # and not classes. + return False + + name = getattr(obj, '__name__', None) + code = getattr(obj, '__code__', None) + defaults = getattr(obj, '__defaults__', _void) # Important to use _void ... + kwdefaults = getattr(obj, '__kwdefaults__', _void) # ... and not None here + annotations = getattr(obj, '__annotations__', None) + + return (isinstance(code, types.CodeType) and + isinstance(name, str) and + (defaults is None or isinstance(defaults, tuple)) and + (kwdefaults is None or isinstance(kwdefaults, dict)) and + isinstance(annotations, dict)) + + +def _signature_get_bound_param(spec): + """ Private helper to get first parameter name from a + __text_signature__ of a builtin method, which should + be in the following format: '($param1, ...)'. + Assumptions are that the first argument won't have + a default value or an annotation. + """ + + assert spec.startswith('($') + + pos = spec.find(',') + if pos == -1: + pos = spec.find(')') + + cpos = spec.find(':') + assert cpos == -1 or cpos > pos + + cpos = spec.find('=') + assert cpos == -1 or cpos > pos + + return spec[2:pos] + + +def _signature_strip_non_python_syntax(signature): + """ + Private helper function. Takes a signature in Argument Clinic's + extended signature format. + + Returns a tuple of three things: + * that signature re-rendered in standard Python syntax, + * the index of the "self" parameter (generally 0), or None if + the function does not have a "self" parameter, and + * the index of the last "positional only" parameter, + or None if the signature has no positional-only parameters. + """ + + if not signature: + return signature, None, None + + self_parameter = None + last_positional_only = None + + lines = [l.encode('ascii') for l in signature.split('\n')] + generator = iter(lines).__next__ + token_stream = tokenize.tokenize(generator) + + delayed_comma = False + skip_next_comma = False + text = [] + add = text.append + + current_parameter = 0 + OP = token.OP + ERRORTOKEN = token.ERRORTOKEN + + # token stream always starts with ENCODING token, skip it + t = next(token_stream) + assert t.type == tokenize.ENCODING + + for t in token_stream: + type, string = t.type, t.string + + if type == OP: + if string == ',': + if skip_next_comma: + skip_next_comma = False + else: + assert not delayed_comma + delayed_comma = True + current_parameter += 1 + continue + + if string == '/': + assert not skip_next_comma + assert last_positional_only is None + skip_next_comma = True + last_positional_only = current_parameter - 1 + continue + + if (type == ERRORTOKEN) and (string == '$'): + assert self_parameter is None + self_parameter = current_parameter + continue + + if delayed_comma: + delayed_comma = False + if not ((type == OP) and (string == ')')): + add(', ') + add(string) + if (string == ','): + add(' ') + clean_signature = ''.join(text) + return clean_signature, self_parameter, last_positional_only + + +def _signature_fromstr(cls, obj, s, skip_bound_arg=True): + """Private helper to parse content of '__text_signature__' + and return a Signature based on it. + """ + # Lazy import ast because it's relatively heavy and + # it's not used for other than this function. + import ast + + Parameter = cls._parameter_cls + + clean_signature, self_parameter, last_positional_only = \ + _signature_strip_non_python_syntax(s) + + program = "def foo" + clean_signature + ": pass" + + try: + module = ast.parse(program) + except SyntaxError: + module = None + + if not isinstance(module, ast.Module): + raise ValueError("{!r} builtin has invalid signature".format(obj)) + + f = module.body[0] + + parameters = [] + empty = Parameter.empty + invalid = object() + + module = None + module_dict = {} + module_name = getattr(obj, '__module__', None) + if module_name: + module = sys.modules.get(module_name, None) + if module: + module_dict = module.__dict__ + sys_module_dict = sys.modules.copy() + + def parse_name(node): + assert isinstance(node, ast.arg) + if node.annotation is not None: + raise ValueError("Annotations are not currently supported") + return node.arg + + def wrap_value(s): + try: + value = eval(s, module_dict) + except NameError: + try: + value = eval(s, sys_module_dict) + except NameError: + raise RuntimeError() + + if isinstance(value, (str, int, float, bytes, bool, type(None))): + return ast.Constant(value) + raise RuntimeError() + + class RewriteSymbolics(ast.NodeTransformer): + def visit_Attribute(self, node): + a = [] + n = node + while isinstance(n, ast.Attribute): + a.append(n.attr) + n = n.value + if not isinstance(n, ast.Name): + raise RuntimeError() + a.append(n.id) + value = ".".join(reversed(a)) + return wrap_value(value) + + def visit_Name(self, node): + if not isinstance(node.ctx, ast.Load): + raise ValueError() + return wrap_value(node.id) + + def p(name_node, default_node, default=empty): + name = parse_name(name_node) + if name is invalid: + return None + if default_node and default_node is not _empty: + try: + default_node = RewriteSymbolics().visit(default_node) + o = ast.literal_eval(default_node) + except ValueError: + o = invalid + if o is invalid: + return None + default = o if o is not invalid else default + parameters.append(Parameter(name, kind, default=default, annotation=empty)) + + # non-keyword-only parameters + args = reversed(f.args.args) + defaults = reversed(f.args.defaults) + iter = itertools.zip_longest(args, defaults, fillvalue=None) + if last_positional_only is not None: + kind = Parameter.POSITIONAL_ONLY + else: + kind = Parameter.POSITIONAL_OR_KEYWORD + for i, (name, default) in enumerate(reversed(list(iter))): + p(name, default) + if i == last_positional_only: + kind = Parameter.POSITIONAL_OR_KEYWORD + + # *args + if f.args.vararg: + kind = Parameter.VAR_POSITIONAL + p(f.args.vararg, empty) + + # keyword-only arguments + kind = Parameter.KEYWORD_ONLY + for name, default in zip(f.args.kwonlyargs, f.args.kw_defaults): + p(name, default) + + # **kwargs + if f.args.kwarg: + kind = Parameter.VAR_KEYWORD + p(f.args.kwarg, empty) + + if self_parameter is not None: + # Possibly strip the bound argument: + # - We *always* strip first bound argument if + # it is a module. + # - We don't strip first bound argument if + # skip_bound_arg is False. + assert parameters + _self = getattr(obj, '__self__', None) + self_isbound = _self is not None + self_ismodule = ismodule(_self) + if self_isbound and (self_ismodule or skip_bound_arg): + parameters.pop(0) + else: + # for builtins, self parameter is always positional-only! + p = parameters[0].replace(kind=Parameter.POSITIONAL_ONLY) + parameters[0] = p + + return cls(parameters, return_annotation=cls.empty) + + +def _signature_from_builtin(cls, func, skip_bound_arg=True): + """Private helper function to get signature for + builtin callables. + """ + + if not _signature_is_builtin(func): + raise TypeError("{!r} is not a Python builtin " + "function".format(func)) + + s = getattr(func, "__text_signature__", None) + if not s: + raise ValueError("no signature found for builtin {!r}".format(func)) + + return _signature_fromstr(cls, func, s, skip_bound_arg) + + +def _signature_from_function(cls, func, skip_bound_arg=True): + """Private helper: constructs Signature for the given python function.""" + + is_duck_function = False + if not isfunction(func): + if _signature_is_functionlike(func): + is_duck_function = True + else: + # If it's not a pure Python function, and not a duck type + # of pure function: + raise TypeError('{!r} is not a Python function'.format(func)) + + s = getattr(func, "__text_signature__", None) + if s: + return _signature_fromstr(cls, func, s, skip_bound_arg) + + Parameter = cls._parameter_cls + + # Parameter information. + func_code = func.__code__ + pos_count = func_code.co_argcount + arg_names = func_code.co_varnames + posonly_count = func_code.co_posonlyargcount + positional = arg_names[:pos_count] + keyword_only_count = func_code.co_kwonlyargcount + keyword_only = arg_names[pos_count:pos_count + keyword_only_count] + annotations = func.__annotations__ + defaults = func.__defaults__ + kwdefaults = func.__kwdefaults__ + + if defaults: + pos_default_count = len(defaults) + else: + pos_default_count = 0 + + parameters = [] + + non_default_count = pos_count - pos_default_count + posonly_left = posonly_count + + # Non-keyword-only parameters w/o defaults. + for name in positional[:non_default_count]: + kind = _POSITIONAL_ONLY if posonly_left else _POSITIONAL_OR_KEYWORD + annotation = annotations.get(name, _empty) + parameters.append(Parameter(name, annotation=annotation, + kind=kind)) + if posonly_left: + posonly_left -= 1 + + # ... w/ defaults. + for offset, name in enumerate(positional[non_default_count:]): + kind = _POSITIONAL_ONLY if posonly_left else _POSITIONAL_OR_KEYWORD + annotation = annotations.get(name, _empty) + parameters.append(Parameter(name, annotation=annotation, + kind=kind, + default=defaults[offset])) + if posonly_left: + posonly_left -= 1 + + # *args + if func_code.co_flags & CO_VARARGS: + name = arg_names[pos_count + keyword_only_count] + annotation = annotations.get(name, _empty) + parameters.append(Parameter(name, annotation=annotation, + kind=_VAR_POSITIONAL)) + + # Keyword-only parameters. + for name in keyword_only: + default = _empty + if kwdefaults is not None: + default = kwdefaults.get(name, _empty) + + annotation = annotations.get(name, _empty) + parameters.append(Parameter(name, annotation=annotation, + kind=_KEYWORD_ONLY, + default=default)) + # **kwargs + if func_code.co_flags & CO_VARKEYWORDS: + index = pos_count + keyword_only_count + if func_code.co_flags & CO_VARARGS: + index += 1 + + name = arg_names[index] + annotation = annotations.get(name, _empty) + parameters.append(Parameter(name, annotation=annotation, + kind=_VAR_KEYWORD)) + + # Is 'func' is a pure Python function - don't validate the + # parameters list (for correct order and defaults), it should be OK. + return cls(parameters, + return_annotation=annotations.get('return', _empty), + __validate_parameters__=is_duck_function) + + +def _signature_from_callable(obj, *, + follow_wrapper_chains=True, + skip_bound_arg=True, + sigcls): + + """Private helper function to get signature for arbitrary + callable objects. + """ + + if not callable(obj): + raise TypeError('{!r} is not a callable object'.format(obj)) + + if isinstance(obj, types.MethodType): + # In this case we skip the first parameter of the underlying + # function (usually `self` or `cls`). + sig = _signature_from_callable( + obj.__func__, + follow_wrapper_chains=follow_wrapper_chains, + skip_bound_arg=skip_bound_arg, + sigcls=sigcls) + + if skip_bound_arg: + return _signature_bound_method(sig) + else: + return sig + + # Was this function wrapped by a decorator? + if follow_wrapper_chains: + obj = unwrap(obj, stop=(lambda f: hasattr(f, "__signature__"))) + if isinstance(obj, types.MethodType): + # If the unwrapped object is a *method*, we might want to + # skip its first parameter (self). + # See test_signature_wrapped_bound_method for details. + return _signature_from_callable( + obj, + follow_wrapper_chains=follow_wrapper_chains, + skip_bound_arg=skip_bound_arg, + sigcls=sigcls) + + try: + sig = obj.__signature__ + except AttributeError: + pass + else: + if sig is not None: + if not isinstance(sig, Signature): + raise TypeError( + 'unexpected object {!r} in __signature__ ' + 'attribute'.format(sig)) + return sig + + try: + partialmethod = obj._partialmethod + except AttributeError: + pass + else: + if isinstance(partialmethod, functools.partialmethod): + # Unbound partialmethod (see functools.partialmethod) + # This means, that we need to calculate the signature + # as if it's a regular partial object, but taking into + # account that the first positional argument + # (usually `self`, or `cls`) will not be passed + # automatically (as for boundmethods) + + wrapped_sig = _signature_from_callable( + partialmethod.func, + follow_wrapper_chains=follow_wrapper_chains, + skip_bound_arg=skip_bound_arg, + sigcls=sigcls) + + sig = _signature_get_partial(wrapped_sig, partialmethod, (None,)) + first_wrapped_param = tuple(wrapped_sig.parameters.values())[0] + if first_wrapped_param.kind is Parameter.VAR_POSITIONAL: + # First argument of the wrapped callable is `*args`, as in + # `partialmethod(lambda *args)`. + return sig + else: + sig_params = tuple(sig.parameters.values()) + assert (not sig_params or + first_wrapped_param is not sig_params[0]) + new_params = (first_wrapped_param,) + sig_params + return sig.replace(parameters=new_params) + + if isfunction(obj) or _signature_is_functionlike(obj): + # If it's a pure Python function, or an object that is duck type + # of a Python function (Cython functions, for instance), then: + return _signature_from_function(sigcls, obj, + skip_bound_arg=skip_bound_arg) + + if _signature_is_builtin(obj): + return _signature_from_builtin(sigcls, obj, + skip_bound_arg=skip_bound_arg) + + if isinstance(obj, functools.partial): + wrapped_sig = _signature_from_callable( + obj.func, + follow_wrapper_chains=follow_wrapper_chains, + skip_bound_arg=skip_bound_arg, + sigcls=sigcls) + return _signature_get_partial(wrapped_sig, obj) + + sig = None + if isinstance(obj, type): + # obj is a class or a metaclass + + # First, let's see if it has an overloaded __call__ defined + # in its metaclass + call = _signature_get_user_defined_method(type(obj), '__call__') + if call is not None: + sig = _signature_from_callable( + call, + follow_wrapper_chains=follow_wrapper_chains, + skip_bound_arg=skip_bound_arg, + sigcls=sigcls) + else: + # Now we check if the 'obj' class has a '__new__' method + new = _signature_get_user_defined_method(obj, '__new__') + if new is not None: + sig = _signature_from_callable( + new, + follow_wrapper_chains=follow_wrapper_chains, + skip_bound_arg=skip_bound_arg, + sigcls=sigcls) + else: + # Finally, we should have at least __init__ implemented + init = _signature_get_user_defined_method(obj, '__init__') + if init is not None: + sig = _signature_from_callable( + init, + follow_wrapper_chains=follow_wrapper_chains, + skip_bound_arg=skip_bound_arg, + sigcls=sigcls) + + if sig is None: + # At this point we know, that `obj` is a class, with no user- + # defined '__init__', '__new__', or class-level '__call__' + + for base in obj.__mro__[:-1]: + # Since '__text_signature__' is implemented as a + # descriptor that extracts text signature from the + # class docstring, if 'obj' is derived from a builtin + # class, its own '__text_signature__' may be 'None'. + # Therefore, we go through the MRO (except the last + # class in there, which is 'object') to find the first + # class with non-empty text signature. + try: + text_sig = base.__text_signature__ + except AttributeError: + pass + else: + if text_sig: + # If 'obj' class has a __text_signature__ attribute: + # return a signature based on it + return _signature_fromstr(sigcls, obj, text_sig) + + # No '__text_signature__' was found for the 'obj' class. + # Last option is to check if its '__init__' is + # object.__init__ or type.__init__. + if type not in obj.__mro__: + # We have a class (not metaclass), but no user-defined + # __init__ or __new__ for it + if (obj.__init__ is object.__init__ and + obj.__new__ is object.__new__): + # Return a signature of 'object' builtin. + return sigcls.from_callable(object) + else: + raise ValueError( + 'no signature found for builtin type {!r}'.format(obj)) + + elif not isinstance(obj, _NonUserDefinedCallables): + # An object with __call__ + # We also check that the 'obj' is not an instance of + # _WrapperDescriptor or _MethodWrapper to avoid + # infinite recursion (and even potential segfault) + call = _signature_get_user_defined_method(type(obj), '__call__') + if call is not None: + try: + sig = _signature_from_callable( + call, + follow_wrapper_chains=follow_wrapper_chains, + skip_bound_arg=skip_bound_arg, + sigcls=sigcls) + except ValueError as ex: + msg = 'no signature found for {!r}'.format(obj) + raise ValueError(msg) from ex + + if sig is not None: + # For classes and objects we skip the first parameter of their + # __call__, __new__, or __init__ methods + if skip_bound_arg: + return _signature_bound_method(sig) + else: + return sig + + if isinstance(obj, types.BuiltinFunctionType): + # Raise a nicer error message for builtins + msg = 'no signature found for builtin function {!r}'.format(obj) + raise ValueError(msg) + + raise ValueError('callable {!r} is not supported by signature'.format(obj)) + + +class _void: + """A private marker - used in Parameter & Signature.""" + + +class _empty: + """Marker object for Signature.empty and Parameter.empty.""" + + +class _ParameterKind(enum.IntEnum): + POSITIONAL_ONLY = 0 + POSITIONAL_OR_KEYWORD = 1 + VAR_POSITIONAL = 2 + KEYWORD_ONLY = 3 + VAR_KEYWORD = 4 + + def __str__(self): + return self._name_ + + @property + def description(self): + return _PARAM_NAME_MAPPING[self] + +_POSITIONAL_ONLY = _ParameterKind.POSITIONAL_ONLY +_POSITIONAL_OR_KEYWORD = _ParameterKind.POSITIONAL_OR_KEYWORD +_VAR_POSITIONAL = _ParameterKind.VAR_POSITIONAL +_KEYWORD_ONLY = _ParameterKind.KEYWORD_ONLY +_VAR_KEYWORD = _ParameterKind.VAR_KEYWORD + +_PARAM_NAME_MAPPING = { + _POSITIONAL_ONLY: 'positional-only', + _POSITIONAL_OR_KEYWORD: 'positional or keyword', + _VAR_POSITIONAL: 'variadic positional', + _KEYWORD_ONLY: 'keyword-only', + _VAR_KEYWORD: 'variadic keyword' +} + + +class Parameter: + """Represents a parameter in a function signature. + + Has the following public attributes: + + * name : str + The name of the parameter as a string. + * default : object + The default value for the parameter if specified. If the + parameter has no default value, this attribute is set to + `Parameter.empty`. + * annotation + The annotation for the parameter if specified. If the + parameter has no annotation, this attribute is set to + `Parameter.empty`. + * kind : str + Describes how argument values are bound to the parameter. + Possible values: `Parameter.POSITIONAL_ONLY`, + `Parameter.POSITIONAL_OR_KEYWORD`, `Parameter.VAR_POSITIONAL`, + `Parameter.KEYWORD_ONLY`, `Parameter.VAR_KEYWORD`. + """ + + __slots__ = ('_name', '_kind', '_default', '_annotation') + + POSITIONAL_ONLY = _POSITIONAL_ONLY + POSITIONAL_OR_KEYWORD = _POSITIONAL_OR_KEYWORD + VAR_POSITIONAL = _VAR_POSITIONAL + KEYWORD_ONLY = _KEYWORD_ONLY + VAR_KEYWORD = _VAR_KEYWORD + + empty = _empty + + def __init__(self, name, kind, *, default=_empty, annotation=_empty): + try: + self._kind = _ParameterKind(kind) + except ValueError: + raise ValueError(f'value {kind!r} is not a valid Parameter.kind') + if default is not _empty: + if self._kind in (_VAR_POSITIONAL, _VAR_KEYWORD): + msg = '{} parameters cannot have default values' + msg = msg.format(self._kind.description) + raise ValueError(msg) + self._default = default + self._annotation = annotation + + if name is _empty: + raise ValueError('name is a required attribute for Parameter') + + if not isinstance(name, str): + msg = 'name must be a str, not a {}'.format(type(name).__name__) + raise TypeError(msg) + + if name[0] == '.' and name[1:].isdigit(): + # These are implicit arguments generated by comprehensions. In + # order to provide a friendlier interface to users, we recast + # their name as "implicitN" and treat them as positional-only. + # See issue 19611. + if self._kind != _POSITIONAL_OR_KEYWORD: + msg = ( + 'implicit arguments must be passed as ' + 'positional or keyword arguments, not {}' + ) + msg = msg.format(self._kind.description) + raise ValueError(msg) + self._kind = _POSITIONAL_ONLY + name = 'implicit{}'.format(name[1:]) + + if not name.isidentifier(): + raise ValueError('{!r} is not a valid parameter name'.format(name)) + + self._name = name + + def __reduce__(self): + return (type(self), + (self._name, self._kind), + {'_default': self._default, + '_annotation': self._annotation}) + + def __setstate__(self, state): + self._default = state['_default'] + self._annotation = state['_annotation'] + + @property + def name(self): + return self._name + + @property + def default(self): + return self._default + + @property + def annotation(self): + return self._annotation + + @property + def kind(self): + return self._kind + + def replace(self, *, name=_void, kind=_void, + annotation=_void, default=_void): + """Creates a customized copy of the Parameter.""" + + if name is _void: + name = self._name + + if kind is _void: + kind = self._kind + + if annotation is _void: + annotation = self._annotation + + if default is _void: + default = self._default + + return type(self)(name, kind, default=default, annotation=annotation) + + def __str__(self): + kind = self.kind + formatted = self._name + + # Add annotation and default value + if self._annotation is not _empty: + formatted = '{}: {}'.format(formatted, + formatannotation(self._annotation)) + + if self._default is not _empty: + if self._annotation is not _empty: + formatted = '{} = {}'.format(formatted, repr(self._default)) + else: + formatted = '{}={}'.format(formatted, repr(self._default)) + + if kind == _VAR_POSITIONAL: + formatted = '*' + formatted + elif kind == _VAR_KEYWORD: + formatted = '**' + formatted + + return formatted + + def __repr__(self): + return '<{} "{}">'.format(self.__class__.__name__, self) + + def __hash__(self): + return hash((self.name, self.kind, self.annotation, self.default)) + + def __eq__(self, other): + if self is other: + return True + if not isinstance(other, Parameter): + return NotImplemented + return (self._name == other._name and + self._kind == other._kind and + self._default == other._default and + self._annotation == other._annotation) + + +class BoundArguments: + """Result of `Signature.bind` call. Holds the mapping of arguments + to the function's parameters. + + Has the following public attributes: + + * arguments : OrderedDict + An ordered mutable mapping of parameters' names to arguments' values. + Does not contain arguments' default values. + * signature : Signature + The Signature object that created this instance. + * args : tuple + Tuple of positional arguments values. + * kwargs : dict + Dict of keyword arguments values. + """ + + __slots__ = ('arguments', '_signature', '__weakref__') + + def __init__(self, signature, arguments): + self.arguments = arguments + self._signature = signature + + @property + def signature(self): + return self._signature + + @property + def args(self): + args = [] + for param_name, param in self._signature.parameters.items(): + if param.kind in (_VAR_KEYWORD, _KEYWORD_ONLY): + break + + try: + arg = self.arguments[param_name] + except KeyError: + # We're done here. Other arguments + # will be mapped in 'BoundArguments.kwargs' + break + else: + if param.kind == _VAR_POSITIONAL: + # *args + args.extend(arg) + else: + # plain argument + args.append(arg) + + return tuple(args) + + @property + def kwargs(self): + kwargs = {} + kwargs_started = False + for param_name, param in self._signature.parameters.items(): + if not kwargs_started: + if param.kind in (_VAR_KEYWORD, _KEYWORD_ONLY): + kwargs_started = True + else: + if param_name not in self.arguments: + kwargs_started = True + continue + + if not kwargs_started: + continue + + try: + arg = self.arguments[param_name] + except KeyError: + pass + else: + if param.kind == _VAR_KEYWORD: + # **kwargs + kwargs.update(arg) + else: + # plain keyword argument + kwargs[param_name] = arg + + return kwargs + + def apply_defaults(self): + """Set default values for missing arguments. + + For variable-positional arguments (*args) the default is an + empty tuple. + + For variable-keyword arguments (**kwargs) the default is an + empty dict. + """ + arguments = self.arguments + new_arguments = [] + for name, param in self._signature.parameters.items(): + try: + new_arguments.append((name, arguments[name])) + except KeyError: + if param.default is not _empty: + val = param.default + elif param.kind is _VAR_POSITIONAL: + val = () + elif param.kind is _VAR_KEYWORD: + val = {} + else: + # This BoundArguments was likely produced by + # Signature.bind_partial(). + continue + new_arguments.append((name, val)) + self.arguments = OrderedDict(new_arguments) + + def __eq__(self, other): + if self is other: + return True + if not isinstance(other, BoundArguments): + return NotImplemented + return (self.signature == other.signature and + self.arguments == other.arguments) + + def __setstate__(self, state): + self._signature = state['_signature'] + self.arguments = state['arguments'] + + def __getstate__(self): + return {'_signature': self._signature, 'arguments': self.arguments} + + def __repr__(self): + args = [] + for arg, value in self.arguments.items(): + args.append('{}={!r}'.format(arg, value)) + return '<{} ({})>'.format(self.__class__.__name__, ', '.join(args)) + + +class Signature: + """A Signature object represents the overall signature of a function. + It stores a Parameter object for each parameter accepted by the + function, as well as information specific to the function itself. + + A Signature object has the following public attributes and methods: + + * parameters : OrderedDict + An ordered mapping of parameters' names to the corresponding + Parameter objects (keyword-only arguments are in the same order + as listed in `code.co_varnames`). + * return_annotation : object + The annotation for the return type of the function if specified. + If the function has no annotation for its return type, this + attribute is set to `Signature.empty`. + * bind(*args, **kwargs) -> BoundArguments + Creates a mapping from positional and keyword arguments to + parameters. + * bind_partial(*args, **kwargs) -> BoundArguments + Creates a partial mapping from positional and keyword arguments + to parameters (simulating 'functools.partial' behavior.) + """ + + __slots__ = ('_return_annotation', '_parameters') + + _parameter_cls = Parameter + _bound_arguments_cls = BoundArguments + + empty = _empty + + def __init__(self, parameters=None, *, return_annotation=_empty, + __validate_parameters__=True): + """Constructs Signature from the given list of Parameter + objects and 'return_annotation'. All arguments are optional. + """ + + if parameters is None: + params = OrderedDict() + else: + if __validate_parameters__: + params = OrderedDict() + top_kind = _POSITIONAL_ONLY + kind_defaults = False + + for idx, param in enumerate(parameters): + kind = param.kind + name = param.name + + if kind < top_kind: + msg = ( + 'wrong parameter order: {} parameter before {} ' + 'parameter' + ) + msg = msg.format(top_kind.description, + kind.description) + raise ValueError(msg) + elif kind > top_kind: + kind_defaults = False + top_kind = kind + + if kind in (_POSITIONAL_ONLY, _POSITIONAL_OR_KEYWORD): + if param.default is _empty: + if kind_defaults: + # No default for this parameter, but the + # previous parameter of the same kind had + # a default + msg = 'non-default argument follows default ' \ + 'argument' + raise ValueError(msg) + else: + # There is a default for this parameter. + kind_defaults = True + + if name in params: + msg = 'duplicate parameter name: {!r}'.format(name) + raise ValueError(msg) + + params[name] = param + else: + params = OrderedDict(((param.name, param) + for param in parameters)) + + self._parameters = types.MappingProxyType(params) + self._return_annotation = return_annotation + + @classmethod + def from_function(cls, func): + """Constructs Signature for the given python function. + + Deprecated since Python 3.5, use `Signature.from_callable()`. + """ + + warnings.warn("inspect.Signature.from_function() is deprecated since " + "Python 3.5, use Signature.from_callable()", + DeprecationWarning, stacklevel=2) + return _signature_from_function(cls, func) + + @classmethod + def from_builtin(cls, func): + """Constructs Signature for the given builtin function. + + Deprecated since Python 3.5, use `Signature.from_callable()`. + """ + + warnings.warn("inspect.Signature.from_builtin() is deprecated since " + "Python 3.5, use Signature.from_callable()", + DeprecationWarning, stacklevel=2) + return _signature_from_builtin(cls, func) + + @classmethod + def from_callable(cls, obj, *, follow_wrapped=True): + """Constructs Signature for the given callable object.""" + return _signature_from_callable(obj, sigcls=cls, + follow_wrapper_chains=follow_wrapped) + + @property + def parameters(self): + return self._parameters + + @property + def return_annotation(self): + return self._return_annotation + + def replace(self, *, parameters=_void, return_annotation=_void): + """Creates a customized copy of the Signature. + Pass 'parameters' and/or 'return_annotation' arguments + to override them in the new copy. + """ + + if parameters is _void: + parameters = self.parameters.values() + + if return_annotation is _void: + return_annotation = self._return_annotation + + return type(self)(parameters, + return_annotation=return_annotation) + + def _hash_basis(self): + params = tuple(param for param in self.parameters.values() + if param.kind != _KEYWORD_ONLY) + + kwo_params = {param.name: param for param in self.parameters.values() + if param.kind == _KEYWORD_ONLY} + + return params, kwo_params, self.return_annotation + + def __hash__(self): + params, kwo_params, return_annotation = self._hash_basis() + kwo_params = frozenset(kwo_params.values()) + return hash((params, kwo_params, return_annotation)) + + def __eq__(self, other): + if self is other: + return True + if not isinstance(other, Signature): + return NotImplemented + return self._hash_basis() == other._hash_basis() + + def _bind(self, args, kwargs, *, partial=False): + """Private method. Don't use directly.""" + + arguments = OrderedDict() + + parameters = iter(self.parameters.values()) + parameters_ex = () + arg_vals = iter(args) + + while True: + # Let's iterate through the positional arguments and corresponding + # parameters + try: + arg_val = next(arg_vals) + except StopIteration: + # No more positional arguments + try: + param = next(parameters) + except StopIteration: + # No more parameters. That's it. Just need to check that + # we have no `kwargs` after this while loop + break + else: + if param.kind == _VAR_POSITIONAL: + # That's OK, just empty *args. Let's start parsing + # kwargs + break + elif param.name in kwargs: + if param.kind == _POSITIONAL_ONLY: + msg = '{arg!r} parameter is positional only, ' \ + 'but was passed as a keyword' + msg = msg.format(arg=param.name) + raise TypeError(msg) from None + parameters_ex = (param,) + break + elif (param.kind == _VAR_KEYWORD or + param.default is not _empty): + # That's fine too - we have a default value for this + # parameter. So, lets start parsing `kwargs`, starting + # with the current parameter + parameters_ex = (param,) + break + else: + # No default, not VAR_KEYWORD, not VAR_POSITIONAL, + # not in `kwargs` + if partial: + parameters_ex = (param,) + break + else: + msg = 'missing a required argument: {arg!r}' + msg = msg.format(arg=param.name) + raise TypeError(msg) from None + else: + # We have a positional argument to process + try: + param = next(parameters) + except StopIteration: + raise TypeError('too many positional arguments') from None + else: + if param.kind in (_VAR_KEYWORD, _KEYWORD_ONLY): + # Looks like we have no parameter for this positional + # argument + raise TypeError( + 'too many positional arguments') from None + + if param.kind == _VAR_POSITIONAL: + # We have an '*args'-like argument, let's fill it with + # all positional arguments we have left and move on to + # the next phase + values = [arg_val] + values.extend(arg_vals) + arguments[param.name] = tuple(values) + break + + if param.name in kwargs and param.kind != _POSITIONAL_ONLY: + raise TypeError( + 'multiple values for argument {arg!r}'.format( + arg=param.name)) from None + + arguments[param.name] = arg_val + + # Now, we iterate through the remaining parameters to process + # keyword arguments + kwargs_param = None + for param in itertools.chain(parameters_ex, parameters): + if param.kind == _VAR_KEYWORD: + # Memorize that we have a '**kwargs'-like parameter + kwargs_param = param + continue + + if param.kind == _VAR_POSITIONAL: + # Named arguments don't refer to '*args'-like parameters. + # We only arrive here if the positional arguments ended + # before reaching the last parameter before *args. + continue + + param_name = param.name + try: + arg_val = kwargs.pop(param_name) + except KeyError: + # We have no value for this parameter. It's fine though, + # if it has a default value, or it is an '*args'-like + # parameter, left alone by the processing of positional + # arguments. + if (not partial and param.kind != _VAR_POSITIONAL and + param.default is _empty): + raise TypeError('missing a required argument: {arg!r}'. \ + format(arg=param_name)) from None + + else: + if param.kind == _POSITIONAL_ONLY: + # This should never happen in case of a properly built + # Signature object (but let's have this check here + # to ensure correct behaviour just in case) + raise TypeError('{arg!r} parameter is positional only, ' + 'but was passed as a keyword'. \ + format(arg=param.name)) + + arguments[param_name] = arg_val + + if kwargs: + if kwargs_param is not None: + # Process our '**kwargs'-like parameter + arguments[kwargs_param.name] = kwargs + else: + raise TypeError( + 'got an unexpected keyword argument {arg!r}'.format( + arg=next(iter(kwargs)))) + + return self._bound_arguments_cls(self, arguments) + + def bind(self, /, *args, **kwargs): + """Get a BoundArguments object, that maps the passed `args` + and `kwargs` to the function's signature. Raises `TypeError` + if the passed arguments can not be bound. + """ + return self._bind(args, kwargs) + + def bind_partial(self, /, *args, **kwargs): + """Get a BoundArguments object, that partially maps the + passed `args` and `kwargs` to the function's signature. + Raises `TypeError` if the passed arguments can not be bound. + """ + return self._bind(args, kwargs, partial=True) + + def __reduce__(self): + return (type(self), + (tuple(self._parameters.values()),), + {'_return_annotation': self._return_annotation}) + + def __setstate__(self, state): + self._return_annotation = state['_return_annotation'] + + def __repr__(self): + return '<{} {}>'.format(self.__class__.__name__, self) + + def __str__(self): + result = [] + render_pos_only_separator = False + render_kw_only_separator = True + for param in self.parameters.values(): + formatted = str(param) + + kind = param.kind + + if kind == _POSITIONAL_ONLY: + render_pos_only_separator = True + elif render_pos_only_separator: + # It's not a positional-only parameter, and the flag + # is set to 'True' (there were pos-only params before.) + result.append('/') + render_pos_only_separator = False + + if kind == _VAR_POSITIONAL: + # OK, we have an '*args'-like parameter, so we won't need + # a '*' to separate keyword-only arguments + render_kw_only_separator = False + elif kind == _KEYWORD_ONLY and render_kw_only_separator: + # We have a keyword-only parameter to render and we haven't + # rendered an '*args'-like parameter before, so add a '*' + # separator to the parameters list ("foo(arg1, *, arg2)" case) + result.append('*') + # This condition should be only triggered once, so + # reset the flag + render_kw_only_separator = False + + result.append(formatted) + + if render_pos_only_separator: + # There were only positional-only parameters, hence the + # flag was not reset to 'False' + result.append('/') + + rendered = '({})'.format(', '.join(result)) + + if self.return_annotation is not _empty: + anno = formatannotation(self.return_annotation) + rendered += ' -> {}'.format(anno) + + return rendered + + +def signature(obj, *, follow_wrapped=True): + """Get a signature object for the passed callable.""" + return Signature.from_callable(obj, follow_wrapped=follow_wrapped) + + +def _main(): + """ Logic for inspecting an object given at command line """ + import argparse + import importlib + + parser = argparse.ArgumentParser() + parser.add_argument( + 'object', + help="The object to be analysed. " + "It supports the 'module:qualname' syntax") + parser.add_argument( + '-d', '--details', action='store_true', + help='Display info about the module rather than its source code') + + args = parser.parse_args() + + target = args.object + mod_name, has_attrs, attrs = target.partition(":") + try: + obj = module = importlib.import_module(mod_name) + except Exception as exc: + msg = "Failed to import {} ({}: {})".format(mod_name, + type(exc).__name__, + exc) + print(msg, file=sys.stderr) + sys.exit(2) + + if has_attrs: + parts = attrs.split(".") + obj = module + for part in parts: + obj = getattr(obj, part) + + if module.__name__ in sys.builtin_module_names: + print("Can't get info for builtin modules.", file=sys.stderr) + sys.exit(1) + + if args.details: + print('Target: {}'.format(target)) + print('Origin: {}'.format(getsourcefile(module))) + print('Cached: {}'.format(module.__cached__)) + if obj is module: + print('Loader: {}'.format(repr(module.__loader__))) + if hasattr(module, '__path__'): + print('Submodule search path: {}'.format(module.__path__)) + else: + try: + __, lineno = findsource(obj) + except Exception: + pass + else: + print('Line: {}'.format(lineno)) + + print('\n') + else: + print(getsource(obj)) + + +if __name__ == "__main__": + _main() diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/io.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/io.py new file mode 100644 index 0000000000000000000000000000000000000000..fbce6efc010c07c78bcf7b4458d5f7f22cd6f0ba --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/io.py @@ -0,0 +1,99 @@ +"""The io module provides the Python interfaces to stream handling. The +builtin open function is defined in this module. + +At the top of the I/O hierarchy is the abstract base class IOBase. It +defines the basic interface to a stream. Note, however, that there is no +separation between reading and writing to streams; implementations are +allowed to raise an OSError if they do not support a given operation. + +Extending IOBase is RawIOBase which deals simply with the reading and +writing of raw bytes to a stream. FileIO subclasses RawIOBase to provide +an interface to OS files. + +BufferedIOBase deals with buffering on a raw byte stream (RawIOBase). Its +subclasses, BufferedWriter, BufferedReader, and BufferedRWPair buffer +streams that are readable, writable, and both respectively. +BufferedRandom provides a buffered interface to random access +streams. BytesIO is a simple stream of in-memory bytes. + +Another IOBase subclass, TextIOBase, deals with the encoding and decoding +of streams into text. TextIOWrapper, which extends it, is a buffered text +interface to a buffered raw stream (`BufferedIOBase`). Finally, StringIO +is an in-memory stream for text. + +Argument names are not part of the specification, and only the arguments +of open() are intended to be used as keyword arguments. + +data: + +DEFAULT_BUFFER_SIZE + + An int containing the default buffer size used by the module's buffered + I/O classes. open() uses the file's blksize (as obtained by os.stat) if + possible. +""" +# New I/O library conforming to PEP 3116. + +__author__ = ("Guido van Rossum , " + "Mike Verdone , " + "Mark Russell , " + "Antoine Pitrou , " + "Amaury Forgeot d'Arc , " + "Benjamin Peterson ") + +__all__ = ["BlockingIOError", "open", "open_code", "IOBase", "RawIOBase", + "FileIO", "BytesIO", "StringIO", "BufferedIOBase", + "BufferedReader", "BufferedWriter", "BufferedRWPair", + "BufferedRandom", "TextIOBase", "TextIOWrapper", + "UnsupportedOperation", "SEEK_SET", "SEEK_CUR", "SEEK_END"] + + +import _io +import abc + +from _io import (DEFAULT_BUFFER_SIZE, BlockingIOError, UnsupportedOperation, + open, open_code, FileIO, BytesIO, StringIO, BufferedReader, + BufferedWriter, BufferedRWPair, BufferedRandom, + IncrementalNewlineDecoder, TextIOWrapper) + +OpenWrapper = _io.open # for compatibility with _pyio + +# Pretend this exception was created here. +UnsupportedOperation.__module__ = "io" + +# for seek() +SEEK_SET = 0 +SEEK_CUR = 1 +SEEK_END = 2 + +# Declaring ABCs in C is tricky so we do it here. +# Method descriptions and default implementations are inherited from the C +# version however. +class IOBase(_io._IOBase, metaclass=abc.ABCMeta): + __doc__ = _io._IOBase.__doc__ + +class RawIOBase(_io._RawIOBase, IOBase): + __doc__ = _io._RawIOBase.__doc__ + +class BufferedIOBase(_io._BufferedIOBase, IOBase): + __doc__ = _io._BufferedIOBase.__doc__ + +class TextIOBase(_io._TextIOBase, IOBase): + __doc__ = _io._TextIOBase.__doc__ + +RawIOBase.register(FileIO) + +for klass in (BytesIO, BufferedReader, BufferedWriter, BufferedRandom, + BufferedRWPair): + BufferedIOBase.register(klass) + +for klass in (StringIO, TextIOWrapper): + TextIOBase.register(klass) +del klass + +try: + from _io import _WindowsConsoleIO +except ImportError: + pass +else: + RawIOBase.register(_WindowsConsoleIO) diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/lzma.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/lzma.py new file mode 100644 index 0000000000000000000000000000000000000000..0817b872d2019f2198b17e382cdc9beb3899ac80 --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/lzma.py @@ -0,0 +1,347 @@ +"""Interface to the liblzma compression library. + +This module provides a class for reading and writing compressed files, +classes for incremental (de)compression, and convenience functions for +one-shot (de)compression. + +These classes and functions support both the XZ and legacy LZMA +container formats, as well as raw compressed data streams. +""" + +__all__ = [ + "CHECK_NONE", "CHECK_CRC32", "CHECK_CRC64", "CHECK_SHA256", + "CHECK_ID_MAX", "CHECK_UNKNOWN", + "FILTER_LZMA1", "FILTER_LZMA2", "FILTER_DELTA", "FILTER_X86", "FILTER_IA64", + "FILTER_ARM", "FILTER_ARMTHUMB", "FILTER_POWERPC", "FILTER_SPARC", + "FORMAT_AUTO", "FORMAT_XZ", "FORMAT_ALONE", "FORMAT_RAW", + "MF_HC3", "MF_HC4", "MF_BT2", "MF_BT3", "MF_BT4", + "MODE_FAST", "MODE_NORMAL", "PRESET_DEFAULT", "PRESET_EXTREME", + + "LZMACompressor", "LZMADecompressor", "LZMAFile", "LZMAError", + "open", "compress", "decompress", "is_check_supported", +] + +import builtins +import io +import os +from _lzma import * +from _lzma import _encode_filter_properties, _decode_filter_properties +import _compression + + +_MODE_CLOSED = 0 +_MODE_READ = 1 +# Value 2 no longer used +_MODE_WRITE = 3 + + +class LZMAFile(_compression.BaseStream): + + """A file object providing transparent LZMA (de)compression. + + An LZMAFile can act as a wrapper for an existing file object, or + refer directly to a named file on disk. + + Note that LZMAFile provides a *binary* file interface - data read + is returned as bytes, and data to be written must be given as bytes. + """ + + def __init__(self, filename=None, mode="r", *, + format=None, check=-1, preset=None, filters=None): + """Open an LZMA-compressed file in binary mode. + + filename can be either an actual file name (given as a str, + bytes, or PathLike object), in which case the named file is + opened, or it can be an existing file object to read from or + write to. + + mode can be "r" for reading (default), "w" for (over)writing, + "x" for creating exclusively, or "a" for appending. These can + equivalently be given as "rb", "wb", "xb" and "ab" respectively. + + format specifies the container format to use for the file. + If mode is "r", this defaults to FORMAT_AUTO. Otherwise, the + default is FORMAT_XZ. + + check specifies the integrity check to use. This argument can + only be used when opening a file for writing. For FORMAT_XZ, + the default is CHECK_CRC64. FORMAT_ALONE and FORMAT_RAW do not + support integrity checks - for these formats, check must be + omitted, or be CHECK_NONE. + + When opening a file for reading, the *preset* argument is not + meaningful, and should be omitted. The *filters* argument should + also be omitted, except when format is FORMAT_RAW (in which case + it is required). + + When opening a file for writing, the settings used by the + compressor can be specified either as a preset compression + level (with the *preset* argument), or in detail as a custom + filter chain (with the *filters* argument). For FORMAT_XZ and + FORMAT_ALONE, the default is to use the PRESET_DEFAULT preset + level. For FORMAT_RAW, the caller must always specify a filter + chain; the raw compressor does not support preset compression + levels. + + preset (if provided) should be an integer in the range 0-9, + optionally OR-ed with the constant PRESET_EXTREME. + + filters (if provided) should be a sequence of dicts. Each dict + should have an entry for "id" indicating ID of the filter, plus + additional entries for options to the filter. + """ + self._fp = None + self._closefp = False + self._mode = _MODE_CLOSED + + if mode in ("r", "rb"): + if check != -1: + raise ValueError("Cannot specify an integrity check " + "when opening a file for reading") + if preset is not None: + raise ValueError("Cannot specify a preset compression " + "level when opening a file for reading") + if format is None: + format = FORMAT_AUTO + mode_code = _MODE_READ + elif mode in ("w", "wb", "a", "ab", "x", "xb"): + if format is None: + format = FORMAT_XZ + mode_code = _MODE_WRITE + self._compressor = LZMACompressor(format=format, check=check, + preset=preset, filters=filters) + self._pos = 0 + else: + raise ValueError("Invalid mode: {!r}".format(mode)) + + if isinstance(filename, (str, bytes, os.PathLike)): + if "b" not in mode: + mode += "b" + self._fp = builtins.open(filename, mode) + self._closefp = True + self._mode = mode_code + elif hasattr(filename, "read") or hasattr(filename, "write"): + self._fp = filename + self._mode = mode_code + else: + raise TypeError("filename must be a str, bytes, file or PathLike object") + + if self._mode == _MODE_READ: + raw = _compression.DecompressReader(self._fp, LZMADecompressor, + trailing_error=LZMAError, format=format, filters=filters) + self._buffer = io.BufferedReader(raw) + + def close(self): + """Flush and close the file. + + May be called more than once without error. Once the file is + closed, any other operation on it will raise a ValueError. + """ + if self._mode == _MODE_CLOSED: + return + try: + if self._mode == _MODE_READ: + self._buffer.close() + self._buffer = None + elif self._mode == _MODE_WRITE: + self._fp.write(self._compressor.flush()) + self._compressor = None + finally: + try: + if self._closefp: + self._fp.close() + finally: + self._fp = None + self._closefp = False + self._mode = _MODE_CLOSED + + @property + def closed(self): + """True if this file is closed.""" + return self._mode == _MODE_CLOSED + + def fileno(self): + """Return the file descriptor for the underlying file.""" + self._check_not_closed() + return self._fp.fileno() + + def seekable(self): + """Return whether the file supports seeking.""" + return self.readable() and self._buffer.seekable() + + def readable(self): + """Return whether the file was opened for reading.""" + self._check_not_closed() + return self._mode == _MODE_READ + + def writable(self): + """Return whether the file was opened for writing.""" + self._check_not_closed() + return self._mode == _MODE_WRITE + + def peek(self, size=-1): + """Return buffered data without advancing the file position. + + Always returns at least one byte of data, unless at EOF. + The exact number of bytes returned is unspecified. + """ + self._check_can_read() + # Relies on the undocumented fact that BufferedReader.peek() always + # returns at least one byte (except at EOF) + return self._buffer.peek(size) + + def read(self, size=-1): + """Read up to size uncompressed bytes from the file. + + If size is negative or omitted, read until EOF is reached. + Returns b"" if the file is already at EOF. + """ + self._check_can_read() + return self._buffer.read(size) + + def read1(self, size=-1): + """Read up to size uncompressed bytes, while trying to avoid + making multiple reads from the underlying stream. Reads up to a + buffer's worth of data if size is negative. + + Returns b"" if the file is at EOF. + """ + self._check_can_read() + if size < 0: + size = io.DEFAULT_BUFFER_SIZE + return self._buffer.read1(size) + + def readline(self, size=-1): + """Read a line of uncompressed bytes from the file. + + The terminating newline (if present) is retained. If size is + non-negative, no more than size bytes will be read (in which + case the line may be incomplete). Returns b'' if already at EOF. + """ + self._check_can_read() + return self._buffer.readline(size) + + def write(self, data): + """Write a bytes object to the file. + + Returns the number of uncompressed bytes written, which is + always len(data). Note that due to buffering, the file on disk + may not reflect the data written until close() is called. + """ + self._check_can_write() + compressed = self._compressor.compress(data) + self._fp.write(compressed) + self._pos += len(data) + return len(data) + + def seek(self, offset, whence=io.SEEK_SET): + """Change the file position. + + The new position is specified by offset, relative to the + position indicated by whence. Possible values for whence are: + + 0: start of stream (default): offset must not be negative + 1: current stream position + 2: end of stream; offset must not be positive + + Returns the new file position. + + Note that seeking is emulated, so depending on the parameters, + this operation may be extremely slow. + """ + self._check_can_seek() + return self._buffer.seek(offset, whence) + + def tell(self): + """Return the current file position.""" + self._check_not_closed() + if self._mode == _MODE_READ: + return self._buffer.tell() + return self._pos + + +def open(filename, mode="rb", *, + format=None, check=-1, preset=None, filters=None, + encoding=None, errors=None, newline=None): + """Open an LZMA-compressed file in binary or text mode. + + filename can be either an actual file name (given as a str, bytes, + or PathLike object), in which case the named file is opened, or it + can be an existing file object to read from or write to. + + The mode argument can be "r", "rb" (default), "w", "wb", "x", "xb", + "a", or "ab" for binary mode, or "rt", "wt", "xt", or "at" for text + mode. + + The format, check, preset and filters arguments specify the + compression settings, as for LZMACompressor, LZMADecompressor and + LZMAFile. + + For binary mode, this function is equivalent to the LZMAFile + constructor: LZMAFile(filename, mode, ...). In this case, the + encoding, errors and newline arguments must not be provided. + + For text mode, an LZMAFile object is created, and wrapped in an + io.TextIOWrapper instance with the specified encoding, error + handling behavior, and line ending(s). + + """ + if "t" in mode: + if "b" in mode: + raise ValueError("Invalid mode: %r" % (mode,)) + else: + if encoding is not None: + raise ValueError("Argument 'encoding' not supported in binary mode") + if errors is not None: + raise ValueError("Argument 'errors' not supported in binary mode") + if newline is not None: + raise ValueError("Argument 'newline' not supported in binary mode") + + lz_mode = mode.replace("t", "") + binary_file = LZMAFile(filename, lz_mode, format=format, check=check, + preset=preset, filters=filters) + + if "t" in mode: + return io.TextIOWrapper(binary_file, encoding, errors, newline) + else: + return binary_file + + +def compress(data, format=FORMAT_XZ, check=-1, preset=None, filters=None): + """Compress a block of data. + + Refer to LZMACompressor's docstring for a description of the + optional arguments *format*, *check*, *preset* and *filters*. + + For incremental compression, use an LZMACompressor instead. + """ + comp = LZMACompressor(format, check, preset, filters) + return comp.compress(data) + comp.flush() + + +def decompress(data, format=FORMAT_AUTO, memlimit=None, filters=None): + """Decompress a block of data. + + Refer to LZMADecompressor's docstring for a description of the + optional arguments *format*, *check* and *filters*. + + For incremental decompression, use an LZMADecompressor instead. + """ + results = [] + while True: + decomp = LZMADecompressor(format, memlimit, filters) + try: + res = decomp.decompress(data) + except LZMAError: + if results: + break # Leftover data is not a valid LZMA/XZ stream; ignore it. + else: + raise # Error on the first iteration; bail out. + results.append(res) + if not decomp.eof: + raise LZMAError("Compressed data ended before the " + "end-of-stream marker was reached") + data = decomp.unused_data + if not data: + break + return b"".join(results) diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/mailbox.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/mailbox.py new file mode 100644 index 0000000000000000000000000000000000000000..5b4e86419f1173147ab4957af415ebcaef655b20 --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/mailbox.py @@ -0,0 +1,2146 @@ +"""Read/write support for Maildir, mbox, MH, Babyl, and MMDF mailboxes.""" + +# Notes for authors of new mailbox subclasses: +# +# Remember to fsync() changes to disk before closing a modified file +# or returning from a flush() method. See functions _sync_flush() and +# _sync_close(). + +import os +import time +import calendar +import socket +import errno +import copy +import warnings +import email +import email.message +import email.generator +import io +import contextlib +try: + import fcntl +except ImportError: + fcntl = None + +__all__ = ['Mailbox', 'Maildir', 'mbox', 'MH', 'Babyl', 'MMDF', + 'Message', 'MaildirMessage', 'mboxMessage', 'MHMessage', + 'BabylMessage', 'MMDFMessage', 'Error', 'NoSuchMailboxError', + 'NotEmptyError', 'ExternalClashError', 'FormatError'] + +linesep = os.linesep.encode('ascii') + +class Mailbox: + """A group of messages in a particular place.""" + + def __init__(self, path, factory=None, create=True): + """Initialize a Mailbox instance.""" + self._path = os.path.abspath(os.path.expanduser(path)) + self._factory = factory + + def add(self, message): + """Add message and return assigned key.""" + raise NotImplementedError('Method must be implemented by subclass') + + def remove(self, key): + """Remove the keyed message; raise KeyError if it doesn't exist.""" + raise NotImplementedError('Method must be implemented by subclass') + + def __delitem__(self, key): + self.remove(key) + + def discard(self, key): + """If the keyed message exists, remove it.""" + try: + self.remove(key) + except KeyError: + pass + + def __setitem__(self, key, message): + """Replace the keyed message; raise KeyError if it doesn't exist.""" + raise NotImplementedError('Method must be implemented by subclass') + + def get(self, key, default=None): + """Return the keyed message, or default if it doesn't exist.""" + try: + return self.__getitem__(key) + except KeyError: + return default + + def __getitem__(self, key): + """Return the keyed message; raise KeyError if it doesn't exist.""" + if not self._factory: + return self.get_message(key) + else: + with contextlib.closing(self.get_file(key)) as file: + return self._factory(file) + + def get_message(self, key): + """Return a Message representation or raise a KeyError.""" + raise NotImplementedError('Method must be implemented by subclass') + + def get_string(self, key): + """Return a string representation or raise a KeyError. + + Uses email.message.Message to create a 7bit clean string + representation of the message.""" + return email.message_from_bytes(self.get_bytes(key)).as_string() + + def get_bytes(self, key): + """Return a byte string representation or raise a KeyError.""" + raise NotImplementedError('Method must be implemented by subclass') + + def get_file(self, key): + """Return a file-like representation or raise a KeyError.""" + raise NotImplementedError('Method must be implemented by subclass') + + def iterkeys(self): + """Return an iterator over keys.""" + raise NotImplementedError('Method must be implemented by subclass') + + def keys(self): + """Return a list of keys.""" + return list(self.iterkeys()) + + def itervalues(self): + """Return an iterator over all messages.""" + for key in self.iterkeys(): + try: + value = self[key] + except KeyError: + continue + yield value + + def __iter__(self): + return self.itervalues() + + def values(self): + """Return a list of messages. Memory intensive.""" + return list(self.itervalues()) + + def iteritems(self): + """Return an iterator over (key, message) tuples.""" + for key in self.iterkeys(): + try: + value = self[key] + except KeyError: + continue + yield (key, value) + + def items(self): + """Return a list of (key, message) tuples. Memory intensive.""" + return list(self.iteritems()) + + def __contains__(self, key): + """Return True if the keyed message exists, False otherwise.""" + raise NotImplementedError('Method must be implemented by subclass') + + def __len__(self): + """Return a count of messages in the mailbox.""" + raise NotImplementedError('Method must be implemented by subclass') + + def clear(self): + """Delete all messages.""" + for key in self.keys(): + self.discard(key) + + def pop(self, key, default=None): + """Delete the keyed message and return it, or default.""" + try: + result = self[key] + except KeyError: + return default + self.discard(key) + return result + + def popitem(self): + """Delete an arbitrary (key, message) pair and return it.""" + for key in self.iterkeys(): + return (key, self.pop(key)) # This is only run once. + else: + raise KeyError('No messages in mailbox') + + def update(self, arg=None): + """Change the messages that correspond to certain keys.""" + if hasattr(arg, 'iteritems'): + source = arg.iteritems() + elif hasattr(arg, 'items'): + source = arg.items() + else: + source = arg + bad_key = False + for key, message in source: + try: + self[key] = message + except KeyError: + bad_key = True + if bad_key: + raise KeyError('No message with key(s)') + + def flush(self): + """Write any pending changes to the disk.""" + raise NotImplementedError('Method must be implemented by subclass') + + def lock(self): + """Lock the mailbox.""" + raise NotImplementedError('Method must be implemented by subclass') + + def unlock(self): + """Unlock the mailbox if it is locked.""" + raise NotImplementedError('Method must be implemented by subclass') + + def close(self): + """Flush and close the mailbox.""" + raise NotImplementedError('Method must be implemented by subclass') + + def _string_to_bytes(self, message): + # If a message is not 7bit clean, we refuse to handle it since it + # likely came from reading invalid messages in text mode, and that way + # lies mojibake. + try: + return message.encode('ascii') + except UnicodeError: + raise ValueError("String input must be ASCII-only; " + "use bytes or a Message instead") + + # Whether each message must end in a newline + _append_newline = False + + def _dump_message(self, message, target, mangle_from_=False): + # This assumes the target file is open in binary mode. + """Dump message contents to target file.""" + if isinstance(message, email.message.Message): + buffer = io.BytesIO() + gen = email.generator.BytesGenerator(buffer, mangle_from_, 0) + gen.flatten(message) + buffer.seek(0) + data = buffer.read() + data = data.replace(b'\n', linesep) + target.write(data) + if self._append_newline and not data.endswith(linesep): + # Make sure the message ends with a newline + target.write(linesep) + elif isinstance(message, (str, bytes, io.StringIO)): + if isinstance(message, io.StringIO): + warnings.warn("Use of StringIO input is deprecated, " + "use BytesIO instead", DeprecationWarning, 3) + message = message.getvalue() + if isinstance(message, str): + message = self._string_to_bytes(message) + if mangle_from_: + message = message.replace(b'\nFrom ', b'\n>From ') + message = message.replace(b'\n', linesep) + target.write(message) + if self._append_newline and not message.endswith(linesep): + # Make sure the message ends with a newline + target.write(linesep) + elif hasattr(message, 'read'): + if hasattr(message, 'buffer'): + warnings.warn("Use of text mode files is deprecated, " + "use a binary mode file instead", DeprecationWarning, 3) + message = message.buffer + lastline = None + while True: + line = message.readline() + # Universal newline support. + if line.endswith(b'\r\n'): + line = line[:-2] + b'\n' + elif line.endswith(b'\r'): + line = line[:-1] + b'\n' + if not line: + break + if mangle_from_ and line.startswith(b'From '): + line = b'>From ' + line[5:] + line = line.replace(b'\n', linesep) + target.write(line) + lastline = line + if self._append_newline and lastline and not lastline.endswith(linesep): + # Make sure the message ends with a newline + target.write(linesep) + else: + raise TypeError('Invalid message type: %s' % type(message)) + + +class Maildir(Mailbox): + """A qmail-style Maildir mailbox.""" + + colon = ':' + + def __init__(self, dirname, factory=None, create=True): + """Initialize a Maildir instance.""" + Mailbox.__init__(self, dirname, factory, create) + self._paths = { + 'tmp': os.path.join(self._path, 'tmp'), + 'new': os.path.join(self._path, 'new'), + 'cur': os.path.join(self._path, 'cur'), + } + if not os.path.exists(self._path): + if create: + os.mkdir(self._path, 0o700) + for path in self._paths.values(): + os.mkdir(path, 0o700) + else: + raise NoSuchMailboxError(self._path) + self._toc = {} + self._toc_mtimes = {'cur': 0, 'new': 0} + self._last_read = 0 # Records last time we read cur/new + self._skewfactor = 0.1 # Adjust if os/fs clocks are skewing + + def add(self, message): + """Add message and return assigned key.""" + tmp_file = self._create_tmp() + try: + self._dump_message(message, tmp_file) + except BaseException: + tmp_file.close() + os.remove(tmp_file.name) + raise + _sync_close(tmp_file) + if isinstance(message, MaildirMessage): + subdir = message.get_subdir() + suffix = self.colon + message.get_info() + if suffix == self.colon: + suffix = '' + else: + subdir = 'new' + suffix = '' + uniq = os.path.basename(tmp_file.name).split(self.colon)[0] + dest = os.path.join(self._path, subdir, uniq + suffix) + if isinstance(message, MaildirMessage): + os.utime(tmp_file.name, + (os.path.getatime(tmp_file.name), message.get_date())) + # No file modification should be done after the file is moved to its + # final position in order to prevent race conditions with changes + # from other programs + try: + try: + os.link(tmp_file.name, dest) + except (AttributeError, PermissionError): + os.rename(tmp_file.name, dest) + else: + os.remove(tmp_file.name) + except OSError as e: + os.remove(tmp_file.name) + if e.errno == errno.EEXIST: + raise ExternalClashError('Name clash with existing message: %s' + % dest) + else: + raise + return uniq + + def remove(self, key): + """Remove the keyed message; raise KeyError if it doesn't exist.""" + os.remove(os.path.join(self._path, self._lookup(key))) + + def discard(self, key): + """If the keyed message exists, remove it.""" + # This overrides an inapplicable implementation in the superclass. + try: + self.remove(key) + except (KeyError, FileNotFoundError): + pass + + def __setitem__(self, key, message): + """Replace the keyed message; raise KeyError if it doesn't exist.""" + old_subpath = self._lookup(key) + temp_key = self.add(message) + temp_subpath = self._lookup(temp_key) + if isinstance(message, MaildirMessage): + # temp's subdir and suffix were specified by message. + dominant_subpath = temp_subpath + else: + # temp's subdir and suffix were defaults from add(). + dominant_subpath = old_subpath + subdir = os.path.dirname(dominant_subpath) + if self.colon in dominant_subpath: + suffix = self.colon + dominant_subpath.split(self.colon)[-1] + else: + suffix = '' + self.discard(key) + tmp_path = os.path.join(self._path, temp_subpath) + new_path = os.path.join(self._path, subdir, key + suffix) + if isinstance(message, MaildirMessage): + os.utime(tmp_path, + (os.path.getatime(tmp_path), message.get_date())) + # No file modification should be done after the file is moved to its + # final position in order to prevent race conditions with changes + # from other programs + os.rename(tmp_path, new_path) + + def get_message(self, key): + """Return a Message representation or raise a KeyError.""" + subpath = self._lookup(key) + with open(os.path.join(self._path, subpath), 'rb') as f: + if self._factory: + msg = self._factory(f) + else: + msg = MaildirMessage(f) + subdir, name = os.path.split(subpath) + msg.set_subdir(subdir) + if self.colon in name: + msg.set_info(name.split(self.colon)[-1]) + msg.set_date(os.path.getmtime(os.path.join(self._path, subpath))) + return msg + + def get_bytes(self, key): + """Return a bytes representation or raise a KeyError.""" + with open(os.path.join(self._path, self._lookup(key)), 'rb') as f: + return f.read().replace(linesep, b'\n') + + def get_file(self, key): + """Return a file-like representation or raise a KeyError.""" + f = open(os.path.join(self._path, self._lookup(key)), 'rb') + return _ProxyFile(f) + + def iterkeys(self): + """Return an iterator over keys.""" + self._refresh() + for key in self._toc: + try: + self._lookup(key) + except KeyError: + continue + yield key + + def __contains__(self, key): + """Return True if the keyed message exists, False otherwise.""" + self._refresh() + return key in self._toc + + def __len__(self): + """Return a count of messages in the mailbox.""" + self._refresh() + return len(self._toc) + + def flush(self): + """Write any pending changes to disk.""" + # Maildir changes are always written immediately, so there's nothing + # to do. + pass + + def lock(self): + """Lock the mailbox.""" + return + + def unlock(self): + """Unlock the mailbox if it is locked.""" + return + + def close(self): + """Flush and close the mailbox.""" + return + + def list_folders(self): + """Return a list of folder names.""" + result = [] + for entry in os.listdir(self._path): + if len(entry) > 1 and entry[0] == '.' and \ + os.path.isdir(os.path.join(self._path, entry)): + result.append(entry[1:]) + return result + + def get_folder(self, folder): + """Return a Maildir instance for the named folder.""" + return Maildir(os.path.join(self._path, '.' + folder), + factory=self._factory, + create=False) + + def add_folder(self, folder): + """Create a folder and return a Maildir instance representing it.""" + path = os.path.join(self._path, '.' + folder) + result = Maildir(path, factory=self._factory) + maildirfolder_path = os.path.join(path, 'maildirfolder') + if not os.path.exists(maildirfolder_path): + os.close(os.open(maildirfolder_path, os.O_CREAT | os.O_WRONLY, + 0o666)) + return result + + def remove_folder(self, folder): + """Delete the named folder, which must be empty.""" + path = os.path.join(self._path, '.' + folder) + for entry in os.listdir(os.path.join(path, 'new')) + \ + os.listdir(os.path.join(path, 'cur')): + if len(entry) < 1 or entry[0] != '.': + raise NotEmptyError('Folder contains message(s): %s' % folder) + for entry in os.listdir(path): + if entry != 'new' and entry != 'cur' and entry != 'tmp' and \ + os.path.isdir(os.path.join(path, entry)): + raise NotEmptyError("Folder contains subdirectory '%s': %s" % + (folder, entry)) + for root, dirs, files in os.walk(path, topdown=False): + for entry in files: + os.remove(os.path.join(root, entry)) + for entry in dirs: + os.rmdir(os.path.join(root, entry)) + os.rmdir(path) + + def clean(self): + """Delete old files in "tmp".""" + now = time.time() + for entry in os.listdir(os.path.join(self._path, 'tmp')): + path = os.path.join(self._path, 'tmp', entry) + if now - os.path.getatime(path) > 129600: # 60 * 60 * 36 + os.remove(path) + + _count = 1 # This is used to generate unique file names. + + def _create_tmp(self): + """Create a file in the tmp subdirectory and open and return it.""" + now = time.time() + hostname = socket.gethostname() + if '/' in hostname: + hostname = hostname.replace('/', r'\057') + if ':' in hostname: + hostname = hostname.replace(':', r'\072') + uniq = "%s.M%sP%sQ%s.%s" % (int(now), int(now % 1 * 1e6), os.getpid(), + Maildir._count, hostname) + path = os.path.join(self._path, 'tmp', uniq) + try: + os.stat(path) + except FileNotFoundError: + Maildir._count += 1 + try: + return _create_carefully(path) + except FileExistsError: + pass + + # Fall through to here if stat succeeded or open raised EEXIST. + raise ExternalClashError('Name clash prevented file creation: %s' % + path) + + def _refresh(self): + """Update table of contents mapping.""" + # If it has been less than two seconds since the last _refresh() call, + # we have to unconditionally re-read the mailbox just in case it has + # been modified, because os.path.mtime() has a 2 sec resolution in the + # most common worst case (FAT) and a 1 sec resolution typically. This + # results in a few unnecessary re-reads when _refresh() is called + # multiple times in that interval, but once the clock ticks over, we + # will only re-read as needed. Because the filesystem might be being + # served by an independent system with its own clock, we record and + # compare with the mtimes from the filesystem. Because the other + # system's clock might be skewing relative to our clock, we add an + # extra delta to our wait. The default is one tenth second, but is an + # instance variable and so can be adjusted if dealing with a + # particularly skewed or irregular system. + if time.time() - self._last_read > 2 + self._skewfactor: + refresh = False + for subdir in self._toc_mtimes: + mtime = os.path.getmtime(self._paths[subdir]) + if mtime > self._toc_mtimes[subdir]: + refresh = True + self._toc_mtimes[subdir] = mtime + if not refresh: + return + # Refresh toc + self._toc = {} + for subdir in self._toc_mtimes: + path = self._paths[subdir] + for entry in os.listdir(path): + p = os.path.join(path, entry) + if os.path.isdir(p): + continue + uniq = entry.split(self.colon)[0] + self._toc[uniq] = os.path.join(subdir, entry) + self._last_read = time.time() + + def _lookup(self, key): + """Use TOC to return subpath for given key, or raise a KeyError.""" + try: + if os.path.exists(os.path.join(self._path, self._toc[key])): + return self._toc[key] + except KeyError: + pass + self._refresh() + try: + return self._toc[key] + except KeyError: + raise KeyError('No message with key: %s' % key) from None + + # This method is for backward compatibility only. + def next(self): + """Return the next message in a one-time iteration.""" + if not hasattr(self, '_onetime_keys'): + self._onetime_keys = self.iterkeys() + while True: + try: + return self[next(self._onetime_keys)] + except StopIteration: + return None + except KeyError: + continue + + +class _singlefileMailbox(Mailbox): + """A single-file mailbox.""" + + def __init__(self, path, factory=None, create=True): + """Initialize a single-file mailbox.""" + Mailbox.__init__(self, path, factory, create) + try: + f = open(self._path, 'rb+') + except OSError as e: + if e.errno == errno.ENOENT: + if create: + f = open(self._path, 'wb+') + else: + raise NoSuchMailboxError(self._path) + elif e.errno in (errno.EACCES, errno.EROFS): + f = open(self._path, 'rb') + else: + raise + self._file = f + self._toc = None + self._next_key = 0 + self._pending = False # No changes require rewriting the file. + self._pending_sync = False # No need to sync the file + self._locked = False + self._file_length = None # Used to record mailbox size + + def add(self, message): + """Add message and return assigned key.""" + self._lookup() + self._toc[self._next_key] = self._append_message(message) + self._next_key += 1 + # _append_message appends the message to the mailbox file. We + # don't need a full rewrite + rename, sync is enough. + self._pending_sync = True + return self._next_key - 1 + + def remove(self, key): + """Remove the keyed message; raise KeyError if it doesn't exist.""" + self._lookup(key) + del self._toc[key] + self._pending = True + + def __setitem__(self, key, message): + """Replace the keyed message; raise KeyError if it doesn't exist.""" + self._lookup(key) + self._toc[key] = self._append_message(message) + self._pending = True + + def iterkeys(self): + """Return an iterator over keys.""" + self._lookup() + yield from self._toc.keys() + + def __contains__(self, key): + """Return True if the keyed message exists, False otherwise.""" + self._lookup() + return key in self._toc + + def __len__(self): + """Return a count of messages in the mailbox.""" + self._lookup() + return len(self._toc) + + def lock(self): + """Lock the mailbox.""" + if not self._locked: + _lock_file(self._file) + self._locked = True + + def unlock(self): + """Unlock the mailbox if it is locked.""" + if self._locked: + _unlock_file(self._file) + self._locked = False + + def flush(self): + """Write any pending changes to disk.""" + if not self._pending: + if self._pending_sync: + # Messages have only been added, so syncing the file + # is enough. + _sync_flush(self._file) + self._pending_sync = False + return + + # In order to be writing anything out at all, self._toc must + # already have been generated (and presumably has been modified + # by adding or deleting an item). + assert self._toc is not None + + # Check length of self._file; if it's changed, some other process + # has modified the mailbox since we scanned it. + self._file.seek(0, 2) + cur_len = self._file.tell() + if cur_len != self._file_length: + raise ExternalClashError('Size of mailbox file changed ' + '(expected %i, found %i)' % + (self._file_length, cur_len)) + + new_file = _create_temporary(self._path) + try: + new_toc = {} + self._pre_mailbox_hook(new_file) + for key in sorted(self._toc.keys()): + start, stop = self._toc[key] + self._file.seek(start) + self._pre_message_hook(new_file) + new_start = new_file.tell() + while True: + buffer = self._file.read(min(4096, + stop - self._file.tell())) + if not buffer: + break + new_file.write(buffer) + new_toc[key] = (new_start, new_file.tell()) + self._post_message_hook(new_file) + self._file_length = new_file.tell() + except: + new_file.close() + os.remove(new_file.name) + raise + _sync_close(new_file) + # self._file is about to get replaced, so no need to sync. + self._file.close() + # Make sure the new file's mode is the same as the old file's + mode = os.stat(self._path).st_mode + os.chmod(new_file.name, mode) + try: + os.rename(new_file.name, self._path) + except FileExistsError: + os.remove(self._path) + os.rename(new_file.name, self._path) + self._file = open(self._path, 'rb+') + self._toc = new_toc + self._pending = False + self._pending_sync = False + if self._locked: + _lock_file(self._file, dotlock=False) + + def _pre_mailbox_hook(self, f): + """Called before writing the mailbox to file f.""" + return + + def _pre_message_hook(self, f): + """Called before writing each message to file f.""" + return + + def _post_message_hook(self, f): + """Called after writing each message to file f.""" + return + + def close(self): + """Flush and close the mailbox.""" + try: + self.flush() + finally: + try: + if self._locked: + self.unlock() + finally: + self._file.close() # Sync has been done by self.flush() above. + + def _lookup(self, key=None): + """Return (start, stop) or raise KeyError.""" + if self._toc is None: + self._generate_toc() + if key is not None: + try: + return self._toc[key] + except KeyError: + raise KeyError('No message with key: %s' % key) from None + + def _append_message(self, message): + """Append message to mailbox and return (start, stop) offsets.""" + self._file.seek(0, 2) + before = self._file.tell() + if len(self._toc) == 0 and not self._pending: + # This is the first message, and the _pre_mailbox_hook + # hasn't yet been called. If self._pending is True, + # messages have been removed, so _pre_mailbox_hook must + # have been called already. + self._pre_mailbox_hook(self._file) + try: + self._pre_message_hook(self._file) + offsets = self._install_message(message) + self._post_message_hook(self._file) + except BaseException: + self._file.truncate(before) + raise + self._file.flush() + self._file_length = self._file.tell() # Record current length of mailbox + return offsets + + + +class _mboxMMDF(_singlefileMailbox): + """An mbox or MMDF mailbox.""" + + _mangle_from_ = True + + def get_message(self, key): + """Return a Message representation or raise a KeyError.""" + start, stop = self._lookup(key) + self._file.seek(start) + from_line = self._file.readline().replace(linesep, b'') + string = self._file.read(stop - self._file.tell()) + msg = self._message_factory(string.replace(linesep, b'\n')) + msg.set_from(from_line[5:].decode('ascii')) + return msg + + def get_string(self, key, from_=False): + """Return a string representation or raise a KeyError.""" + return email.message_from_bytes( + self.get_bytes(key, from_)).as_string(unixfrom=from_) + + def get_bytes(self, key, from_=False): + """Return a string representation or raise a KeyError.""" + start, stop = self._lookup(key) + self._file.seek(start) + if not from_: + self._file.readline() + string = self._file.read(stop - self._file.tell()) + return string.replace(linesep, b'\n') + + def get_file(self, key, from_=False): + """Return a file-like representation or raise a KeyError.""" + start, stop = self._lookup(key) + self._file.seek(start) + if not from_: + self._file.readline() + return _PartialFile(self._file, self._file.tell(), stop) + + def _install_message(self, message): + """Format a message and blindly write to self._file.""" + from_line = None + if isinstance(message, str): + message = self._string_to_bytes(message) + if isinstance(message, bytes) and message.startswith(b'From '): + newline = message.find(b'\n') + if newline != -1: + from_line = message[:newline] + message = message[newline + 1:] + else: + from_line = message + message = b'' + elif isinstance(message, _mboxMMDFMessage): + author = message.get_from().encode('ascii') + from_line = b'From ' + author + elif isinstance(message, email.message.Message): + from_line = message.get_unixfrom() # May be None. + if from_line is not None: + from_line = from_line.encode('ascii') + if from_line is None: + from_line = b'From MAILER-DAEMON ' + time.asctime(time.gmtime()).encode() + start = self._file.tell() + self._file.write(from_line + linesep) + self._dump_message(message, self._file, self._mangle_from_) + stop = self._file.tell() + return (start, stop) + + +class mbox(_mboxMMDF): + """A classic mbox mailbox.""" + + _mangle_from_ = True + + # All messages must end in a newline character, and + # _post_message_hooks outputs an empty line between messages. + _append_newline = True + + def __init__(self, path, factory=None, create=True): + """Initialize an mbox mailbox.""" + self._message_factory = mboxMessage + _mboxMMDF.__init__(self, path, factory, create) + + def _post_message_hook(self, f): + """Called after writing each message to file f.""" + f.write(linesep) + + def _generate_toc(self): + """Generate key-to-(start, stop) table of contents.""" + starts, stops = [], [] + last_was_empty = False + self._file.seek(0) + while True: + line_pos = self._file.tell() + line = self._file.readline() + if line.startswith(b'From '): + if len(stops) < len(starts): + if last_was_empty: + stops.append(line_pos - len(linesep)) + else: + # The last line before the "From " line wasn't + # blank, but we consider it a start of a + # message anyway. + stops.append(line_pos) + starts.append(line_pos) + last_was_empty = False + elif not line: + if last_was_empty: + stops.append(line_pos - len(linesep)) + else: + stops.append(line_pos) + break + elif line == linesep: + last_was_empty = True + else: + last_was_empty = False + self._toc = dict(enumerate(zip(starts, stops))) + self._next_key = len(self._toc) + self._file_length = self._file.tell() + + +class MMDF(_mboxMMDF): + """An MMDF mailbox.""" + + def __init__(self, path, factory=None, create=True): + """Initialize an MMDF mailbox.""" + self._message_factory = MMDFMessage + _mboxMMDF.__init__(self, path, factory, create) + + def _pre_message_hook(self, f): + """Called before writing each message to file f.""" + f.write(b'\001\001\001\001' + linesep) + + def _post_message_hook(self, f): + """Called after writing each message to file f.""" + f.write(linesep + b'\001\001\001\001' + linesep) + + def _generate_toc(self): + """Generate key-to-(start, stop) table of contents.""" + starts, stops = [], [] + self._file.seek(0) + next_pos = 0 + while True: + line_pos = next_pos + line = self._file.readline() + next_pos = self._file.tell() + if line.startswith(b'\001\001\001\001' + linesep): + starts.append(next_pos) + while True: + line_pos = next_pos + line = self._file.readline() + next_pos = self._file.tell() + if line == b'\001\001\001\001' + linesep: + stops.append(line_pos - len(linesep)) + break + elif not line: + stops.append(line_pos) + break + elif not line: + break + self._toc = dict(enumerate(zip(starts, stops))) + self._next_key = len(self._toc) + self._file.seek(0, 2) + self._file_length = self._file.tell() + + +class MH(Mailbox): + """An MH mailbox.""" + + def __init__(self, path, factory=None, create=True): + """Initialize an MH instance.""" + Mailbox.__init__(self, path, factory, create) + if not os.path.exists(self._path): + if create: + os.mkdir(self._path, 0o700) + os.close(os.open(os.path.join(self._path, '.mh_sequences'), + os.O_CREAT | os.O_EXCL | os.O_WRONLY, 0o600)) + else: + raise NoSuchMailboxError(self._path) + self._locked = False + + def add(self, message): + """Add message and return assigned key.""" + keys = self.keys() + if len(keys) == 0: + new_key = 1 + else: + new_key = max(keys) + 1 + new_path = os.path.join(self._path, str(new_key)) + f = _create_carefully(new_path) + closed = False + try: + if self._locked: + _lock_file(f) + try: + try: + self._dump_message(message, f) + except BaseException: + # Unlock and close so it can be deleted on Windows + if self._locked: + _unlock_file(f) + _sync_close(f) + closed = True + os.remove(new_path) + raise + if isinstance(message, MHMessage): + self._dump_sequences(message, new_key) + finally: + if self._locked: + _unlock_file(f) + finally: + if not closed: + _sync_close(f) + return new_key + + def remove(self, key): + """Remove the keyed message; raise KeyError if it doesn't exist.""" + path = os.path.join(self._path, str(key)) + try: + f = open(path, 'rb+') + except OSError as e: + if e.errno == errno.ENOENT: + raise KeyError('No message with key: %s' % key) + else: + raise + else: + f.close() + os.remove(path) + + def __setitem__(self, key, message): + """Replace the keyed message; raise KeyError if it doesn't exist.""" + path = os.path.join(self._path, str(key)) + try: + f = open(path, 'rb+') + except OSError as e: + if e.errno == errno.ENOENT: + raise KeyError('No message with key: %s' % key) + else: + raise + try: + if self._locked: + _lock_file(f) + try: + os.close(os.open(path, os.O_WRONLY | os.O_TRUNC)) + self._dump_message(message, f) + if isinstance(message, MHMessage): + self._dump_sequences(message, key) + finally: + if self._locked: + _unlock_file(f) + finally: + _sync_close(f) + + def get_message(self, key): + """Return a Message representation or raise a KeyError.""" + try: + if self._locked: + f = open(os.path.join(self._path, str(key)), 'rb+') + else: + f = open(os.path.join(self._path, str(key)), 'rb') + except OSError as e: + if e.errno == errno.ENOENT: + raise KeyError('No message with key: %s' % key) + else: + raise + with f: + if self._locked: + _lock_file(f) + try: + msg = MHMessage(f) + finally: + if self._locked: + _unlock_file(f) + for name, key_list in self.get_sequences().items(): + if key in key_list: + msg.add_sequence(name) + return msg + + def get_bytes(self, key): + """Return a bytes representation or raise a KeyError.""" + try: + if self._locked: + f = open(os.path.join(self._path, str(key)), 'rb+') + else: + f = open(os.path.join(self._path, str(key)), 'rb') + except OSError as e: + if e.errno == errno.ENOENT: + raise KeyError('No message with key: %s' % key) + else: + raise + with f: + if self._locked: + _lock_file(f) + try: + return f.read().replace(linesep, b'\n') + finally: + if self._locked: + _unlock_file(f) + + def get_file(self, key): + """Return a file-like representation or raise a KeyError.""" + try: + f = open(os.path.join(self._path, str(key)), 'rb') + except OSError as e: + if e.errno == errno.ENOENT: + raise KeyError('No message with key: %s' % key) + else: + raise + return _ProxyFile(f) + + def iterkeys(self): + """Return an iterator over keys.""" + return iter(sorted(int(entry) for entry in os.listdir(self._path) + if entry.isdigit())) + + def __contains__(self, key): + """Return True if the keyed message exists, False otherwise.""" + return os.path.exists(os.path.join(self._path, str(key))) + + def __len__(self): + """Return a count of messages in the mailbox.""" + return len(list(self.iterkeys())) + + def lock(self): + """Lock the mailbox.""" + if not self._locked: + self._file = open(os.path.join(self._path, '.mh_sequences'), 'rb+') + _lock_file(self._file) + self._locked = True + + def unlock(self): + """Unlock the mailbox if it is locked.""" + if self._locked: + _unlock_file(self._file) + _sync_close(self._file) + del self._file + self._locked = False + + def flush(self): + """Write any pending changes to the disk.""" + return + + def close(self): + """Flush and close the mailbox.""" + if self._locked: + self.unlock() + + def list_folders(self): + """Return a list of folder names.""" + result = [] + for entry in os.listdir(self._path): + if os.path.isdir(os.path.join(self._path, entry)): + result.append(entry) + return result + + def get_folder(self, folder): + """Return an MH instance for the named folder.""" + return MH(os.path.join(self._path, folder), + factory=self._factory, create=False) + + def add_folder(self, folder): + """Create a folder and return an MH instance representing it.""" + return MH(os.path.join(self._path, folder), + factory=self._factory) + + def remove_folder(self, folder): + """Delete the named folder, which must be empty.""" + path = os.path.join(self._path, folder) + entries = os.listdir(path) + if entries == ['.mh_sequences']: + os.remove(os.path.join(path, '.mh_sequences')) + elif entries == []: + pass + else: + raise NotEmptyError('Folder not empty: %s' % self._path) + os.rmdir(path) + + def get_sequences(self): + """Return a name-to-key-list dictionary to define each sequence.""" + results = {} + with open(os.path.join(self._path, '.mh_sequences'), 'r', encoding='ASCII') as f: + all_keys = set(self.keys()) + for line in f: + try: + name, contents = line.split(':') + keys = set() + for spec in contents.split(): + if spec.isdigit(): + keys.add(int(spec)) + else: + start, stop = (int(x) for x in spec.split('-')) + keys.update(range(start, stop + 1)) + results[name] = [key for key in sorted(keys) \ + if key in all_keys] + if len(results[name]) == 0: + del results[name] + except ValueError: + raise FormatError('Invalid sequence specification: %s' % + line.rstrip()) + return results + + def set_sequences(self, sequences): + """Set sequences using the given name-to-key-list dictionary.""" + f = open(os.path.join(self._path, '.mh_sequences'), 'r+', encoding='ASCII') + try: + os.close(os.open(f.name, os.O_WRONLY | os.O_TRUNC)) + for name, keys in sequences.items(): + if len(keys) == 0: + continue + f.write(name + ':') + prev = None + completing = False + for key in sorted(set(keys)): + if key - 1 == prev: + if not completing: + completing = True + f.write('-') + elif completing: + completing = False + f.write('%s %s' % (prev, key)) + else: + f.write(' %s' % key) + prev = key + if completing: + f.write(str(prev) + '\n') + else: + f.write('\n') + finally: + _sync_close(f) + + def pack(self): + """Re-name messages to eliminate numbering gaps. Invalidates keys.""" + sequences = self.get_sequences() + prev = 0 + changes = [] + for key in self.iterkeys(): + if key - 1 != prev: + changes.append((key, prev + 1)) + try: + os.link(os.path.join(self._path, str(key)), + os.path.join(self._path, str(prev + 1))) + except (AttributeError, PermissionError): + os.rename(os.path.join(self._path, str(key)), + os.path.join(self._path, str(prev + 1))) + else: + os.unlink(os.path.join(self._path, str(key))) + prev += 1 + self._next_key = prev + 1 + if len(changes) == 0: + return + for name, key_list in sequences.items(): + for old, new in changes: + if old in key_list: + key_list[key_list.index(old)] = new + self.set_sequences(sequences) + + def _dump_sequences(self, message, key): + """Inspect a new MHMessage and update sequences appropriately.""" + pending_sequences = message.get_sequences() + all_sequences = self.get_sequences() + for name, key_list in all_sequences.items(): + if name in pending_sequences: + key_list.append(key) + elif key in key_list: + del key_list[key_list.index(key)] + for sequence in pending_sequences: + if sequence not in all_sequences: + all_sequences[sequence] = [key] + self.set_sequences(all_sequences) + + +class Babyl(_singlefileMailbox): + """An Rmail-style Babyl mailbox.""" + + _special_labels = frozenset({'unseen', 'deleted', 'filed', 'answered', + 'forwarded', 'edited', 'resent'}) + + def __init__(self, path, factory=None, create=True): + """Initialize a Babyl mailbox.""" + _singlefileMailbox.__init__(self, path, factory, create) + self._labels = {} + + def add(self, message): + """Add message and return assigned key.""" + key = _singlefileMailbox.add(self, message) + if isinstance(message, BabylMessage): + self._labels[key] = message.get_labels() + return key + + def remove(self, key): + """Remove the keyed message; raise KeyError if it doesn't exist.""" + _singlefileMailbox.remove(self, key) + if key in self._labels: + del self._labels[key] + + def __setitem__(self, key, message): + """Replace the keyed message; raise KeyError if it doesn't exist.""" + _singlefileMailbox.__setitem__(self, key, message) + if isinstance(message, BabylMessage): + self._labels[key] = message.get_labels() + + def get_message(self, key): + """Return a Message representation or raise a KeyError.""" + start, stop = self._lookup(key) + self._file.seek(start) + self._file.readline() # Skip b'1,' line specifying labels. + original_headers = io.BytesIO() + while True: + line = self._file.readline() + if line == b'*** EOOH ***' + linesep or not line: + break + original_headers.write(line.replace(linesep, b'\n')) + visible_headers = io.BytesIO() + while True: + line = self._file.readline() + if line == linesep or not line: + break + visible_headers.write(line.replace(linesep, b'\n')) + # Read up to the stop, or to the end + n = stop - self._file.tell() + assert n >= 0 + body = self._file.read(n) + body = body.replace(linesep, b'\n') + msg = BabylMessage(original_headers.getvalue() + body) + msg.set_visible(visible_headers.getvalue()) + if key in self._labels: + msg.set_labels(self._labels[key]) + return msg + + def get_bytes(self, key): + """Return a string representation or raise a KeyError.""" + start, stop = self._lookup(key) + self._file.seek(start) + self._file.readline() # Skip b'1,' line specifying labels. + original_headers = io.BytesIO() + while True: + line = self._file.readline() + if line == b'*** EOOH ***' + linesep or not line: + break + original_headers.write(line.replace(linesep, b'\n')) + while True: + line = self._file.readline() + if line == linesep or not line: + break + headers = original_headers.getvalue() + n = stop - self._file.tell() + assert n >= 0 + data = self._file.read(n) + data = data.replace(linesep, b'\n') + return headers + data + + def get_file(self, key): + """Return a file-like representation or raise a KeyError.""" + return io.BytesIO(self.get_bytes(key).replace(b'\n', linesep)) + + def get_labels(self): + """Return a list of user-defined labels in the mailbox.""" + self._lookup() + labels = set() + for label_list in self._labels.values(): + labels.update(label_list) + labels.difference_update(self._special_labels) + return list(labels) + + def _generate_toc(self): + """Generate key-to-(start, stop) table of contents.""" + starts, stops = [], [] + self._file.seek(0) + next_pos = 0 + label_lists = [] + while True: + line_pos = next_pos + line = self._file.readline() + next_pos = self._file.tell() + if line == b'\037\014' + linesep: + if len(stops) < len(starts): + stops.append(line_pos - len(linesep)) + starts.append(next_pos) + labels = [label.strip() for label + in self._file.readline()[1:].split(b',') + if label.strip()] + label_lists.append(labels) + elif line == b'\037' or line == b'\037' + linesep: + if len(stops) < len(starts): + stops.append(line_pos - len(linesep)) + elif not line: + stops.append(line_pos - len(linesep)) + break + self._toc = dict(enumerate(zip(starts, stops))) + self._labels = dict(enumerate(label_lists)) + self._next_key = len(self._toc) + self._file.seek(0, 2) + self._file_length = self._file.tell() + + def _pre_mailbox_hook(self, f): + """Called before writing the mailbox to file f.""" + babyl = b'BABYL OPTIONS:' + linesep + babyl += b'Version: 5' + linesep + labels = self.get_labels() + labels = (label.encode() for label in labels) + babyl += b'Labels:' + b','.join(labels) + linesep + babyl += b'\037' + f.write(babyl) + + def _pre_message_hook(self, f): + """Called before writing each message to file f.""" + f.write(b'\014' + linesep) + + def _post_message_hook(self, f): + """Called after writing each message to file f.""" + f.write(linesep + b'\037') + + def _install_message(self, message): + """Write message contents and return (start, stop).""" + start = self._file.tell() + if isinstance(message, BabylMessage): + special_labels = [] + labels = [] + for label in message.get_labels(): + if label in self._special_labels: + special_labels.append(label) + else: + labels.append(label) + self._file.write(b'1') + for label in special_labels: + self._file.write(b', ' + label.encode()) + self._file.write(b',,') + for label in labels: + self._file.write(b' ' + label.encode() + b',') + self._file.write(linesep) + else: + self._file.write(b'1,,' + linesep) + if isinstance(message, email.message.Message): + orig_buffer = io.BytesIO() + orig_generator = email.generator.BytesGenerator(orig_buffer, False, 0) + orig_generator.flatten(message) + orig_buffer.seek(0) + while True: + line = orig_buffer.readline() + self._file.write(line.replace(b'\n', linesep)) + if line == b'\n' or not line: + break + self._file.write(b'*** EOOH ***' + linesep) + if isinstance(message, BabylMessage): + vis_buffer = io.BytesIO() + vis_generator = email.generator.BytesGenerator(vis_buffer, False, 0) + vis_generator.flatten(message.get_visible()) + while True: + line = vis_buffer.readline() + self._file.write(line.replace(b'\n', linesep)) + if line == b'\n' or not line: + break + else: + orig_buffer.seek(0) + while True: + line = orig_buffer.readline() + self._file.write(line.replace(b'\n', linesep)) + if line == b'\n' or not line: + break + while True: + buffer = orig_buffer.read(4096) # Buffer size is arbitrary. + if not buffer: + break + self._file.write(buffer.replace(b'\n', linesep)) + elif isinstance(message, (bytes, str, io.StringIO)): + if isinstance(message, io.StringIO): + warnings.warn("Use of StringIO input is deprecated, " + "use BytesIO instead", DeprecationWarning, 3) + message = message.getvalue() + if isinstance(message, str): + message = self._string_to_bytes(message) + body_start = message.find(b'\n\n') + 2 + if body_start - 2 != -1: + self._file.write(message[:body_start].replace(b'\n', linesep)) + self._file.write(b'*** EOOH ***' + linesep) + self._file.write(message[:body_start].replace(b'\n', linesep)) + self._file.write(message[body_start:].replace(b'\n', linesep)) + else: + self._file.write(b'*** EOOH ***' + linesep + linesep) + self._file.write(message.replace(b'\n', linesep)) + elif hasattr(message, 'readline'): + if hasattr(message, 'buffer'): + warnings.warn("Use of text mode files is deprecated, " + "use a binary mode file instead", DeprecationWarning, 3) + message = message.buffer + original_pos = message.tell() + first_pass = True + while True: + line = message.readline() + # Universal newline support. + if line.endswith(b'\r\n'): + line = line[:-2] + b'\n' + elif line.endswith(b'\r'): + line = line[:-1] + b'\n' + self._file.write(line.replace(b'\n', linesep)) + if line == b'\n' or not line: + if first_pass: + first_pass = False + self._file.write(b'*** EOOH ***' + linesep) + message.seek(original_pos) + else: + break + while True: + line = message.readline() + if not line: + break + # Universal newline support. + if line.endswith(b'\r\n'): + line = line[:-2] + linesep + elif line.endswith(b'\r'): + line = line[:-1] + linesep + elif line.endswith(b'\n'): + line = line[:-1] + linesep + self._file.write(line) + else: + raise TypeError('Invalid message type: %s' % type(message)) + stop = self._file.tell() + return (start, stop) + + +class Message(email.message.Message): + """Message with mailbox-format-specific properties.""" + + def __init__(self, message=None): + """Initialize a Message instance.""" + if isinstance(message, email.message.Message): + self._become_message(copy.deepcopy(message)) + if isinstance(message, Message): + message._explain_to(self) + elif isinstance(message, bytes): + self._become_message(email.message_from_bytes(message)) + elif isinstance(message, str): + self._become_message(email.message_from_string(message)) + elif isinstance(message, io.TextIOWrapper): + self._become_message(email.message_from_file(message)) + elif hasattr(message, "read"): + self._become_message(email.message_from_binary_file(message)) + elif message is None: + email.message.Message.__init__(self) + else: + raise TypeError('Invalid message type: %s' % type(message)) + + def _become_message(self, message): + """Assume the non-format-specific state of message.""" + type_specific = getattr(message, '_type_specific_attributes', []) + for name in message.__dict__: + if name not in type_specific: + self.__dict__[name] = message.__dict__[name] + + def _explain_to(self, message): + """Copy format-specific state to message insofar as possible.""" + if isinstance(message, Message): + return # There's nothing format-specific to explain. + else: + raise TypeError('Cannot convert to specified type') + + +class MaildirMessage(Message): + """Message with Maildir-specific properties.""" + + _type_specific_attributes = ['_subdir', '_info', '_date'] + + def __init__(self, message=None): + """Initialize a MaildirMessage instance.""" + self._subdir = 'new' + self._info = '' + self._date = time.time() + Message.__init__(self, message) + + def get_subdir(self): + """Return 'new' or 'cur'.""" + return self._subdir + + def set_subdir(self, subdir): + """Set subdir to 'new' or 'cur'.""" + if subdir == 'new' or subdir == 'cur': + self._subdir = subdir + else: + raise ValueError("subdir must be 'new' or 'cur': %s" % subdir) + + def get_flags(self): + """Return as a string the flags that are set.""" + if self._info.startswith('2,'): + return self._info[2:] + else: + return '' + + def set_flags(self, flags): + """Set the given flags and unset all others.""" + self._info = '2,' + ''.join(sorted(flags)) + + def add_flag(self, flag): + """Set the given flag(s) without changing others.""" + self.set_flags(''.join(set(self.get_flags()) | set(flag))) + + def remove_flag(self, flag): + """Unset the given string flag(s) without changing others.""" + if self.get_flags(): + self.set_flags(''.join(set(self.get_flags()) - set(flag))) + + def get_date(self): + """Return delivery date of message, in seconds since the epoch.""" + return self._date + + def set_date(self, date): + """Set delivery date of message, in seconds since the epoch.""" + try: + self._date = float(date) + except ValueError: + raise TypeError("can't convert to float: %s" % date) from None + + def get_info(self): + """Get the message's "info" as a string.""" + return self._info + + def set_info(self, info): + """Set the message's "info" string.""" + if isinstance(info, str): + self._info = info + else: + raise TypeError('info must be a string: %s' % type(info)) + + def _explain_to(self, message): + """Copy Maildir-specific state to message insofar as possible.""" + if isinstance(message, MaildirMessage): + message.set_flags(self.get_flags()) + message.set_subdir(self.get_subdir()) + message.set_date(self.get_date()) + elif isinstance(message, _mboxMMDFMessage): + flags = set(self.get_flags()) + if 'S' in flags: + message.add_flag('R') + if self.get_subdir() == 'cur': + message.add_flag('O') + if 'T' in flags: + message.add_flag('D') + if 'F' in flags: + message.add_flag('F') + if 'R' in flags: + message.add_flag('A') + message.set_from('MAILER-DAEMON', time.gmtime(self.get_date())) + elif isinstance(message, MHMessage): + flags = set(self.get_flags()) + if 'S' not in flags: + message.add_sequence('unseen') + if 'R' in flags: + message.add_sequence('replied') + if 'F' in flags: + message.add_sequence('flagged') + elif isinstance(message, BabylMessage): + flags = set(self.get_flags()) + if 'S' not in flags: + message.add_label('unseen') + if 'T' in flags: + message.add_label('deleted') + if 'R' in flags: + message.add_label('answered') + if 'P' in flags: + message.add_label('forwarded') + elif isinstance(message, Message): + pass + else: + raise TypeError('Cannot convert to specified type: %s' % + type(message)) + + +class _mboxMMDFMessage(Message): + """Message with mbox- or MMDF-specific properties.""" + + _type_specific_attributes = ['_from'] + + def __init__(self, message=None): + """Initialize an mboxMMDFMessage instance.""" + self.set_from('MAILER-DAEMON', True) + if isinstance(message, email.message.Message): + unixfrom = message.get_unixfrom() + if unixfrom is not None and unixfrom.startswith('From '): + self.set_from(unixfrom[5:]) + Message.__init__(self, message) + + def get_from(self): + """Return contents of "From " line.""" + return self._from + + def set_from(self, from_, time_=None): + """Set "From " line, formatting and appending time_ if specified.""" + if time_ is not None: + if time_ is True: + time_ = time.gmtime() + from_ += ' ' + time.asctime(time_) + self._from = from_ + + def get_flags(self): + """Return as a string the flags that are set.""" + return self.get('Status', '') + self.get('X-Status', '') + + def set_flags(self, flags): + """Set the given flags and unset all others.""" + flags = set(flags) + status_flags, xstatus_flags = '', '' + for flag in ('R', 'O'): + if flag in flags: + status_flags += flag + flags.remove(flag) + for flag in ('D', 'F', 'A'): + if flag in flags: + xstatus_flags += flag + flags.remove(flag) + xstatus_flags += ''.join(sorted(flags)) + try: + self.replace_header('Status', status_flags) + except KeyError: + self.add_header('Status', status_flags) + try: + self.replace_header('X-Status', xstatus_flags) + except KeyError: + self.add_header('X-Status', xstatus_flags) + + def add_flag(self, flag): + """Set the given flag(s) without changing others.""" + self.set_flags(''.join(set(self.get_flags()) | set(flag))) + + def remove_flag(self, flag): + """Unset the given string flag(s) without changing others.""" + if 'Status' in self or 'X-Status' in self: + self.set_flags(''.join(set(self.get_flags()) - set(flag))) + + def _explain_to(self, message): + """Copy mbox- or MMDF-specific state to message insofar as possible.""" + if isinstance(message, MaildirMessage): + flags = set(self.get_flags()) + if 'O' in flags: + message.set_subdir('cur') + if 'F' in flags: + message.add_flag('F') + if 'A' in flags: + message.add_flag('R') + if 'R' in flags: + message.add_flag('S') + if 'D' in flags: + message.add_flag('T') + del message['status'] + del message['x-status'] + maybe_date = ' '.join(self.get_from().split()[-5:]) + try: + message.set_date(calendar.timegm(time.strptime(maybe_date, + '%a %b %d %H:%M:%S %Y'))) + except (ValueError, OverflowError): + pass + elif isinstance(message, _mboxMMDFMessage): + message.set_flags(self.get_flags()) + message.set_from(self.get_from()) + elif isinstance(message, MHMessage): + flags = set(self.get_flags()) + if 'R' not in flags: + message.add_sequence('unseen') + if 'A' in flags: + message.add_sequence('replied') + if 'F' in flags: + message.add_sequence('flagged') + del message['status'] + del message['x-status'] + elif isinstance(message, BabylMessage): + flags = set(self.get_flags()) + if 'R' not in flags: + message.add_label('unseen') + if 'D' in flags: + message.add_label('deleted') + if 'A' in flags: + message.add_label('answered') + del message['status'] + del message['x-status'] + elif isinstance(message, Message): + pass + else: + raise TypeError('Cannot convert to specified type: %s' % + type(message)) + + +class mboxMessage(_mboxMMDFMessage): + """Message with mbox-specific properties.""" + + +class MHMessage(Message): + """Message with MH-specific properties.""" + + _type_specific_attributes = ['_sequences'] + + def __init__(self, message=None): + """Initialize an MHMessage instance.""" + self._sequences = [] + Message.__init__(self, message) + + def get_sequences(self): + """Return a list of sequences that include the message.""" + return self._sequences[:] + + def set_sequences(self, sequences): + """Set the list of sequences that include the message.""" + self._sequences = list(sequences) + + def add_sequence(self, sequence): + """Add sequence to list of sequences including the message.""" + if isinstance(sequence, str): + if not sequence in self._sequences: + self._sequences.append(sequence) + else: + raise TypeError('sequence type must be str: %s' % type(sequence)) + + def remove_sequence(self, sequence): + """Remove sequence from the list of sequences including the message.""" + try: + self._sequences.remove(sequence) + except ValueError: + pass + + def _explain_to(self, message): + """Copy MH-specific state to message insofar as possible.""" + if isinstance(message, MaildirMessage): + sequences = set(self.get_sequences()) + if 'unseen' in sequences: + message.set_subdir('cur') + else: + message.set_subdir('cur') + message.add_flag('S') + if 'flagged' in sequences: + message.add_flag('F') + if 'replied' in sequences: + message.add_flag('R') + elif isinstance(message, _mboxMMDFMessage): + sequences = set(self.get_sequences()) + if 'unseen' not in sequences: + message.add_flag('RO') + else: + message.add_flag('O') + if 'flagged' in sequences: + message.add_flag('F') + if 'replied' in sequences: + message.add_flag('A') + elif isinstance(message, MHMessage): + for sequence in self.get_sequences(): + message.add_sequence(sequence) + elif isinstance(message, BabylMessage): + sequences = set(self.get_sequences()) + if 'unseen' in sequences: + message.add_label('unseen') + if 'replied' in sequences: + message.add_label('answered') + elif isinstance(message, Message): + pass + else: + raise TypeError('Cannot convert to specified type: %s' % + type(message)) + + +class BabylMessage(Message): + """Message with Babyl-specific properties.""" + + _type_specific_attributes = ['_labels', '_visible'] + + def __init__(self, message=None): + """Initialize a BabylMessage instance.""" + self._labels = [] + self._visible = Message() + Message.__init__(self, message) + + def get_labels(self): + """Return a list of labels on the message.""" + return self._labels[:] + + def set_labels(self, labels): + """Set the list of labels on the message.""" + self._labels = list(labels) + + def add_label(self, label): + """Add label to list of labels on the message.""" + if isinstance(label, str): + if label not in self._labels: + self._labels.append(label) + else: + raise TypeError('label must be a string: %s' % type(label)) + + def remove_label(self, label): + """Remove label from the list of labels on the message.""" + try: + self._labels.remove(label) + except ValueError: + pass + + def get_visible(self): + """Return a Message representation of visible headers.""" + return Message(self._visible) + + def set_visible(self, visible): + """Set the Message representation of visible headers.""" + self._visible = Message(visible) + + def update_visible(self): + """Update and/or sensibly generate a set of visible headers.""" + for header in self._visible.keys(): + if header in self: + self._visible.replace_header(header, self[header]) + else: + del self._visible[header] + for header in ('Date', 'From', 'Reply-To', 'To', 'CC', 'Subject'): + if header in self and header not in self._visible: + self._visible[header] = self[header] + + def _explain_to(self, message): + """Copy Babyl-specific state to message insofar as possible.""" + if isinstance(message, MaildirMessage): + labels = set(self.get_labels()) + if 'unseen' in labels: + message.set_subdir('cur') + else: + message.set_subdir('cur') + message.add_flag('S') + if 'forwarded' in labels or 'resent' in labels: + message.add_flag('P') + if 'answered' in labels: + message.add_flag('R') + if 'deleted' in labels: + message.add_flag('T') + elif isinstance(message, _mboxMMDFMessage): + labels = set(self.get_labels()) + if 'unseen' not in labels: + message.add_flag('RO') + else: + message.add_flag('O') + if 'deleted' in labels: + message.add_flag('D') + if 'answered' in labels: + message.add_flag('A') + elif isinstance(message, MHMessage): + labels = set(self.get_labels()) + if 'unseen' in labels: + message.add_sequence('unseen') + if 'answered' in labels: + message.add_sequence('replied') + elif isinstance(message, BabylMessage): + message.set_visible(self.get_visible()) + for label in self.get_labels(): + message.add_label(label) + elif isinstance(message, Message): + pass + else: + raise TypeError('Cannot convert to specified type: %s' % + type(message)) + + +class MMDFMessage(_mboxMMDFMessage): + """Message with MMDF-specific properties.""" + + +class _ProxyFile: + """A read-only wrapper of a file.""" + + def __init__(self, f, pos=None): + """Initialize a _ProxyFile.""" + self._file = f + if pos is None: + self._pos = f.tell() + else: + self._pos = pos + + def read(self, size=None): + """Read bytes.""" + return self._read(size, self._file.read) + + def read1(self, size=None): + """Read bytes.""" + return self._read(size, self._file.read1) + + def readline(self, size=None): + """Read a line.""" + return self._read(size, self._file.readline) + + def readlines(self, sizehint=None): + """Read multiple lines.""" + result = [] + for line in self: + result.append(line) + if sizehint is not None: + sizehint -= len(line) + if sizehint <= 0: + break + return result + + def __iter__(self): + """Iterate over lines.""" + while True: + line = self.readline() + if not line: + return + yield line + + def tell(self): + """Return the position.""" + return self._pos + + def seek(self, offset, whence=0): + """Change position.""" + if whence == 1: + self._file.seek(self._pos) + self._file.seek(offset, whence) + self._pos = self._file.tell() + + def close(self): + """Close the file.""" + if hasattr(self, '_file'): + try: + if hasattr(self._file, 'close'): + self._file.close() + finally: + del self._file + + def _read(self, size, read_method): + """Read size bytes using read_method.""" + if size is None: + size = -1 + self._file.seek(self._pos) + result = read_method(size) + self._pos = self._file.tell() + return result + + def __enter__(self): + """Context management protocol support.""" + return self + + def __exit__(self, *exc): + self.close() + + def readable(self): + return self._file.readable() + + def writable(self): + return self._file.writable() + + def seekable(self): + return self._file.seekable() + + def flush(self): + return self._file.flush() + + @property + def closed(self): + if not hasattr(self, '_file'): + return True + if not hasattr(self._file, 'closed'): + return False + return self._file.closed + + +class _PartialFile(_ProxyFile): + """A read-only wrapper of part of a file.""" + + def __init__(self, f, start=None, stop=None): + """Initialize a _PartialFile.""" + _ProxyFile.__init__(self, f, start) + self._start = start + self._stop = stop + + def tell(self): + """Return the position with respect to start.""" + return _ProxyFile.tell(self) - self._start + + def seek(self, offset, whence=0): + """Change position, possibly with respect to start or stop.""" + if whence == 0: + self._pos = self._start + whence = 1 + elif whence == 2: + self._pos = self._stop + whence = 1 + _ProxyFile.seek(self, offset, whence) + + def _read(self, size, read_method): + """Read size bytes using read_method, honoring start and stop.""" + remaining = self._stop - self._pos + if remaining <= 0: + return b'' + if size is None or size < 0 or size > remaining: + size = remaining + return _ProxyFile._read(self, size, read_method) + + def close(self): + # do *not* close the underlying file object for partial files, + # since it's global to the mailbox object + if hasattr(self, '_file'): + del self._file + + +def _lock_file(f, dotlock=True): + """Lock file f using lockf and dot locking.""" + dotlock_done = False + try: + if fcntl: + try: + fcntl.lockf(f, fcntl.LOCK_EX | fcntl.LOCK_NB) + except OSError as e: + if e.errno in (errno.EAGAIN, errno.EACCES, errno.EROFS): + raise ExternalClashError('lockf: lock unavailable: %s' % + f.name) + else: + raise + if dotlock: + try: + pre_lock = _create_temporary(f.name + '.lock') + pre_lock.close() + except OSError as e: + if e.errno in (errno.EACCES, errno.EROFS): + return # Without write access, just skip dotlocking. + else: + raise + try: + try: + os.link(pre_lock.name, f.name + '.lock') + dotlock_done = True + except (AttributeError, PermissionError): + os.rename(pre_lock.name, f.name + '.lock') + dotlock_done = True + else: + os.unlink(pre_lock.name) + except FileExistsError: + os.remove(pre_lock.name) + raise ExternalClashError('dot lock unavailable: %s' % + f.name) + except: + if fcntl: + fcntl.lockf(f, fcntl.LOCK_UN) + if dotlock_done: + os.remove(f.name + '.lock') + raise + +def _unlock_file(f): + """Unlock file f using lockf and dot locking.""" + if fcntl: + fcntl.lockf(f, fcntl.LOCK_UN) + if os.path.exists(f.name + '.lock'): + os.remove(f.name + '.lock') + +def _create_carefully(path): + """Create a file if it doesn't exist and open for reading and writing.""" + fd = os.open(path, os.O_CREAT | os.O_EXCL | os.O_RDWR, 0o666) + try: + return open(path, 'rb+') + finally: + os.close(fd) + +def _create_temporary(path): + """Create a temp file based on path and open for reading and writing.""" + return _create_carefully('%s.%s.%s.%s' % (path, int(time.time()), + socket.gethostname(), + os.getpid())) + +def _sync_flush(f): + """Ensure changes to file f are physically on disk.""" + f.flush() + if hasattr(os, 'fsync'): + os.fsync(f.fileno()) + +def _sync_close(f): + """Close file f, ensuring all changes are physically on disk.""" + _sync_flush(f) + f.close() + + +class Error(Exception): + """Raised for module-specific errors.""" + +class NoSuchMailboxError(Error): + """The specified mailbox does not exist and won't be created.""" + +class NotEmptyError(Error): + """The specified mailbox is not empty and deletion was requested.""" + +class ExternalClashError(Error): + """Another process caused an action to fail.""" + +class FormatError(Error): + """A file appears to have an invalid format.""" diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/mimetypes.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/mimetypes.py new file mode 100644 index 0000000000000000000000000000000000000000..954bb0a7453477fc5aa0ff44adad6a9a4da89dcb --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/mimetypes.py @@ -0,0 +1,614 @@ +"""Guess the MIME type of a file. + +This module defines two useful functions: + +guess_type(url, strict=True) -- guess the MIME type and encoding of a URL. + +guess_extension(type, strict=True) -- guess the extension for a given MIME type. + +It also contains the following, for tuning the behavior: + +Data: + +knownfiles -- list of files to parse +inited -- flag set when init() has been called +suffix_map -- dictionary mapping suffixes to suffixes +encodings_map -- dictionary mapping suffixes to encodings +types_map -- dictionary mapping suffixes to types + +Functions: + +init([files]) -- parse a list of files, default knownfiles (on Windows, the + default values are taken from the registry) +read_mime_types(file) -- parse one file, return a dictionary or None +""" + +import os +import sys +import posixpath +import urllib.parse +try: + import winreg as _winreg +except ImportError: + _winreg = None + +__all__ = [ + "knownfiles", "inited", "MimeTypes", + "guess_type", "guess_all_extensions", "guess_extension", + "add_type", "init", "read_mime_types", + "suffix_map", "encodings_map", "types_map", "common_types" +] + +knownfiles = [ + "/etc/mime.types", + "/etc/httpd/mime.types", # Mac OS X + "/etc/httpd/conf/mime.types", # Apache + "/etc/apache/mime.types", # Apache 1 + "/etc/apache2/mime.types", # Apache 2 + "/usr/local/etc/httpd/conf/mime.types", + "/usr/local/lib/netscape/mime.types", + "/usr/local/etc/httpd/conf/mime.types", # Apache 1.2 + "/usr/local/etc/mime.types", # Apache 1.3 + ] + +inited = False +_db = None + + +class MimeTypes: + """MIME-types datastore. + + This datastore can handle information from mime.types-style files + and supports basic determination of MIME type from a filename or + URL, and can guess a reasonable extension given a MIME type. + """ + + def __init__(self, filenames=(), strict=True): + if not inited: + init() + self.encodings_map = _encodings_map_default.copy() + self.suffix_map = _suffix_map_default.copy() + self.types_map = ({}, {}) # dict for (non-strict, strict) + self.types_map_inv = ({}, {}) + for (ext, type) in _types_map_default.items(): + self.add_type(type, ext, True) + for (ext, type) in _common_types_default.items(): + self.add_type(type, ext, False) + for name in filenames: + self.read(name, strict) + + def add_type(self, type, ext, strict=True): + """Add a mapping between a type and an extension. + + When the extension is already known, the new + type will replace the old one. When the type + is already known the extension will be added + to the list of known extensions. + + If strict is true, information will be added to + list of standard types, else to the list of non-standard + types. + """ + self.types_map[strict][ext] = type + exts = self.types_map_inv[strict].setdefault(type, []) + if ext not in exts: + exts.append(ext) + + def guess_type(self, url, strict=True): + """Guess the type of a file which is either a URL or a path-like object. + + Return value is a tuple (type, encoding) where type is None if + the type can't be guessed (no or unknown suffix) or a string + of the form type/subtype, usable for a MIME Content-type + header; and encoding is None for no encoding or the name of + the program used to encode (e.g. compress or gzip). The + mappings are table driven. Encoding suffixes are case + sensitive; type suffixes are first tried case sensitive, then + case insensitive. + + The suffixes .tgz, .taz and .tz (case sensitive!) are all + mapped to '.tar.gz'. (This is table-driven too, using the + dictionary suffix_map.) + + Optional `strict' argument when False adds a bunch of commonly found, + but non-standard types. + """ + url = os.fspath(url) + scheme, url = urllib.parse._splittype(url) + if scheme == 'data': + # syntax of data URLs: + # dataurl := "data:" [ mediatype ] [ ";base64" ] "," data + # mediatype := [ type "/" subtype ] *( ";" parameter ) + # data := *urlchar + # parameter := attribute "=" value + # type/subtype defaults to "text/plain" + comma = url.find(',') + if comma < 0: + # bad data URL + return None, None + semi = url.find(';', 0, comma) + if semi >= 0: + type = url[:semi] + else: + type = url[:comma] + if '=' in type or '/' not in type: + type = 'text/plain' + return type, None # never compressed, so encoding is None + base, ext = posixpath.splitext(url) + while ext in self.suffix_map: + base, ext = posixpath.splitext(base + self.suffix_map[ext]) + if ext in self.encodings_map: + encoding = self.encodings_map[ext] + base, ext = posixpath.splitext(base) + else: + encoding = None + types_map = self.types_map[True] + if ext in types_map: + return types_map[ext], encoding + elif ext.lower() in types_map: + return types_map[ext.lower()], encoding + elif strict: + return None, encoding + types_map = self.types_map[False] + if ext in types_map: + return types_map[ext], encoding + elif ext.lower() in types_map: + return types_map[ext.lower()], encoding + else: + return None, encoding + + def guess_all_extensions(self, type, strict=True): + """Guess the extensions for a file based on its MIME type. + + Return value is a list of strings giving the possible filename + extensions, including the leading dot ('.'). The extension is not + guaranteed to have been associated with any particular data stream, + but would be mapped to the MIME type `type' by guess_type(). + + Optional `strict' argument when false adds a bunch of commonly found, + but non-standard types. + """ + type = type.lower() + extensions = self.types_map_inv[True].get(type, []) + if not strict: + for ext in self.types_map_inv[False].get(type, []): + if ext not in extensions: + extensions.append(ext) + return extensions + + def guess_extension(self, type, strict=True): + """Guess the extension for a file based on its MIME type. + + Return value is a string giving a filename extension, + including the leading dot ('.'). The extension is not + guaranteed to have been associated with any particular data + stream, but would be mapped to the MIME type `type' by + guess_type(). If no extension can be guessed for `type', None + is returned. + + Optional `strict' argument when false adds a bunch of commonly found, + but non-standard types. + """ + extensions = self.guess_all_extensions(type, strict) + if not extensions: + return None + return extensions[0] + + def read(self, filename, strict=True): + """ + Read a single mime.types-format file, specified by pathname. + + If strict is true, information will be added to + list of standard types, else to the list of non-standard + types. + """ + with open(filename, encoding='utf-8') as fp: + self.readfp(fp, strict) + + def readfp(self, fp, strict=True): + """ + Read a single mime.types-format file. + + If strict is true, information will be added to + list of standard types, else to the list of non-standard + types. + """ + while 1: + line = fp.readline() + if not line: + break + words = line.split() + for i in range(len(words)): + if words[i][0] == '#': + del words[i:] + break + if not words: + continue + type, suffixes = words[0], words[1:] + for suff in suffixes: + self.add_type(type, '.' + suff, strict) + + def read_windows_registry(self, strict=True): + """ + Load the MIME types database from Windows registry. + + If strict is true, information will be added to + list of standard types, else to the list of non-standard + types. + """ + + # Windows only + if not _winreg: + return + + def enum_types(mimedb): + i = 0 + while True: + try: + ctype = _winreg.EnumKey(mimedb, i) + except OSError: + break + else: + if '\0' not in ctype: + yield ctype + i += 1 + + with _winreg.OpenKey(_winreg.HKEY_CLASSES_ROOT, '') as hkcr: + for subkeyname in enum_types(hkcr): + try: + with _winreg.OpenKey(hkcr, subkeyname) as subkey: + # Only check file extensions + if not subkeyname.startswith("."): + continue + # raises OSError if no 'Content Type' value + mimetype, datatype = _winreg.QueryValueEx( + subkey, 'Content Type') + if datatype != _winreg.REG_SZ: + continue + self.add_type(mimetype, subkeyname, strict) + except OSError: + continue + +def guess_type(url, strict=True): + """Guess the type of a file based on its URL. + + Return value is a tuple (type, encoding) where type is None if the + type can't be guessed (no or unknown suffix) or a string of the + form type/subtype, usable for a MIME Content-type header; and + encoding is None for no encoding or the name of the program used + to encode (e.g. compress or gzip). The mappings are table + driven. Encoding suffixes are case sensitive; type suffixes are + first tried case sensitive, then case insensitive. + + The suffixes .tgz, .taz and .tz (case sensitive!) are all mapped + to ".tar.gz". (This is table-driven too, using the dictionary + suffix_map). + + Optional `strict' argument when false adds a bunch of commonly found, but + non-standard types. + """ + if _db is None: + init() + return _db.guess_type(url, strict) + + +def guess_all_extensions(type, strict=True): + """Guess the extensions for a file based on its MIME type. + + Return value is a list of strings giving the possible filename + extensions, including the leading dot ('.'). The extension is not + guaranteed to have been associated with any particular data + stream, but would be mapped to the MIME type `type' by + guess_type(). If no extension can be guessed for `type', None + is returned. + + Optional `strict' argument when false adds a bunch of commonly found, + but non-standard types. + """ + if _db is None: + init() + return _db.guess_all_extensions(type, strict) + +def guess_extension(type, strict=True): + """Guess the extension for a file based on its MIME type. + + Return value is a string giving a filename extension, including the + leading dot ('.'). The extension is not guaranteed to have been + associated with any particular data stream, but would be mapped to the + MIME type `type' by guess_type(). If no extension can be guessed for + `type', None is returned. + + Optional `strict' argument when false adds a bunch of commonly found, + but non-standard types. + """ + if _db is None: + init() + return _db.guess_extension(type, strict) + +def add_type(type, ext, strict=True): + """Add a mapping between a type and an extension. + + When the extension is already known, the new + type will replace the old one. When the type + is already known the extension will be added + to the list of known extensions. + + If strict is true, information will be added to + list of standard types, else to the list of non-standard + types. + """ + if _db is None: + init() + return _db.add_type(type, ext, strict) + + +def init(files=None): + global suffix_map, types_map, encodings_map, common_types + global inited, _db + inited = True # so that MimeTypes.__init__() doesn't call us again + + if files is None or _db is None: + db = MimeTypes() + if _winreg: + db.read_windows_registry() + + if files is None: + files = knownfiles + else: + files = knownfiles + list(files) + else: + db = _db + + for file in files: + if os.path.isfile(file): + db.read(file) + encodings_map = db.encodings_map + suffix_map = db.suffix_map + types_map = db.types_map[True] + common_types = db.types_map[False] + # Make the DB a global variable now that it is fully initialized + _db = db + + +def read_mime_types(file): + try: + f = open(file, encoding='utf-8') + except OSError: + return None + with f: + db = MimeTypes() + db.readfp(f, True) + return db.types_map[True] + + +def _default_mime_types(): + global suffix_map, _suffix_map_default + global encodings_map, _encodings_map_default + global types_map, _types_map_default + global common_types, _common_types_default + + suffix_map = _suffix_map_default = { + '.svgz': '.svg.gz', + '.tgz': '.tar.gz', + '.taz': '.tar.gz', + '.tz': '.tar.gz', + '.tbz2': '.tar.bz2', + '.txz': '.tar.xz', + } + + encodings_map = _encodings_map_default = { + '.gz': 'gzip', + '.Z': 'compress', + '.bz2': 'bzip2', + '.xz': 'xz', + } + + # Before adding new types, make sure they are either registered with IANA, + # at http://www.iana.org/assignments/media-types + # or extensions, i.e. using the x- prefix + + # If you add to these, please keep them sorted by mime type. + # Make sure the entry with the preferred file extension for a particular mime type + # appears before any others of the same mimetype. + types_map = _types_map_default = { + '.js' : 'application/javascript', + '.mjs' : 'application/javascript', + '.json' : 'application/json', + '.webmanifest': 'application/manifest+json', + '.doc' : 'application/msword', + '.dot' : 'application/msword', + '.wiz' : 'application/msword', + '.bin' : 'application/octet-stream', + '.a' : 'application/octet-stream', + '.dll' : 'application/octet-stream', + '.exe' : 'application/octet-stream', + '.o' : 'application/octet-stream', + '.obj' : 'application/octet-stream', + '.so' : 'application/octet-stream', + '.oda' : 'application/oda', + '.pdf' : 'application/pdf', + '.p7c' : 'application/pkcs7-mime', + '.ps' : 'application/postscript', + '.ai' : 'application/postscript', + '.eps' : 'application/postscript', + '.m3u' : 'application/vnd.apple.mpegurl', + '.m3u8' : 'application/vnd.apple.mpegurl', + '.xls' : 'application/vnd.ms-excel', + '.xlb' : 'application/vnd.ms-excel', + '.ppt' : 'application/vnd.ms-powerpoint', + '.pot' : 'application/vnd.ms-powerpoint', + '.ppa' : 'application/vnd.ms-powerpoint', + '.pps' : 'application/vnd.ms-powerpoint', + '.pwz' : 'application/vnd.ms-powerpoint', + '.wasm' : 'application/wasm', + '.bcpio' : 'application/x-bcpio', + '.cpio' : 'application/x-cpio', + '.csh' : 'application/x-csh', + '.dvi' : 'application/x-dvi', + '.gtar' : 'application/x-gtar', + '.hdf' : 'application/x-hdf', + '.h5' : 'application/x-hdf5', + '.latex' : 'application/x-latex', + '.mif' : 'application/x-mif', + '.cdf' : 'application/x-netcdf', + '.nc' : 'application/x-netcdf', + '.p12' : 'application/x-pkcs12', + '.pfx' : 'application/x-pkcs12', + '.ram' : 'application/x-pn-realaudio', + '.pyc' : 'application/x-python-code', + '.pyo' : 'application/x-python-code', + '.sh' : 'application/x-sh', + '.shar' : 'application/x-shar', + '.swf' : 'application/x-shockwave-flash', + '.sv4cpio': 'application/x-sv4cpio', + '.sv4crc' : 'application/x-sv4crc', + '.tar' : 'application/x-tar', + '.tcl' : 'application/x-tcl', + '.tex' : 'application/x-tex', + '.texi' : 'application/x-texinfo', + '.texinfo': 'application/x-texinfo', + '.roff' : 'application/x-troff', + '.t' : 'application/x-troff', + '.tr' : 'application/x-troff', + '.man' : 'application/x-troff-man', + '.me' : 'application/x-troff-me', + '.ms' : 'application/x-troff-ms', + '.ustar' : 'application/x-ustar', + '.src' : 'application/x-wais-source', + '.xsl' : 'application/xml', + '.rdf' : 'application/xml', + '.wsdl' : 'application/xml', + '.xpdl' : 'application/xml', + '.zip' : 'application/zip', + '.au' : 'audio/basic', + '.snd' : 'audio/basic', + '.mp3' : 'audio/mpeg', + '.mp2' : 'audio/mpeg', + '.aif' : 'audio/x-aiff', + '.aifc' : 'audio/x-aiff', + '.aiff' : 'audio/x-aiff', + '.ra' : 'audio/x-pn-realaudio', + '.wav' : 'audio/x-wav', + '.bmp' : 'image/bmp', + '.gif' : 'image/gif', + '.ief' : 'image/ief', + '.jpg' : 'image/jpeg', + '.jpe' : 'image/jpeg', + '.jpeg' : 'image/jpeg', + '.png' : 'image/png', + '.svg' : 'image/svg+xml', + '.tiff' : 'image/tiff', + '.tif' : 'image/tiff', + '.ico' : 'image/vnd.microsoft.icon', + '.ras' : 'image/x-cmu-raster', + '.bmp' : 'image/x-ms-bmp', + '.pnm' : 'image/x-portable-anymap', + '.pbm' : 'image/x-portable-bitmap', + '.pgm' : 'image/x-portable-graymap', + '.ppm' : 'image/x-portable-pixmap', + '.rgb' : 'image/x-rgb', + '.xbm' : 'image/x-xbitmap', + '.xpm' : 'image/x-xpixmap', + '.xwd' : 'image/x-xwindowdump', + '.eml' : 'message/rfc822', + '.mht' : 'message/rfc822', + '.mhtml' : 'message/rfc822', + '.nws' : 'message/rfc822', + '.css' : 'text/css', + '.csv' : 'text/csv', + '.html' : 'text/html', + '.htm' : 'text/html', + '.txt' : 'text/plain', + '.bat' : 'text/plain', + '.c' : 'text/plain', + '.h' : 'text/plain', + '.ksh' : 'text/plain', + '.pl' : 'text/plain', + '.rtx' : 'text/richtext', + '.tsv' : 'text/tab-separated-values', + '.py' : 'text/x-python', + '.etx' : 'text/x-setext', + '.sgm' : 'text/x-sgml', + '.sgml' : 'text/x-sgml', + '.vcf' : 'text/x-vcard', + '.xml' : 'text/xml', + '.mp4' : 'video/mp4', + '.mpeg' : 'video/mpeg', + '.m1v' : 'video/mpeg', + '.mpa' : 'video/mpeg', + '.mpe' : 'video/mpeg', + '.mpg' : 'video/mpeg', + '.mov' : 'video/quicktime', + '.qt' : 'video/quicktime', + '.webm' : 'video/webm', + '.avi' : 'video/x-msvideo', + '.movie' : 'video/x-sgi-movie', + } + + # These are non-standard types, commonly found in the wild. They will + # only match if strict=0 flag is given to the API methods. + + # Please sort these too + common_types = _common_types_default = { + '.rtf' : 'application/rtf', + '.midi': 'audio/midi', + '.mid' : 'audio/midi', + '.jpg' : 'image/jpg', + '.pict': 'image/pict', + '.pct' : 'image/pict', + '.pic' : 'image/pict', + '.xul' : 'text/xul', + } + + +_default_mime_types() + + +def _main(): + import getopt + + USAGE = """\ +Usage: mimetypes.py [options] type + +Options: + --help / -h -- print this message and exit + --lenient / -l -- additionally search of some common, but non-standard + types. + --extension / -e -- guess extension instead of type + +More than one type argument may be given. +""" + + def usage(code, msg=''): + print(USAGE) + if msg: print(msg) + sys.exit(code) + + try: + opts, args = getopt.getopt(sys.argv[1:], 'hle', + ['help', 'lenient', 'extension']) + except getopt.error as msg: + usage(1, msg) + + strict = 1 + extension = 0 + for opt, arg in opts: + if opt in ('-h', '--help'): + usage(0) + elif opt in ('-l', '--lenient'): + strict = 0 + elif opt in ('-e', '--extension'): + extension = 1 + for gtype in args: + if extension: + guess = guess_extension(gtype, strict) + if not guess: print("I don't know anything about type", gtype) + else: print(guess) + else: + guess, encoding = guess_type(gtype, strict) + if not guess: print("I don't know anything about type", gtype) + else: print('type:', guess, 'encoding:', encoding) + + +if __name__ == '__main__': + _main() diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/modulefinder.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/modulefinder.py new file mode 100644 index 0000000000000000000000000000000000000000..aadcd23edbaaa7c91eb28ffe56b4cd75ab4d418f --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/modulefinder.py @@ -0,0 +1,687 @@ +"""Find modules used by a script, using introspection.""" + +import dis +import importlib._bootstrap_external +import importlib.machinery +import marshal +import os +import io +import sys +import types +import warnings + + +LOAD_CONST = dis.opmap['LOAD_CONST'] +IMPORT_NAME = dis.opmap['IMPORT_NAME'] +STORE_NAME = dis.opmap['STORE_NAME'] +STORE_GLOBAL = dis.opmap['STORE_GLOBAL'] +STORE_OPS = STORE_NAME, STORE_GLOBAL +EXTENDED_ARG = dis.EXTENDED_ARG + +# Old imp constants: + +_SEARCH_ERROR = 0 +_PY_SOURCE = 1 +_PY_COMPILED = 2 +_C_EXTENSION = 3 +_PKG_DIRECTORY = 5 +_C_BUILTIN = 6 +_PY_FROZEN = 7 + +# Modulefinder does a good job at simulating Python's, but it can not +# handle __path__ modifications packages make at runtime. Therefore there +# is a mechanism whereby you can register extra paths in this map for a +# package, and it will be honored. + +# Note this is a mapping is lists of paths. +packagePathMap = {} + +# A Public interface +def AddPackagePath(packagename, path): + packagePathMap.setdefault(packagename, []).append(path) + +replacePackageMap = {} + +# This ReplacePackage mechanism allows modulefinder to work around +# situations in which a package injects itself under the name +# of another package into sys.modules at runtime by calling +# ReplacePackage("real_package_name", "faked_package_name") +# before running ModuleFinder. + +def ReplacePackage(oldname, newname): + replacePackageMap[oldname] = newname + + +def _find_module(name, path=None): + """An importlib reimplementation of imp.find_module (for our purposes).""" + + # It's necessary to clear the caches for our Finder first, in case any + # modules are being added/deleted/modified at runtime. In particular, + # test_modulefinder.py changes file tree contents in a cache-breaking way: + + importlib.machinery.PathFinder.invalidate_caches() + + spec = importlib.machinery.PathFinder.find_spec(name, path) + + if spec is None: + raise ImportError("No module named {name!r}".format(name=name), name=name) + + # Some special cases: + + if spec.loader is importlib.machinery.BuiltinImporter: + return None, None, ("", "", _C_BUILTIN) + + if spec.loader is importlib.machinery.FrozenImporter: + return None, None, ("", "", _PY_FROZEN) + + file_path = spec.origin + + if spec.loader.is_package(name): + return None, os.path.dirname(file_path), ("", "", _PKG_DIRECTORY) + + if isinstance(spec.loader, importlib.machinery.SourceFileLoader): + kind = _PY_SOURCE + + elif isinstance(spec.loader, importlib.machinery.ExtensionFileLoader): + kind = _C_EXTENSION + + elif isinstance(spec.loader, importlib.machinery.SourcelessFileLoader): + kind = _PY_COMPILED + + else: # Should never happen. + return None, None, ("", "", _SEARCH_ERROR) + + file = io.open_code(file_path) + suffix = os.path.splitext(file_path)[-1] + + return file, file_path, (suffix, "rb", kind) + + +class Module: + + def __init__(self, name, file=None, path=None): + self.__name__ = name + self.__file__ = file + self.__path__ = path + self.__code__ = None + # The set of global names that are assigned to in the module. + # This includes those names imported through starimports of + # Python modules. + self.globalnames = {} + # The set of starimports this module did that could not be + # resolved, ie. a starimport from a non-Python module. + self.starimports = {} + + def __repr__(self): + s = "Module(%r" % (self.__name__,) + if self.__file__ is not None: + s = s + ", %r" % (self.__file__,) + if self.__path__ is not None: + s = s + ", %r" % (self.__path__,) + s = s + ")" + return s + +class ModuleFinder: + + def __init__(self, path=None, debug=0, excludes=None, replace_paths=None): + if path is None: + path = sys.path + self.path = path + self.modules = {} + self.badmodules = {} + self.debug = debug + self.indent = 0 + self.excludes = excludes if excludes is not None else [] + self.replace_paths = replace_paths if replace_paths is not None else [] + self.processed_paths = [] # Used in debugging only + + def msg(self, level, str, *args): + if level <= self.debug: + for i in range(self.indent): + print(" ", end=' ') + print(str, end=' ') + for arg in args: + print(repr(arg), end=' ') + print() + + def msgin(self, *args): + level = args[0] + if level <= self.debug: + self.indent = self.indent + 1 + self.msg(*args) + + def msgout(self, *args): + level = args[0] + if level <= self.debug: + self.indent = self.indent - 1 + self.msg(*args) + + def run_script(self, pathname): + self.msg(2, "run_script", pathname) + with io.open_code(pathname) as fp: + stuff = ("", "rb", _PY_SOURCE) + self.load_module('__main__', fp, pathname, stuff) + + def load_file(self, pathname): + dir, name = os.path.split(pathname) + name, ext = os.path.splitext(name) + with io.open_code(pathname) as fp: + stuff = (ext, "rb", _PY_SOURCE) + self.load_module(name, fp, pathname, stuff) + + def import_hook(self, name, caller=None, fromlist=None, level=-1): + self.msg(3, "import_hook", name, caller, fromlist, level) + parent = self.determine_parent(caller, level=level) + q, tail = self.find_head_package(parent, name) + m = self.load_tail(q, tail) + if not fromlist: + return q + if m.__path__: + self.ensure_fromlist(m, fromlist) + return None + + def determine_parent(self, caller, level=-1): + self.msgin(4, "determine_parent", caller, level) + if not caller or level == 0: + self.msgout(4, "determine_parent -> None") + return None + pname = caller.__name__ + if level >= 1: # relative import + if caller.__path__: + level -= 1 + if level == 0: + parent = self.modules[pname] + assert parent is caller + self.msgout(4, "determine_parent ->", parent) + return parent + if pname.count(".") < level: + raise ImportError("relative importpath too deep") + pname = ".".join(pname.split(".")[:-level]) + parent = self.modules[pname] + self.msgout(4, "determine_parent ->", parent) + return parent + if caller.__path__: + parent = self.modules[pname] + assert caller is parent + self.msgout(4, "determine_parent ->", parent) + return parent + if '.' in pname: + i = pname.rfind('.') + pname = pname[:i] + parent = self.modules[pname] + assert parent.__name__ == pname + self.msgout(4, "determine_parent ->", parent) + return parent + self.msgout(4, "determine_parent -> None") + return None + + def find_head_package(self, parent, name): + self.msgin(4, "find_head_package", parent, name) + if '.' in name: + i = name.find('.') + head = name[:i] + tail = name[i+1:] + else: + head = name + tail = "" + if parent: + qname = "%s.%s" % (parent.__name__, head) + else: + qname = head + q = self.import_module(head, qname, parent) + if q: + self.msgout(4, "find_head_package ->", (q, tail)) + return q, tail + if parent: + qname = head + parent = None + q = self.import_module(head, qname, parent) + if q: + self.msgout(4, "find_head_package ->", (q, tail)) + return q, tail + self.msgout(4, "raise ImportError: No module named", qname) + raise ImportError("No module named " + qname) + + def load_tail(self, q, tail): + self.msgin(4, "load_tail", q, tail) + m = q + while tail: + i = tail.find('.') + if i < 0: i = len(tail) + head, tail = tail[:i], tail[i+1:] + mname = "%s.%s" % (m.__name__, head) + m = self.import_module(head, mname, m) + if not m: + self.msgout(4, "raise ImportError: No module named", mname) + raise ImportError("No module named " + mname) + self.msgout(4, "load_tail ->", m) + return m + + def ensure_fromlist(self, m, fromlist, recursive=0): + self.msg(4, "ensure_fromlist", m, fromlist, recursive) + for sub in fromlist: + if sub == "*": + if not recursive: + all = self.find_all_submodules(m) + if all: + self.ensure_fromlist(m, all, 1) + elif not hasattr(m, sub): + subname = "%s.%s" % (m.__name__, sub) + submod = self.import_module(sub, subname, m) + if not submod: + raise ImportError("No module named " + subname) + + def find_all_submodules(self, m): + if not m.__path__: + return + modules = {} + # 'suffixes' used to be a list hardcoded to [".py", ".pyc"]. + # But we must also collect Python extension modules - although + # we cannot separate normal dlls from Python extensions. + suffixes = [] + suffixes += importlib.machinery.EXTENSION_SUFFIXES[:] + suffixes += importlib.machinery.SOURCE_SUFFIXES[:] + suffixes += importlib.machinery.BYTECODE_SUFFIXES[:] + for dir in m.__path__: + try: + names = os.listdir(dir) + except OSError: + self.msg(2, "can't list directory", dir) + continue + for name in names: + mod = None + for suff in suffixes: + n = len(suff) + if name[-n:] == suff: + mod = name[:-n] + break + if mod and mod != "__init__": + modules[mod] = mod + return modules.keys() + + def import_module(self, partname, fqname, parent): + self.msgin(3, "import_module", partname, fqname, parent) + try: + m = self.modules[fqname] + except KeyError: + pass + else: + self.msgout(3, "import_module ->", m) + return m + if fqname in self.badmodules: + self.msgout(3, "import_module -> None") + return None + if parent and parent.__path__ is None: + self.msgout(3, "import_module -> None") + return None + try: + fp, pathname, stuff = self.find_module(partname, + parent and parent.__path__, parent) + except ImportError: + self.msgout(3, "import_module ->", None) + return None + + try: + m = self.load_module(fqname, fp, pathname, stuff) + finally: + if fp: + fp.close() + if parent: + setattr(parent, partname, m) + self.msgout(3, "import_module ->", m) + return m + + def load_module(self, fqname, fp, pathname, file_info): + suffix, mode, type = file_info + self.msgin(2, "load_module", fqname, fp and "fp", pathname) + if type == _PKG_DIRECTORY: + m = self.load_package(fqname, pathname) + self.msgout(2, "load_module ->", m) + return m + if type == _PY_SOURCE: + co = compile(fp.read(), pathname, 'exec') + elif type == _PY_COMPILED: + try: + data = fp.read() + importlib._bootstrap_external._classify_pyc(data, fqname, {}) + except ImportError as exc: + self.msgout(2, "raise ImportError: " + str(exc), pathname) + raise + co = marshal.loads(memoryview(data)[16:]) + else: + co = None + m = self.add_module(fqname) + m.__file__ = pathname + if co: + if self.replace_paths: + co = self.replace_paths_in_code(co) + m.__code__ = co + self.scan_code(co, m) + self.msgout(2, "load_module ->", m) + return m + + def _add_badmodule(self, name, caller): + if name not in self.badmodules: + self.badmodules[name] = {} + if caller: + self.badmodules[name][caller.__name__] = 1 + else: + self.badmodules[name]["-"] = 1 + + def _safe_import_hook(self, name, caller, fromlist, level=-1): + # wrapper for self.import_hook() that won't raise ImportError + if name in self.badmodules: + self._add_badmodule(name, caller) + return + try: + self.import_hook(name, caller, level=level) + except ImportError as msg: + self.msg(2, "ImportError:", str(msg)) + self._add_badmodule(name, caller) + except SyntaxError as msg: + self.msg(2, "SyntaxError:", str(msg)) + self._add_badmodule(name, caller) + else: + if fromlist: + for sub in fromlist: + fullname = name + "." + sub + if fullname in self.badmodules: + self._add_badmodule(fullname, caller) + continue + try: + self.import_hook(name, caller, [sub], level=level) + except ImportError as msg: + self.msg(2, "ImportError:", str(msg)) + self._add_badmodule(fullname, caller) + + def scan_opcodes(self, co): + # Scan the code, and yield 'interesting' opcode combinations + code = co.co_code + names = co.co_names + consts = co.co_consts + opargs = [(op, arg) for _, op, arg in dis._unpack_opargs(code) + if op != EXTENDED_ARG] + for i, (op, oparg) in enumerate(opargs): + if op in STORE_OPS: + yield "store", (names[oparg],) + continue + if (op == IMPORT_NAME and i >= 2 + and opargs[i-1][0] == opargs[i-2][0] == LOAD_CONST): + level = consts[opargs[i-2][1]] + fromlist = consts[opargs[i-1][1]] + if level == 0: # absolute import + yield "absolute_import", (fromlist, names[oparg]) + else: # relative import + yield "relative_import", (level, fromlist, names[oparg]) + continue + + def scan_code(self, co, m): + code = co.co_code + scanner = self.scan_opcodes + for what, args in scanner(co): + if what == "store": + name, = args + m.globalnames[name] = 1 + elif what == "absolute_import": + fromlist, name = args + have_star = 0 + if fromlist is not None: + if "*" in fromlist: + have_star = 1 + fromlist = [f for f in fromlist if f != "*"] + self._safe_import_hook(name, m, fromlist, level=0) + if have_star: + # We've encountered an "import *". If it is a Python module, + # the code has already been parsed and we can suck out the + # global names. + mm = None + if m.__path__: + # At this point we don't know whether 'name' is a + # submodule of 'm' or a global module. Let's just try + # the full name first. + mm = self.modules.get(m.__name__ + "." + name) + if mm is None: + mm = self.modules.get(name) + if mm is not None: + m.globalnames.update(mm.globalnames) + m.starimports.update(mm.starimports) + if mm.__code__ is None: + m.starimports[name] = 1 + else: + m.starimports[name] = 1 + elif what == "relative_import": + level, fromlist, name = args + if name: + self._safe_import_hook(name, m, fromlist, level=level) + else: + parent = self.determine_parent(m, level=level) + self._safe_import_hook(parent.__name__, None, fromlist, level=0) + else: + # We don't expect anything else from the generator. + raise RuntimeError(what) + + for c in co.co_consts: + if isinstance(c, type(co)): + self.scan_code(c, m) + + def load_package(self, fqname, pathname): + self.msgin(2, "load_package", fqname, pathname) + newname = replacePackageMap.get(fqname) + if newname: + fqname = newname + m = self.add_module(fqname) + m.__file__ = pathname + m.__path__ = [pathname] + + # As per comment at top of file, simulate runtime __path__ additions. + m.__path__ = m.__path__ + packagePathMap.get(fqname, []) + + fp, buf, stuff = self.find_module("__init__", m.__path__) + try: + self.load_module(fqname, fp, buf, stuff) + self.msgout(2, "load_package ->", m) + return m + finally: + if fp: + fp.close() + + def add_module(self, fqname): + if fqname in self.modules: + return self.modules[fqname] + self.modules[fqname] = m = Module(fqname) + return m + + def find_module(self, name, path, parent=None): + if parent is not None: + # assert path is not None + fullname = parent.__name__+'.'+name + else: + fullname = name + if fullname in self.excludes: + self.msgout(3, "find_module -> Excluded", fullname) + raise ImportError(name) + + if path is None: + if name in sys.builtin_module_names: + return (None, None, ("", "", _C_BUILTIN)) + + path = self.path + + return _find_module(name, path) + + def report(self): + """Print a report to stdout, listing the found modules with their + paths, as well as modules that are missing, or seem to be missing. + """ + print() + print(" %-25s %s" % ("Name", "File")) + print(" %-25s %s" % ("----", "----")) + # Print modules found + keys = sorted(self.modules.keys()) + for key in keys: + m = self.modules[key] + if m.__path__: + print("P", end=' ') + else: + print("m", end=' ') + print("%-25s" % key, m.__file__ or "") + + # Print missing modules + missing, maybe = self.any_missing_maybe() + if missing: + print() + print("Missing modules:") + for name in missing: + mods = sorted(self.badmodules[name].keys()) + print("?", name, "imported from", ', '.join(mods)) + # Print modules that may be missing, but then again, maybe not... + if maybe: + print() + print("Submodules that appear to be missing, but could also be", end=' ') + print("global names in the parent package:") + for name in maybe: + mods = sorted(self.badmodules[name].keys()) + print("?", name, "imported from", ', '.join(mods)) + + def any_missing(self): + """Return a list of modules that appear to be missing. Use + any_missing_maybe() if you want to know which modules are + certain to be missing, and which *may* be missing. + """ + missing, maybe = self.any_missing_maybe() + return missing + maybe + + def any_missing_maybe(self): + """Return two lists, one with modules that are certainly missing + and one with modules that *may* be missing. The latter names could + either be submodules *or* just global names in the package. + + The reason it can't always be determined is that it's impossible to + tell which names are imported when "from module import *" is done + with an extension module, short of actually importing it. + """ + missing = [] + maybe = [] + for name in self.badmodules: + if name in self.excludes: + continue + i = name.rfind(".") + if i < 0: + missing.append(name) + continue + subname = name[i+1:] + pkgname = name[:i] + pkg = self.modules.get(pkgname) + if pkg is not None: + if pkgname in self.badmodules[name]: + # The package tried to import this module itself and + # failed. It's definitely missing. + missing.append(name) + elif subname in pkg.globalnames: + # It's a global in the package: definitely not missing. + pass + elif pkg.starimports: + # It could be missing, but the package did an "import *" + # from a non-Python module, so we simply can't be sure. + maybe.append(name) + else: + # It's not a global in the package, the package didn't + # do funny star imports, it's very likely to be missing. + # The symbol could be inserted into the package from the + # outside, but since that's not good style we simply list + # it missing. + missing.append(name) + else: + missing.append(name) + missing.sort() + maybe.sort() + return missing, maybe + + def replace_paths_in_code(self, co): + new_filename = original_filename = os.path.normpath(co.co_filename) + for f, r in self.replace_paths: + if original_filename.startswith(f): + new_filename = r + original_filename[len(f):] + break + + if self.debug and original_filename not in self.processed_paths: + if new_filename != original_filename: + self.msgout(2, "co_filename %r changed to %r" \ + % (original_filename,new_filename,)) + else: + self.msgout(2, "co_filename %r remains unchanged" \ + % (original_filename,)) + self.processed_paths.append(original_filename) + + consts = list(co.co_consts) + for i in range(len(consts)): + if isinstance(consts[i], type(co)): + consts[i] = self.replace_paths_in_code(consts[i]) + + return co.replace(co_consts=tuple(consts), co_filename=new_filename) + + +def test(): + # Parse command line + import getopt + try: + opts, args = getopt.getopt(sys.argv[1:], "dmp:qx:") + except getopt.error as msg: + print(msg) + return + + # Process options + debug = 1 + domods = 0 + addpath = [] + exclude = [] + for o, a in opts: + if o == '-d': + debug = debug + 1 + if o == '-m': + domods = 1 + if o == '-p': + addpath = addpath + a.split(os.pathsep) + if o == '-q': + debug = 0 + if o == '-x': + exclude.append(a) + + # Provide default arguments + if not args: + script = "hello.py" + else: + script = args[0] + + # Set the path based on sys.path and the script directory + path = sys.path[:] + path[0] = os.path.dirname(script) + path = addpath + path + if debug > 1: + print("path:") + for item in path: + print(" ", repr(item)) + + # Create the module finder and turn its crank + mf = ModuleFinder(path, debug, exclude) + for arg in args[1:]: + if arg == '-m': + domods = 1 + continue + if domods: + if arg[-2:] == '.*': + mf.import_hook(arg[:-2], None, ["*"]) + else: + mf.import_hook(arg) + else: + mf.load_file(arg) + mf.run_script(script) + mf.report() + return mf # for -i debugging + + +if __name__ == '__main__': + try: + mf = test() + except KeyboardInterrupt: + print("\n[interrupted]") diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/netrc.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/netrc.py new file mode 100644 index 0000000000000000000000000000000000000000..f0ae48cfed9e67e8192b82fc66c0755a8cfdb270 --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/netrc.py @@ -0,0 +1,139 @@ +"""An object-oriented interface to .netrc files.""" + +# Module and documentation by Eric S. Raymond, 21 Dec 1998 + +import os, shlex, stat + +__all__ = ["netrc", "NetrcParseError"] + + +class NetrcParseError(Exception): + """Exception raised on syntax errors in the .netrc file.""" + def __init__(self, msg, filename=None, lineno=None): + self.filename = filename + self.lineno = lineno + self.msg = msg + Exception.__init__(self, msg) + + def __str__(self): + return "%s (%s, line %s)" % (self.msg, self.filename, self.lineno) + + +class netrc: + def __init__(self, file=None): + default_netrc = file is None + if file is None: + file = os.path.join(os.path.expanduser("~"), ".netrc") + self.hosts = {} + self.macros = {} + with open(file) as fp: + self._parse(file, fp, default_netrc) + + def _parse(self, file, fp, default_netrc): + lexer = shlex.shlex(fp) + lexer.wordchars += r"""!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~""" + lexer.commenters = lexer.commenters.replace('#', '') + while 1: + # Look for a machine, default, or macdef top-level keyword + saved_lineno = lexer.lineno + toplevel = tt = lexer.get_token() + if not tt: + break + elif tt[0] == '#': + if lexer.lineno == saved_lineno and len(tt) == 1: + lexer.instream.readline() + continue + elif tt == 'machine': + entryname = lexer.get_token() + elif tt == 'default': + entryname = 'default' + elif tt == 'macdef': # Just skip to end of macdefs + entryname = lexer.get_token() + self.macros[entryname] = [] + lexer.whitespace = ' \t' + while 1: + line = lexer.instream.readline() + if not line or line == '\012': + lexer.whitespace = ' \t\r\n' + break + self.macros[entryname].append(line) + continue + else: + raise NetrcParseError( + "bad toplevel token %r" % tt, file, lexer.lineno) + + # We're looking at start of an entry for a named machine or default. + login = '' + account = password = None + self.hosts[entryname] = {} + while 1: + tt = lexer.get_token() + if (tt.startswith('#') or + tt in {'', 'machine', 'default', 'macdef'}): + if password: + self.hosts[entryname] = (login, account, password) + lexer.push_token(tt) + break + else: + raise NetrcParseError( + "malformed %s entry %s terminated by %s" + % (toplevel, entryname, repr(tt)), + file, lexer.lineno) + elif tt == 'login' or tt == 'user': + login = lexer.get_token() + elif tt == 'account': + account = lexer.get_token() + elif tt == 'password': + if os.name == 'posix' and default_netrc: + prop = os.fstat(fp.fileno()) + if prop.st_uid != os.getuid(): + import pwd + try: + fowner = pwd.getpwuid(prop.st_uid)[0] + except KeyError: + fowner = 'uid %s' % prop.st_uid + try: + user = pwd.getpwuid(os.getuid())[0] + except KeyError: + user = 'uid %s' % os.getuid() + raise NetrcParseError( + ("~/.netrc file owner (%s) does not match" + " current user (%s)") % (fowner, user), + file, lexer.lineno) + if (prop.st_mode & (stat.S_IRWXG | stat.S_IRWXO)): + raise NetrcParseError( + "~/.netrc access too permissive: access" + " permissions must restrict access to only" + " the owner", file, lexer.lineno) + password = lexer.get_token() + else: + raise NetrcParseError("bad follower token %r" % tt, + file, lexer.lineno) + + def authenticators(self, host): + """Return a (user, account, password) tuple for given host.""" + if host in self.hosts: + return self.hosts[host] + elif 'default' in self.hosts: + return self.hosts['default'] + else: + return None + + def __repr__(self): + """Dump the class data in the format of a .netrc file.""" + rep = "" + for host in self.hosts.keys(): + attrs = self.hosts[host] + rep += f"machine {host}\n\tlogin {attrs[0]}\n" + if attrs[1]: + rep += f"\taccount {attrs[1]}\n" + rep += f"\tpassword {attrs[2]}\n" + for macro in self.macros.keys(): + rep += f"macdef {macro}\n" + for line in self.macros[macro]: + rep += line + rep += "\n" + return rep + +if __name__ == '__main__': + print(netrc()) diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/nntplib.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/nntplib.py new file mode 100644 index 0000000000000000000000000000000000000000..9036f361b5fb6b9e631b713016f1d341baed8a7b --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/nntplib.py @@ -0,0 +1,1151 @@ +"""An NNTP client class based on: +- RFC 977: Network News Transfer Protocol +- RFC 2980: Common NNTP Extensions +- RFC 3977: Network News Transfer Protocol (version 2) + +Example: + +>>> from nntplib import NNTP +>>> s = NNTP('news') +>>> resp, count, first, last, name = s.group('comp.lang.python') +>>> print('Group', name, 'has', count, 'articles, range', first, 'to', last) +Group comp.lang.python has 51 articles, range 5770 to 5821 +>>> resp, subs = s.xhdr('subject', '{0}-{1}'.format(first, last)) +>>> resp = s.quit() +>>> + +Here 'resp' is the server response line. +Error responses are turned into exceptions. + +To post an article from a file: +>>> f = open(filename, 'rb') # file containing article, including header +>>> resp = s.post(f) +>>> + +For descriptions of all methods, read the comments in the code below. +Note that all arguments and return values representing article numbers +are strings, not numbers, since they are rarely used for calculations. +""" + +# RFC 977 by Brian Kantor and Phil Lapsley. +# xover, xgtitle, xpath, date methods by Kevan Heydon + +# Incompatible changes from the 2.x nntplib: +# - all commands are encoded as UTF-8 data (using the "surrogateescape" +# error handler), except for raw message data (POST, IHAVE) +# - all responses are decoded as UTF-8 data (using the "surrogateescape" +# error handler), except for raw message data (ARTICLE, HEAD, BODY) +# - the `file` argument to various methods is keyword-only +# +# - NNTP.date() returns a datetime object +# - NNTP.newgroups() and NNTP.newnews() take a datetime (or date) object, +# rather than a pair of (date, time) strings. +# - NNTP.newgroups() and NNTP.list() return a list of GroupInfo named tuples +# - NNTP.descriptions() returns a dict mapping group names to descriptions +# - NNTP.xover() returns a list of dicts mapping field names (header or metadata) +# to field values; each dict representing a message overview. +# - NNTP.article(), NNTP.head() and NNTP.body() return a (response, ArticleInfo) +# tuple. +# - the "internal" methods have been marked private (they now start with +# an underscore) + +# Other changes from the 2.x/3.1 nntplib: +# - automatic querying of capabilities at connect +# - New method NNTP.getcapabilities() +# - New method NNTP.over() +# - New helper function decode_header() +# - NNTP.post() and NNTP.ihave() accept file objects, bytes-like objects and +# arbitrary iterables yielding lines. +# - An extensive test suite :-) + +# TODO: +# - return structured data (GroupInfo etc.) everywhere +# - support HDR + +# Imports +import re +import socket +import collections +import datetime +import warnings +import sys + +try: + import ssl +except ImportError: + _have_ssl = False +else: + _have_ssl = True + +from email.header import decode_header as _email_decode_header +from socket import _GLOBAL_DEFAULT_TIMEOUT + +__all__ = ["NNTP", + "NNTPError", "NNTPReplyError", "NNTPTemporaryError", + "NNTPPermanentError", "NNTPProtocolError", "NNTPDataError", + "decode_header", + ] + +# maximal line length when calling readline(). This is to prevent +# reading arbitrary length lines. RFC 3977 limits NNTP line length to +# 512 characters, including CRLF. We have selected 2048 just to be on +# the safe side. +_MAXLINE = 2048 + + +# Exceptions raised when an error or invalid response is received +class NNTPError(Exception): + """Base class for all nntplib exceptions""" + def __init__(self, *args): + Exception.__init__(self, *args) + try: + self.response = args[0] + except IndexError: + self.response = 'No response given' + +class NNTPReplyError(NNTPError): + """Unexpected [123]xx reply""" + pass + +class NNTPTemporaryError(NNTPError): + """4xx errors""" + pass + +class NNTPPermanentError(NNTPError): + """5xx errors""" + pass + +class NNTPProtocolError(NNTPError): + """Response does not begin with [1-5]""" + pass + +class NNTPDataError(NNTPError): + """Error in response data""" + pass + + +# Standard port used by NNTP servers +NNTP_PORT = 119 +NNTP_SSL_PORT = 563 + +# Response numbers that are followed by additional text (e.g. article) +_LONGRESP = { + '100', # HELP + '101', # CAPABILITIES + '211', # LISTGROUP (also not multi-line with GROUP) + '215', # LIST + '220', # ARTICLE + '221', # HEAD, XHDR + '222', # BODY + '224', # OVER, XOVER + '225', # HDR + '230', # NEWNEWS + '231', # NEWGROUPS + '282', # XGTITLE +} + +# Default decoded value for LIST OVERVIEW.FMT if not supported +_DEFAULT_OVERVIEW_FMT = [ + "subject", "from", "date", "message-id", "references", ":bytes", ":lines"] + +# Alternative names allowed in LIST OVERVIEW.FMT response +_OVERVIEW_FMT_ALTERNATIVES = { + 'bytes': ':bytes', + 'lines': ':lines', +} + +# Line terminators (we always output CRLF, but accept any of CRLF, CR, LF) +_CRLF = b'\r\n' + +GroupInfo = collections.namedtuple('GroupInfo', + ['group', 'last', 'first', 'flag']) + +ArticleInfo = collections.namedtuple('ArticleInfo', + ['number', 'message_id', 'lines']) + + +# Helper function(s) +def decode_header(header_str): + """Takes a unicode string representing a munged header value + and decodes it as a (possibly non-ASCII) readable value.""" + parts = [] + for v, enc in _email_decode_header(header_str): + if isinstance(v, bytes): + parts.append(v.decode(enc or 'ascii')) + else: + parts.append(v) + return ''.join(parts) + +def _parse_overview_fmt(lines): + """Parse a list of string representing the response to LIST OVERVIEW.FMT + and return a list of header/metadata names. + Raises NNTPDataError if the response is not compliant + (cf. RFC 3977, section 8.4).""" + fmt = [] + for line in lines: + if line[0] == ':': + # Metadata name (e.g. ":bytes") + name, _, suffix = line[1:].partition(':') + name = ':' + name + else: + # Header name (e.g. "Subject:" or "Xref:full") + name, _, suffix = line.partition(':') + name = name.lower() + name = _OVERVIEW_FMT_ALTERNATIVES.get(name, name) + # Should we do something with the suffix? + fmt.append(name) + defaults = _DEFAULT_OVERVIEW_FMT + if len(fmt) < len(defaults): + raise NNTPDataError("LIST OVERVIEW.FMT response too short") + if fmt[:len(defaults)] != defaults: + raise NNTPDataError("LIST OVERVIEW.FMT redefines default fields") + return fmt + +def _parse_overview(lines, fmt, data_process_func=None): + """Parse the response to an OVER or XOVER command according to the + overview format `fmt`.""" + n_defaults = len(_DEFAULT_OVERVIEW_FMT) + overview = [] + for line in lines: + fields = {} + article_number, *tokens = line.split('\t') + article_number = int(article_number) + for i, token in enumerate(tokens): + if i >= len(fmt): + # XXX should we raise an error? Some servers might not + # support LIST OVERVIEW.FMT and still return additional + # headers. + continue + field_name = fmt[i] + is_metadata = field_name.startswith(':') + if i >= n_defaults and not is_metadata: + # Non-default header names are included in full in the response + # (unless the field is totally empty) + h = field_name + ": " + if token and token[:len(h)].lower() != h: + raise NNTPDataError("OVER/XOVER response doesn't include " + "names of additional headers") + token = token[len(h):] if token else None + fields[fmt[i]] = token + overview.append((article_number, fields)) + return overview + +def _parse_datetime(date_str, time_str=None): + """Parse a pair of (date, time) strings, and return a datetime object. + If only the date is given, it is assumed to be date and time + concatenated together (e.g. response to the DATE command). + """ + if time_str is None: + time_str = date_str[-6:] + date_str = date_str[:-6] + hours = int(time_str[:2]) + minutes = int(time_str[2:4]) + seconds = int(time_str[4:]) + year = int(date_str[:-4]) + month = int(date_str[-4:-2]) + day = int(date_str[-2:]) + # RFC 3977 doesn't say how to interpret 2-char years. Assume that + # there are no dates before 1970 on Usenet. + if year < 70: + year += 2000 + elif year < 100: + year += 1900 + return datetime.datetime(year, month, day, hours, minutes, seconds) + +def _unparse_datetime(dt, legacy=False): + """Format a date or datetime object as a pair of (date, time) strings + in the format required by the NEWNEWS and NEWGROUPS commands. If a + date object is passed, the time is assumed to be midnight (00h00). + + The returned representation depends on the legacy flag: + * if legacy is False (the default): + date has the YYYYMMDD format and time the HHMMSS format + * if legacy is True: + date has the YYMMDD format and time the HHMMSS format. + RFC 3977 compliant servers should understand both formats; therefore, + legacy is only needed when talking to old servers. + """ + if not isinstance(dt, datetime.datetime): + time_str = "000000" + else: + time_str = "{0.hour:02d}{0.minute:02d}{0.second:02d}".format(dt) + y = dt.year + if legacy: + y = y % 100 + date_str = "{0:02d}{1.month:02d}{1.day:02d}".format(y, dt) + else: + date_str = "{0:04d}{1.month:02d}{1.day:02d}".format(y, dt) + return date_str, time_str + + +if _have_ssl: + + def _encrypt_on(sock, context, hostname): + """Wrap a socket in SSL/TLS. Arguments: + - sock: Socket to wrap + - context: SSL context to use for the encrypted connection + Returns: + - sock: New, encrypted socket. + """ + # Generate a default SSL context if none was passed. + if context is None: + context = ssl._create_stdlib_context() + return context.wrap_socket(sock, server_hostname=hostname) + + +# The classes themselves +class _NNTPBase: + # UTF-8 is the character set for all NNTP commands and responses: they + # are automatically encoded (when sending) and decoded (and receiving) + # by this class. + # However, some multi-line data blocks can contain arbitrary bytes (for + # example, latin-1 or utf-16 data in the body of a message). Commands + # taking (POST, IHAVE) or returning (HEAD, BODY, ARTICLE) raw message + # data will therefore only accept and produce bytes objects. + # Furthermore, since there could be non-compliant servers out there, + # we use 'surrogateescape' as the error handler for fault tolerance + # and easy round-tripping. This could be useful for some applications + # (e.g. NNTP gateways). + + encoding = 'utf-8' + errors = 'surrogateescape' + + def __init__(self, file, host, + readermode=None, timeout=_GLOBAL_DEFAULT_TIMEOUT): + """Initialize an instance. Arguments: + - file: file-like object (open for read/write in binary mode) + - host: hostname of the server + - readermode: if true, send 'mode reader' command after + connecting. + - timeout: timeout (in seconds) used for socket connections + + readermode is sometimes necessary if you are connecting to an + NNTP server on the local machine and intend to call + reader-specific commands, such as `group'. If you get + unexpected NNTPPermanentErrors, you might need to set + readermode. + """ + self.host = host + self.file = file + self.debugging = 0 + self.welcome = self._getresp() + + # Inquire about capabilities (RFC 3977). + self._caps = None + self.getcapabilities() + + # 'MODE READER' is sometimes necessary to enable 'reader' mode. + # However, the order in which 'MODE READER' and 'AUTHINFO' need to + # arrive differs between some NNTP servers. If _setreadermode() fails + # with an authorization failed error, it will set this to True; + # the login() routine will interpret that as a request to try again + # after performing its normal function. + # Enable only if we're not already in READER mode anyway. + self.readermode_afterauth = False + if readermode and 'READER' not in self._caps: + self._setreadermode() + if not self.readermode_afterauth: + # Capabilities might have changed after MODE READER + self._caps = None + self.getcapabilities() + + # RFC 4642 2.2.2: Both the client and the server MUST know if there is + # a TLS session active. A client MUST NOT attempt to start a TLS + # session if a TLS session is already active. + self.tls_on = False + + # Log in and encryption setup order is left to subclasses. + self.authenticated = False + + def __enter__(self): + return self + + def __exit__(self, *args): + is_connected = lambda: hasattr(self, "file") + if is_connected(): + try: + self.quit() + except (OSError, EOFError): + pass + finally: + if is_connected(): + self._close() + + def getwelcome(self): + """Get the welcome message from the server + (this is read and squirreled away by __init__()). + If the response code is 200, posting is allowed; + if it 201, posting is not allowed.""" + + if self.debugging: print('*welcome*', repr(self.welcome)) + return self.welcome + + def getcapabilities(self): + """Get the server capabilities, as read by __init__(). + If the CAPABILITIES command is not supported, an empty dict is + returned.""" + if self._caps is None: + self.nntp_version = 1 + self.nntp_implementation = None + try: + resp, caps = self.capabilities() + except (NNTPPermanentError, NNTPTemporaryError): + # Server doesn't support capabilities + self._caps = {} + else: + self._caps = caps + if 'VERSION' in caps: + # The server can advertise several supported versions, + # choose the highest. + self.nntp_version = max(map(int, caps['VERSION'])) + if 'IMPLEMENTATION' in caps: + self.nntp_implementation = ' '.join(caps['IMPLEMENTATION']) + return self._caps + + def set_debuglevel(self, level): + """Set the debugging level. Argument 'level' means: + 0: no debugging output (default) + 1: print commands and responses but not body text etc. + 2: also print raw lines read and sent before stripping CR/LF""" + + self.debugging = level + debug = set_debuglevel + + def _putline(self, line): + """Internal: send one line to the server, appending CRLF. + The `line` must be a bytes-like object.""" + sys.audit("nntplib.putline", self, line) + line = line + _CRLF + if self.debugging > 1: print('*put*', repr(line)) + self.file.write(line) + self.file.flush() + + def _putcmd(self, line): + """Internal: send one command to the server (through _putline()). + The `line` must be a unicode string.""" + if self.debugging: print('*cmd*', repr(line)) + line = line.encode(self.encoding, self.errors) + self._putline(line) + + def _getline(self, strip_crlf=True): + """Internal: return one line from the server, stripping _CRLF. + Raise EOFError if the connection is closed. + Returns a bytes object.""" + line = self.file.readline(_MAXLINE +1) + if len(line) > _MAXLINE: + raise NNTPDataError('line too long') + if self.debugging > 1: + print('*get*', repr(line)) + if not line: raise EOFError + if strip_crlf: + if line[-2:] == _CRLF: + line = line[:-2] + elif line[-1:] in _CRLF: + line = line[:-1] + return line + + def _getresp(self): + """Internal: get a response from the server. + Raise various errors if the response indicates an error. + Returns a unicode string.""" + resp = self._getline() + if self.debugging: print('*resp*', repr(resp)) + resp = resp.decode(self.encoding, self.errors) + c = resp[:1] + if c == '4': + raise NNTPTemporaryError(resp) + if c == '5': + raise NNTPPermanentError(resp) + if c not in '123': + raise NNTPProtocolError(resp) + return resp + + def _getlongresp(self, file=None): + """Internal: get a response plus following text from the server. + Raise various errors if the response indicates an error. + + Returns a (response, lines) tuple where `response` is a unicode + string and `lines` is a list of bytes objects. + If `file` is a file-like object, it must be open in binary mode. + """ + + openedFile = None + try: + # If a string was passed then open a file with that name + if isinstance(file, (str, bytes)): + openedFile = file = open(file, "wb") + + resp = self._getresp() + if resp[:3] not in _LONGRESP: + raise NNTPReplyError(resp) + + lines = [] + if file is not None: + # XXX lines = None instead? + terminators = (b'.' + _CRLF, b'.\n') + while 1: + line = self._getline(False) + if line in terminators: + break + if line.startswith(b'..'): + line = line[1:] + file.write(line) + else: + terminator = b'.' + while 1: + line = self._getline() + if line == terminator: + break + if line.startswith(b'..'): + line = line[1:] + lines.append(line) + finally: + # If this method created the file, then it must close it + if openedFile: + openedFile.close() + + return resp, lines + + def _shortcmd(self, line): + """Internal: send a command and get the response. + Same return value as _getresp().""" + self._putcmd(line) + return self._getresp() + + def _longcmd(self, line, file=None): + """Internal: send a command and get the response plus following text. + Same return value as _getlongresp().""" + self._putcmd(line) + return self._getlongresp(file) + + def _longcmdstring(self, line, file=None): + """Internal: send a command and get the response plus following text. + Same as _longcmd() and _getlongresp(), except that the returned `lines` + are unicode strings rather than bytes objects. + """ + self._putcmd(line) + resp, list = self._getlongresp(file) + return resp, [line.decode(self.encoding, self.errors) + for line in list] + + def _getoverviewfmt(self): + """Internal: get the overview format. Queries the server if not + already done, else returns the cached value.""" + try: + return self._cachedoverviewfmt + except AttributeError: + pass + try: + resp, lines = self._longcmdstring("LIST OVERVIEW.FMT") + except NNTPPermanentError: + # Not supported by server? + fmt = _DEFAULT_OVERVIEW_FMT[:] + else: + fmt = _parse_overview_fmt(lines) + self._cachedoverviewfmt = fmt + return fmt + + def _grouplist(self, lines): + # Parse lines into "group last first flag" + return [GroupInfo(*line.split()) for line in lines] + + def capabilities(self): + """Process a CAPABILITIES command. Not supported by all servers. + Return: + - resp: server response if successful + - caps: a dictionary mapping capability names to lists of tokens + (for example {'VERSION': ['2'], 'OVER': [], LIST: ['ACTIVE', 'HEADERS'] }) + """ + caps = {} + resp, lines = self._longcmdstring("CAPABILITIES") + for line in lines: + name, *tokens = line.split() + caps[name] = tokens + return resp, caps + + def newgroups(self, date, *, file=None): + """Process a NEWGROUPS command. Arguments: + - date: a date or datetime object + Return: + - resp: server response if successful + - list: list of newsgroup names + """ + if not isinstance(date, (datetime.date, datetime.date)): + raise TypeError( + "the date parameter must be a date or datetime object, " + "not '{:40}'".format(date.__class__.__name__)) + date_str, time_str = _unparse_datetime(date, self.nntp_version < 2) + cmd = 'NEWGROUPS {0} {1}'.format(date_str, time_str) + resp, lines = self._longcmdstring(cmd, file) + return resp, self._grouplist(lines) + + def newnews(self, group, date, *, file=None): + """Process a NEWNEWS command. Arguments: + - group: group name or '*' + - date: a date or datetime object + Return: + - resp: server response if successful + - list: list of message ids + """ + if not isinstance(date, (datetime.date, datetime.date)): + raise TypeError( + "the date parameter must be a date or datetime object, " + "not '{:40}'".format(date.__class__.__name__)) + date_str, time_str = _unparse_datetime(date, self.nntp_version < 2) + cmd = 'NEWNEWS {0} {1} {2}'.format(group, date_str, time_str) + return self._longcmdstring(cmd, file) + + def list(self, group_pattern=None, *, file=None): + """Process a LIST or LIST ACTIVE command. Arguments: + - group_pattern: a pattern indicating which groups to query + - file: Filename string or file object to store the result in + Returns: + - resp: server response if successful + - list: list of (group, last, first, flag) (strings) + """ + if group_pattern is not None: + command = 'LIST ACTIVE ' + group_pattern + else: + command = 'LIST' + resp, lines = self._longcmdstring(command, file) + return resp, self._grouplist(lines) + + def _getdescriptions(self, group_pattern, return_all): + line_pat = re.compile('^(?P[^ \t]+)[ \t]+(.*)$') + # Try the more std (acc. to RFC2980) LIST NEWSGROUPS first + resp, lines = self._longcmdstring('LIST NEWSGROUPS ' + group_pattern) + if not resp.startswith('215'): + # Now the deprecated XGTITLE. This either raises an error + # or succeeds with the same output structure as LIST + # NEWSGROUPS. + resp, lines = self._longcmdstring('XGTITLE ' + group_pattern) + groups = {} + for raw_line in lines: + match = line_pat.search(raw_line.strip()) + if match: + name, desc = match.group(1, 2) + if not return_all: + return desc + groups[name] = desc + if return_all: + return resp, groups + else: + # Nothing found + return '' + + def description(self, group): + """Get a description for a single group. If more than one + group matches ('group' is a pattern), return the first. If no + group matches, return an empty string. + + This elides the response code from the server, since it can + only be '215' or '285' (for xgtitle) anyway. If the response + code is needed, use the 'descriptions' method. + + NOTE: This neither checks for a wildcard in 'group' nor does + it check whether the group actually exists.""" + return self._getdescriptions(group, False) + + def descriptions(self, group_pattern): + """Get descriptions for a range of groups.""" + return self._getdescriptions(group_pattern, True) + + def group(self, name): + """Process a GROUP command. Argument: + - group: the group name + Returns: + - resp: server response if successful + - count: number of articles + - first: first article number + - last: last article number + - name: the group name + """ + resp = self._shortcmd('GROUP ' + name) + if not resp.startswith('211'): + raise NNTPReplyError(resp) + words = resp.split() + count = first = last = 0 + n = len(words) + if n > 1: + count = words[1] + if n > 2: + first = words[2] + if n > 3: + last = words[3] + if n > 4: + name = words[4].lower() + return resp, int(count), int(first), int(last), name + + def help(self, *, file=None): + """Process a HELP command. Argument: + - file: Filename string or file object to store the result in + Returns: + - resp: server response if successful + - list: list of strings returned by the server in response to the + HELP command + """ + return self._longcmdstring('HELP', file) + + def _statparse(self, resp): + """Internal: parse the response line of a STAT, NEXT, LAST, + ARTICLE, HEAD or BODY command.""" + if not resp.startswith('22'): + raise NNTPReplyError(resp) + words = resp.split() + art_num = int(words[1]) + message_id = words[2] + return resp, art_num, message_id + + def _statcmd(self, line): + """Internal: process a STAT, NEXT or LAST command.""" + resp = self._shortcmd(line) + return self._statparse(resp) + + def stat(self, message_spec=None): + """Process a STAT command. Argument: + - message_spec: article number or message id (if not specified, + the current article is selected) + Returns: + - resp: server response if successful + - art_num: the article number + - message_id: the message id + """ + if message_spec: + return self._statcmd('STAT {0}'.format(message_spec)) + else: + return self._statcmd('STAT') + + def next(self): + """Process a NEXT command. No arguments. Return as for STAT.""" + return self._statcmd('NEXT') + + def last(self): + """Process a LAST command. No arguments. Return as for STAT.""" + return self._statcmd('LAST') + + def _artcmd(self, line, file=None): + """Internal: process a HEAD, BODY or ARTICLE command.""" + resp, lines = self._longcmd(line, file) + resp, art_num, message_id = self._statparse(resp) + return resp, ArticleInfo(art_num, message_id, lines) + + def head(self, message_spec=None, *, file=None): + """Process a HEAD command. Argument: + - message_spec: article number or message id + - file: filename string or file object to store the headers in + Returns: + - resp: server response if successful + - ArticleInfo: (article number, message id, list of header lines) + """ + if message_spec is not None: + cmd = 'HEAD {0}'.format(message_spec) + else: + cmd = 'HEAD' + return self._artcmd(cmd, file) + + def body(self, message_spec=None, *, file=None): + """Process a BODY command. Argument: + - message_spec: article number or message id + - file: filename string or file object to store the body in + Returns: + - resp: server response if successful + - ArticleInfo: (article number, message id, list of body lines) + """ + if message_spec is not None: + cmd = 'BODY {0}'.format(message_spec) + else: + cmd = 'BODY' + return self._artcmd(cmd, file) + + def article(self, message_spec=None, *, file=None): + """Process an ARTICLE command. Argument: + - message_spec: article number or message id + - file: filename string or file object to store the article in + Returns: + - resp: server response if successful + - ArticleInfo: (article number, message id, list of article lines) + """ + if message_spec is not None: + cmd = 'ARTICLE {0}'.format(message_spec) + else: + cmd = 'ARTICLE' + return self._artcmd(cmd, file) + + def slave(self): + """Process a SLAVE command. Returns: + - resp: server response if successful + """ + return self._shortcmd('SLAVE') + + def xhdr(self, hdr, str, *, file=None): + """Process an XHDR command (optional server extension). Arguments: + - hdr: the header type (e.g. 'subject') + - str: an article nr, a message id, or a range nr1-nr2 + - file: Filename string or file object to store the result in + Returns: + - resp: server response if successful + - list: list of (nr, value) strings + """ + pat = re.compile('^([0-9]+) ?(.*)\n?') + resp, lines = self._longcmdstring('XHDR {0} {1}'.format(hdr, str), file) + def remove_number(line): + m = pat.match(line) + return m.group(1, 2) if m else line + return resp, [remove_number(line) for line in lines] + + def xover(self, start, end, *, file=None): + """Process an XOVER command (optional server extension) Arguments: + - start: start of range + - end: end of range + - file: Filename string or file object to store the result in + Returns: + - resp: server response if successful + - list: list of dicts containing the response fields + """ + resp, lines = self._longcmdstring('XOVER {0}-{1}'.format(start, end), + file) + fmt = self._getoverviewfmt() + return resp, _parse_overview(lines, fmt) + + def over(self, message_spec, *, file=None): + """Process an OVER command. If the command isn't supported, fall + back to XOVER. Arguments: + - message_spec: + - either a message id, indicating the article to fetch + information about + - or a (start, end) tuple, indicating a range of article numbers; + if end is None, information up to the newest message will be + retrieved + - or None, indicating the current article number must be used + - file: Filename string or file object to store the result in + Returns: + - resp: server response if successful + - list: list of dicts containing the response fields + + NOTE: the "message id" form isn't supported by XOVER + """ + cmd = 'OVER' if 'OVER' in self._caps else 'XOVER' + if isinstance(message_spec, (tuple, list)): + start, end = message_spec + cmd += ' {0}-{1}'.format(start, end or '') + elif message_spec is not None: + cmd = cmd + ' ' + message_spec + resp, lines = self._longcmdstring(cmd, file) + fmt = self._getoverviewfmt() + return resp, _parse_overview(lines, fmt) + + def xgtitle(self, group, *, file=None): + """Process an XGTITLE command (optional server extension) Arguments: + - group: group name wildcard (i.e. news.*) + Returns: + - resp: server response if successful + - list: list of (name,title) strings""" + warnings.warn("The XGTITLE extension is not actively used, " + "use descriptions() instead", + DeprecationWarning, 2) + line_pat = re.compile('^([^ \t]+)[ \t]+(.*)$') + resp, raw_lines = self._longcmdstring('XGTITLE ' + group, file) + lines = [] + for raw_line in raw_lines: + match = line_pat.search(raw_line.strip()) + if match: + lines.append(match.group(1, 2)) + return resp, lines + + def xpath(self, id): + """Process an XPATH command (optional server extension) Arguments: + - id: Message id of article + Returns: + resp: server response if successful + path: directory path to article + """ + warnings.warn("The XPATH extension is not actively used", + DeprecationWarning, 2) + + resp = self._shortcmd('XPATH {0}'.format(id)) + if not resp.startswith('223'): + raise NNTPReplyError(resp) + try: + [resp_num, path] = resp.split() + except ValueError: + raise NNTPReplyError(resp) from None + else: + return resp, path + + def date(self): + """Process the DATE command. + Returns: + - resp: server response if successful + - date: datetime object + """ + resp = self._shortcmd("DATE") + if not resp.startswith('111'): + raise NNTPReplyError(resp) + elem = resp.split() + if len(elem) != 2: + raise NNTPDataError(resp) + date = elem[1] + if len(date) != 14: + raise NNTPDataError(resp) + return resp, _parse_datetime(date, None) + + def _post(self, command, f): + resp = self._shortcmd(command) + # Raises a specific exception if posting is not allowed + if not resp.startswith('3'): + raise NNTPReplyError(resp) + if isinstance(f, (bytes, bytearray)): + f = f.splitlines() + # We don't use _putline() because: + # - we don't want additional CRLF if the file or iterable is already + # in the right format + # - we don't want a spurious flush() after each line is written + for line in f: + if not line.endswith(_CRLF): + line = line.rstrip(b"\r\n") + _CRLF + if line.startswith(b'.'): + line = b'.' + line + self.file.write(line) + self.file.write(b".\r\n") + self.file.flush() + return self._getresp() + + def post(self, data): + """Process a POST command. Arguments: + - data: bytes object, iterable or file containing the article + Returns: + - resp: server response if successful""" + return self._post('POST', data) + + def ihave(self, message_id, data): + """Process an IHAVE command. Arguments: + - message_id: message-id of the article + - data: file containing the article + Returns: + - resp: server response if successful + Note that if the server refuses the article an exception is raised.""" + return self._post('IHAVE {0}'.format(message_id), data) + + def _close(self): + self.file.close() + del self.file + + def quit(self): + """Process a QUIT command and close the socket. Returns: + - resp: server response if successful""" + try: + resp = self._shortcmd('QUIT') + finally: + self._close() + return resp + + def login(self, user=None, password=None, usenetrc=True): + if self.authenticated: + raise ValueError("Already logged in.") + if not user and not usenetrc: + raise ValueError( + "At least one of `user` and `usenetrc` must be specified") + # If no login/password was specified but netrc was requested, + # try to get them from ~/.netrc + # Presume that if .netrc has an entry, NNRP authentication is required. + try: + if usenetrc and not user: + import netrc + credentials = netrc.netrc() + auth = credentials.authenticators(self.host) + if auth: + user = auth[0] + password = auth[2] + except OSError: + pass + # Perform NNTP authentication if needed. + if not user: + return + resp = self._shortcmd('authinfo user ' + user) + if resp.startswith('381'): + if not password: + raise NNTPReplyError(resp) + else: + resp = self._shortcmd('authinfo pass ' + password) + if not resp.startswith('281'): + raise NNTPPermanentError(resp) + # Capabilities might have changed after login + self._caps = None + self.getcapabilities() + # Attempt to send mode reader if it was requested after login. + # Only do so if we're not in reader mode already. + if self.readermode_afterauth and 'READER' not in self._caps: + self._setreadermode() + # Capabilities might have changed after MODE READER + self._caps = None + self.getcapabilities() + + def _setreadermode(self): + try: + self.welcome = self._shortcmd('mode reader') + except NNTPPermanentError: + # Error 5xx, probably 'not implemented' + pass + except NNTPTemporaryError as e: + if e.response.startswith('480'): + # Need authorization before 'mode reader' + self.readermode_afterauth = True + else: + raise + + if _have_ssl: + def starttls(self, context=None): + """Process a STARTTLS command. Arguments: + - context: SSL context to use for the encrypted connection + """ + # Per RFC 4642, STARTTLS MUST NOT be sent after authentication or if + # a TLS session already exists. + if self.tls_on: + raise ValueError("TLS is already enabled.") + if self.authenticated: + raise ValueError("TLS cannot be started after authentication.") + resp = self._shortcmd('STARTTLS') + if resp.startswith('382'): + self.file.close() + self.sock = _encrypt_on(self.sock, context, self.host) + self.file = self.sock.makefile("rwb") + self.tls_on = True + # Capabilities may change after TLS starts up, so ask for them + # again. + self._caps = None + self.getcapabilities() + else: + raise NNTPError("TLS failed to start.") + + +class NNTP(_NNTPBase): + + def __init__(self, host, port=NNTP_PORT, user=None, password=None, + readermode=None, usenetrc=False, + timeout=_GLOBAL_DEFAULT_TIMEOUT): + """Initialize an instance. Arguments: + - host: hostname to connect to + - port: port to connect to (default the standard NNTP port) + - user: username to authenticate with + - password: password to use with username + - readermode: if true, send 'mode reader' command after + connecting. + - usenetrc: allow loading username and password from ~/.netrc file + if not specified explicitly + - timeout: timeout (in seconds) used for socket connections + + readermode is sometimes necessary if you are connecting to an + NNTP server on the local machine and intend to call + reader-specific commands, such as `group'. If you get + unexpected NNTPPermanentErrors, you might need to set + readermode. + """ + self.host = host + self.port = port + sys.audit("nntplib.connect", self, host, port) + self.sock = socket.create_connection((host, port), timeout) + file = None + try: + file = self.sock.makefile("rwb") + _NNTPBase.__init__(self, file, host, + readermode, timeout) + if user or usenetrc: + self.login(user, password, usenetrc) + except: + if file: + file.close() + self.sock.close() + raise + + def _close(self): + try: + _NNTPBase._close(self) + finally: + self.sock.close() + + +if _have_ssl: + class NNTP_SSL(_NNTPBase): + + def __init__(self, host, port=NNTP_SSL_PORT, + user=None, password=None, ssl_context=None, + readermode=None, usenetrc=False, + timeout=_GLOBAL_DEFAULT_TIMEOUT): + """This works identically to NNTP.__init__, except for the change + in default port and the `ssl_context` argument for SSL connections. + """ + sys.audit("nntplib.connect", self, host, port) + self.sock = socket.create_connection((host, port), timeout) + file = None + try: + self.sock = _encrypt_on(self.sock, ssl_context, host) + file = self.sock.makefile("rwb") + _NNTPBase.__init__(self, file, host, + readermode=readermode, timeout=timeout) + if user or usenetrc: + self.login(user, password, usenetrc) + except: + if file: + file.close() + self.sock.close() + raise + + def _close(self): + try: + _NNTPBase._close(self) + finally: + self.sock.close() + + __all__.append("NNTP_SSL") + + +# Test retrieval when run as a script. +if __name__ == '__main__': + import argparse + + parser = argparse.ArgumentParser(description="""\ + nntplib built-in demo - display the latest articles in a newsgroup""") + parser.add_argument('-g', '--group', default='gmane.comp.python.general', + help='group to fetch messages from (default: %(default)s)') + parser.add_argument('-s', '--server', default='news.gmane.io', + help='NNTP server hostname (default: %(default)s)') + parser.add_argument('-p', '--port', default=-1, type=int, + help='NNTP port number (default: %s / %s)' % (NNTP_PORT, NNTP_SSL_PORT)) + parser.add_argument('-n', '--nb-articles', default=10, type=int, + help='number of articles to fetch (default: %(default)s)') + parser.add_argument('-S', '--ssl', action='store_true', default=False, + help='use NNTP over SSL') + args = parser.parse_args() + + port = args.port + if not args.ssl: + if port == -1: + port = NNTP_PORT + s = NNTP(host=args.server, port=port) + else: + if port == -1: + port = NNTP_SSL_PORT + s = NNTP_SSL(host=args.server, port=port) + + caps = s.getcapabilities() + if 'STARTTLS' in caps: + s.starttls() + resp, count, first, last, name = s.group(args.group) + print('Group', name, 'has', count, 'articles, range', first, 'to', last) + + def cut(s, lim): + if len(s) > lim: + s = s[:lim - 4] + "..." + return s + + first = str(int(last) - args.nb_articles + 1) + resp, overviews = s.xover(first, last) + for artnum, over in overviews: + author = decode_header(over['from']).split('<', 1)[0] + subject = decode_header(over['subject']) + lines = int(over[':lines']) + print("{:7} {:20} {:42} ({})".format( + artnum, cut(author, 20), cut(subject, 42), lines) + ) + + s.quit() diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/numbers.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/numbers.py new file mode 100644 index 0000000000000000000000000000000000000000..ed815ef41ebe121da873fd9bedb90e6c298c1269 --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/numbers.py @@ -0,0 +1,389 @@ +# Copyright 2007 Google, Inc. All Rights Reserved. +# Licensed to PSF under a Contributor Agreement. + +"""Abstract Base Classes (ABCs) for numbers, according to PEP 3141. + +TODO: Fill out more detailed documentation on the operators.""" + +from abc import ABCMeta, abstractmethod + +__all__ = ["Number", "Complex", "Real", "Rational", "Integral"] + +class Number(metaclass=ABCMeta): + """All numbers inherit from this class. + + If you just want to check if an argument x is a number, without + caring what kind, use isinstance(x, Number). + """ + __slots__ = () + + # Concrete numeric types must provide their own hash implementation + __hash__ = None + + +## Notes on Decimal +## ---------------- +## Decimal has all of the methods specified by the Real abc, but it should +## not be registered as a Real because decimals do not interoperate with +## binary floats (i.e. Decimal('3.14') + 2.71828 is undefined). But, +## abstract reals are expected to interoperate (i.e. R1 + R2 should be +## expected to work if R1 and R2 are both Reals). + +class Complex(Number): + """Complex defines the operations that work on the builtin complex type. + + In short, those are: a conversion to complex, .real, .imag, +, -, + *, /, abs(), .conjugate, ==, and !=. + + If it is given heterogeneous arguments, and doesn't have special + knowledge about them, it should fall back to the builtin complex + type as described below. + """ + + __slots__ = () + + @abstractmethod + def __complex__(self): + """Return a builtin complex instance. Called for complex(self).""" + + def __bool__(self): + """True if self != 0. Called for bool(self).""" + return self != 0 + + @property + @abstractmethod + def real(self): + """Retrieve the real component of this number. + + This should subclass Real. + """ + raise NotImplementedError + + @property + @abstractmethod + def imag(self): + """Retrieve the imaginary component of this number. + + This should subclass Real. + """ + raise NotImplementedError + + @abstractmethod + def __add__(self, other): + """self + other""" + raise NotImplementedError + + @abstractmethod + def __radd__(self, other): + """other + self""" + raise NotImplementedError + + @abstractmethod + def __neg__(self): + """-self""" + raise NotImplementedError + + @abstractmethod + def __pos__(self): + """+self""" + raise NotImplementedError + + def __sub__(self, other): + """self - other""" + return self + -other + + def __rsub__(self, other): + """other - self""" + return -self + other + + @abstractmethod + def __mul__(self, other): + """self * other""" + raise NotImplementedError + + @abstractmethod + def __rmul__(self, other): + """other * self""" + raise NotImplementedError + + @abstractmethod + def __truediv__(self, other): + """self / other: Should promote to float when necessary.""" + raise NotImplementedError + + @abstractmethod + def __rtruediv__(self, other): + """other / self""" + raise NotImplementedError + + @abstractmethod + def __pow__(self, exponent): + """self**exponent; should promote to float or complex when necessary.""" + raise NotImplementedError + + @abstractmethod + def __rpow__(self, base): + """base ** self""" + raise NotImplementedError + + @abstractmethod + def __abs__(self): + """Returns the Real distance from 0. Called for abs(self).""" + raise NotImplementedError + + @abstractmethod + def conjugate(self): + """(x+y*i).conjugate() returns (x-y*i).""" + raise NotImplementedError + + @abstractmethod + def __eq__(self, other): + """self == other""" + raise NotImplementedError + +Complex.register(complex) + + +class Real(Complex): + """To Complex, Real adds the operations that work on real numbers. + + In short, those are: a conversion to float, trunc(), divmod, + %, <, <=, >, and >=. + + Real also provides defaults for the derived operations. + """ + + __slots__ = () + + @abstractmethod + def __float__(self): + """Any Real can be converted to a native float object. + + Called for float(self).""" + raise NotImplementedError + + @abstractmethod + def __trunc__(self): + """trunc(self): Truncates self to an Integral. + + Returns an Integral i such that: + * i>0 iff self>0; + * abs(i) <= abs(self); + * for any Integral j satisfying the first two conditions, + abs(i) >= abs(j) [i.e. i has "maximal" abs among those]. + i.e. "truncate towards 0". + """ + raise NotImplementedError + + @abstractmethod + def __floor__(self): + """Finds the greatest Integral <= self.""" + raise NotImplementedError + + @abstractmethod + def __ceil__(self): + """Finds the least Integral >= self.""" + raise NotImplementedError + + @abstractmethod + def __round__(self, ndigits=None): + """Rounds self to ndigits decimal places, defaulting to 0. + + If ndigits is omitted or None, returns an Integral, otherwise + returns a Real. Rounds half toward even. + """ + raise NotImplementedError + + def __divmod__(self, other): + """divmod(self, other): The pair (self // other, self % other). + + Sometimes this can be computed faster than the pair of + operations. + """ + return (self // other, self % other) + + def __rdivmod__(self, other): + """divmod(other, self): The pair (self // other, self % other). + + Sometimes this can be computed faster than the pair of + operations. + """ + return (other // self, other % self) + + @abstractmethod + def __floordiv__(self, other): + """self // other: The floor() of self/other.""" + raise NotImplementedError + + @abstractmethod + def __rfloordiv__(self, other): + """other // self: The floor() of other/self.""" + raise NotImplementedError + + @abstractmethod + def __mod__(self, other): + """self % other""" + raise NotImplementedError + + @abstractmethod + def __rmod__(self, other): + """other % self""" + raise NotImplementedError + + @abstractmethod + def __lt__(self, other): + """self < other + + < on Reals defines a total ordering, except perhaps for NaN.""" + raise NotImplementedError + + @abstractmethod + def __le__(self, other): + """self <= other""" + raise NotImplementedError + + # Concrete implementations of Complex abstract methods. + def __complex__(self): + """complex(self) == complex(float(self), 0)""" + return complex(float(self)) + + @property + def real(self): + """Real numbers are their real component.""" + return +self + + @property + def imag(self): + """Real numbers have no imaginary component.""" + return 0 + + def conjugate(self): + """Conjugate is a no-op for Reals.""" + return +self + +Real.register(float) + + +class Rational(Real): + """.numerator and .denominator should be in lowest terms.""" + + __slots__ = () + + @property + @abstractmethod + def numerator(self): + raise NotImplementedError + + @property + @abstractmethod + def denominator(self): + raise NotImplementedError + + # Concrete implementation of Real's conversion to float. + def __float__(self): + """float(self) = self.numerator / self.denominator + + It's important that this conversion use the integer's "true" + division rather than casting one side to float before dividing + so that ratios of huge integers convert without overflowing. + + """ + return self.numerator / self.denominator + + +class Integral(Rational): + """Integral adds a conversion to int and the bit-string operations.""" + + __slots__ = () + + @abstractmethod + def __int__(self): + """int(self)""" + raise NotImplementedError + + def __index__(self): + """Called whenever an index is needed, such as in slicing""" + return int(self) + + @abstractmethod + def __pow__(self, exponent, modulus=None): + """self ** exponent % modulus, but maybe faster. + + Accept the modulus argument if you want to support the + 3-argument version of pow(). Raise a TypeError if exponent < 0 + or any argument isn't Integral. Otherwise, just implement the + 2-argument version described in Complex. + """ + raise NotImplementedError + + @abstractmethod + def __lshift__(self, other): + """self << other""" + raise NotImplementedError + + @abstractmethod + def __rlshift__(self, other): + """other << self""" + raise NotImplementedError + + @abstractmethod + def __rshift__(self, other): + """self >> other""" + raise NotImplementedError + + @abstractmethod + def __rrshift__(self, other): + """other >> self""" + raise NotImplementedError + + @abstractmethod + def __and__(self, other): + """self & other""" + raise NotImplementedError + + @abstractmethod + def __rand__(self, other): + """other & self""" + raise NotImplementedError + + @abstractmethod + def __xor__(self, other): + """self ^ other""" + raise NotImplementedError + + @abstractmethod + def __rxor__(self, other): + """other ^ self""" + raise NotImplementedError + + @abstractmethod + def __or__(self, other): + """self | other""" + raise NotImplementedError + + @abstractmethod + def __ror__(self, other): + """other | self""" + raise NotImplementedError + + @abstractmethod + def __invert__(self): + """~self""" + raise NotImplementedError + + # Concrete implementations of Rational and Real abstract methods. + def __float__(self): + """float(self) == float(int(self))""" + return float(int(self)) + + @property + def numerator(self): + """Integers are their own numerators.""" + return +self + + @property + def denominator(self): + """Integers have a denominator of 1.""" + return 1 + +Integral.register(int) diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/opcode.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/opcode.py new file mode 100644 index 0000000000000000000000000000000000000000..3fb716b5d96a2909556eb25621ec0b354a5a3a03 --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/opcode.py @@ -0,0 +1,216 @@ + +""" +opcode module - potentially shared between dis and other modules which +operate on bytecodes (e.g. peephole optimizers). +""" + +__all__ = ["cmp_op", "hasconst", "hasname", "hasjrel", "hasjabs", + "haslocal", "hascompare", "hasfree", "opname", "opmap", + "HAVE_ARGUMENT", "EXTENDED_ARG", "hasnargs"] + +# It's a chicken-and-egg I'm afraid: +# We're imported before _opcode's made. +# With exception unheeded +# (stack_effect is not needed) +# Both our chickens and eggs are allayed. +# --Larry Hastings, 2013/11/23 + +try: + from _opcode import stack_effect + __all__.append('stack_effect') +except ImportError: + pass + +cmp_op = ('<', '<=', '==', '!=', '>', '>=', 'in', 'not in', 'is', + 'is not', 'exception match', 'BAD') + +hasconst = [] +hasname = [] +hasjrel = [] +hasjabs = [] +haslocal = [] +hascompare = [] +hasfree = [] +hasnargs = [] # unused + +opmap = {} +opname = ['<%r>' % (op,) for op in range(256)] + +def def_op(name, op): + opname[op] = name + opmap[name] = op + +def name_op(name, op): + def_op(name, op) + hasname.append(op) + +def jrel_op(name, op): + def_op(name, op) + hasjrel.append(op) + +def jabs_op(name, op): + def_op(name, op) + hasjabs.append(op) + +# Instruction opcodes for compiled code +# Blank lines correspond to available opcodes + +def_op('POP_TOP', 1) +def_op('ROT_TWO', 2) +def_op('ROT_THREE', 3) +def_op('DUP_TOP', 4) +def_op('DUP_TOP_TWO', 5) +def_op('ROT_FOUR', 6) + +def_op('NOP', 9) +def_op('UNARY_POSITIVE', 10) +def_op('UNARY_NEGATIVE', 11) +def_op('UNARY_NOT', 12) + +def_op('UNARY_INVERT', 15) + +def_op('BINARY_MATRIX_MULTIPLY', 16) +def_op('INPLACE_MATRIX_MULTIPLY', 17) + +def_op('BINARY_POWER', 19) +def_op('BINARY_MULTIPLY', 20) + +def_op('BINARY_MODULO', 22) +def_op('BINARY_ADD', 23) +def_op('BINARY_SUBTRACT', 24) +def_op('BINARY_SUBSCR', 25) +def_op('BINARY_FLOOR_DIVIDE', 26) +def_op('BINARY_TRUE_DIVIDE', 27) +def_op('INPLACE_FLOOR_DIVIDE', 28) +def_op('INPLACE_TRUE_DIVIDE', 29) + +def_op('GET_AITER', 50) +def_op('GET_ANEXT', 51) +def_op('BEFORE_ASYNC_WITH', 52) +def_op('BEGIN_FINALLY', 53) +def_op('END_ASYNC_FOR', 54) +def_op('INPLACE_ADD', 55) +def_op('INPLACE_SUBTRACT', 56) +def_op('INPLACE_MULTIPLY', 57) + +def_op('INPLACE_MODULO', 59) +def_op('STORE_SUBSCR', 60) +def_op('DELETE_SUBSCR', 61) +def_op('BINARY_LSHIFT', 62) +def_op('BINARY_RSHIFT', 63) +def_op('BINARY_AND', 64) +def_op('BINARY_XOR', 65) +def_op('BINARY_OR', 66) +def_op('INPLACE_POWER', 67) +def_op('GET_ITER', 68) +def_op('GET_YIELD_FROM_ITER', 69) + +def_op('PRINT_EXPR', 70) +def_op('LOAD_BUILD_CLASS', 71) +def_op('YIELD_FROM', 72) +def_op('GET_AWAITABLE', 73) + +def_op('INPLACE_LSHIFT', 75) +def_op('INPLACE_RSHIFT', 76) +def_op('INPLACE_AND', 77) +def_op('INPLACE_XOR', 78) +def_op('INPLACE_OR', 79) +def_op('WITH_CLEANUP_START', 81) +def_op('WITH_CLEANUP_FINISH', 82) +def_op('RETURN_VALUE', 83) +def_op('IMPORT_STAR', 84) +def_op('SETUP_ANNOTATIONS', 85) +def_op('YIELD_VALUE', 86) +def_op('POP_BLOCK', 87) +def_op('END_FINALLY', 88) +def_op('POP_EXCEPT', 89) + +HAVE_ARGUMENT = 90 # Opcodes from here have an argument: + +name_op('STORE_NAME', 90) # Index in name list +name_op('DELETE_NAME', 91) # "" +def_op('UNPACK_SEQUENCE', 92) # Number of tuple items +jrel_op('FOR_ITER', 93) +def_op('UNPACK_EX', 94) +name_op('STORE_ATTR', 95) # Index in name list +name_op('DELETE_ATTR', 96) # "" +name_op('STORE_GLOBAL', 97) # "" +name_op('DELETE_GLOBAL', 98) # "" +def_op('LOAD_CONST', 100) # Index in const list +hasconst.append(100) +name_op('LOAD_NAME', 101) # Index in name list +def_op('BUILD_TUPLE', 102) # Number of tuple items +def_op('BUILD_LIST', 103) # Number of list items +def_op('BUILD_SET', 104) # Number of set items +def_op('BUILD_MAP', 105) # Number of dict entries +name_op('LOAD_ATTR', 106) # Index in name list +def_op('COMPARE_OP', 107) # Comparison operator +hascompare.append(107) +name_op('IMPORT_NAME', 108) # Index in name list +name_op('IMPORT_FROM', 109) # Index in name list + +jrel_op('JUMP_FORWARD', 110) # Number of bytes to skip +jabs_op('JUMP_IF_FALSE_OR_POP', 111) # Target byte offset from beginning of code +jabs_op('JUMP_IF_TRUE_OR_POP', 112) # "" +jabs_op('JUMP_ABSOLUTE', 113) # "" +jabs_op('POP_JUMP_IF_FALSE', 114) # "" +jabs_op('POP_JUMP_IF_TRUE', 115) # "" + +name_op('LOAD_GLOBAL', 116) # Index in name list + +jrel_op('SETUP_FINALLY', 122) # Distance to target address + +def_op('LOAD_FAST', 124) # Local variable number +haslocal.append(124) +def_op('STORE_FAST', 125) # Local variable number +haslocal.append(125) +def_op('DELETE_FAST', 126) # Local variable number +haslocal.append(126) + +def_op('RAISE_VARARGS', 130) # Number of raise arguments (1, 2, or 3) +def_op('CALL_FUNCTION', 131) # #args +def_op('MAKE_FUNCTION', 132) # Flags +def_op('BUILD_SLICE', 133) # Number of items +def_op('LOAD_CLOSURE', 135) +hasfree.append(135) +def_op('LOAD_DEREF', 136) +hasfree.append(136) +def_op('STORE_DEREF', 137) +hasfree.append(137) +def_op('DELETE_DEREF', 138) +hasfree.append(138) + +def_op('CALL_FUNCTION_KW', 141) # #args + #kwargs +def_op('CALL_FUNCTION_EX', 142) # Flags + +jrel_op('SETUP_WITH', 143) + +def_op('LIST_APPEND', 145) +def_op('SET_ADD', 146) +def_op('MAP_ADD', 147) + +def_op('LOAD_CLASSDEREF', 148) +hasfree.append(148) + +def_op('EXTENDED_ARG', 144) +EXTENDED_ARG = 144 + +def_op('BUILD_LIST_UNPACK', 149) +def_op('BUILD_MAP_UNPACK', 150) +def_op('BUILD_MAP_UNPACK_WITH_CALL', 151) +def_op('BUILD_TUPLE_UNPACK', 152) +def_op('BUILD_SET_UNPACK', 153) + +jrel_op('SETUP_ASYNC_WITH', 154) + +def_op('FORMAT_VALUE', 155) +def_op('BUILD_CONST_KEY_MAP', 156) +def_op('BUILD_STRING', 157) +def_op('BUILD_TUPLE_UNPACK_WITH_CALL', 158) + +name_op('LOAD_METHOD', 160) +def_op('CALL_METHOD', 161) +jrel_op('CALL_FINALLY', 162) +def_op('POP_FINALLY', 163) + +del def_op, name_op, jrel_op, jabs_op diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/pathlib.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/pathlib.py new file mode 100644 index 0000000000000000000000000000000000000000..350f27e4a06de1a7b4ab75e1c13632ea723f7ea1 --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/pathlib.py @@ -0,0 +1,1583 @@ +import fnmatch +import functools +import io +import ntpath +import os +import posixpath +import re +import sys +from _collections_abc import Sequence +from errno import EINVAL, ENOENT, ENOTDIR, EBADF, ELOOP +from operator import attrgetter +from stat import S_ISDIR, S_ISLNK, S_ISREG, S_ISSOCK, S_ISBLK, S_ISCHR, S_ISFIFO +from urllib.parse import quote_from_bytes as urlquote_from_bytes + + +supports_symlinks = True +if os.name == 'nt': + import nt + if sys.getwindowsversion()[:2] >= (6, 0): + from nt import _getfinalpathname + else: + supports_symlinks = False + _getfinalpathname = None +else: + nt = None + + +__all__ = [ + "PurePath", "PurePosixPath", "PureWindowsPath", + "Path", "PosixPath", "WindowsPath", + ] + +# +# Internals +# + +# EBADF - guard against macOS `stat` throwing EBADF +_IGNORED_ERROS = (ENOENT, ENOTDIR, EBADF, ELOOP) + +_IGNORED_WINERRORS = ( + 21, # ERROR_NOT_READY - drive exists but is not accessible + 123, # ERROR_INVALID_NAME - fix for bpo-35306 + 1921, # ERROR_CANT_RESOLVE_FILENAME - fix for broken symlink pointing to itself +) + +def _ignore_error(exception): + return (getattr(exception, 'errno', None) in _IGNORED_ERROS or + getattr(exception, 'winerror', None) in _IGNORED_WINERRORS) + + +def _is_wildcard_pattern(pat): + # Whether this pattern needs actual matching using fnmatch, or can + # be looked up directly as a file. + return "*" in pat or "?" in pat or "[" in pat + + +class _Flavour(object): + """A flavour implements a particular (platform-specific) set of path + semantics.""" + + def __init__(self): + self.join = self.sep.join + + def parse_parts(self, parts): + parsed = [] + sep = self.sep + altsep = self.altsep + drv = root = '' + it = reversed(parts) + for part in it: + if not part: + continue + if altsep: + part = part.replace(altsep, sep) + drv, root, rel = self.splitroot(part) + if sep in rel: + for x in reversed(rel.split(sep)): + if x and x != '.': + parsed.append(sys.intern(x)) + else: + if rel and rel != '.': + parsed.append(sys.intern(rel)) + if drv or root: + if not drv: + # If no drive is present, try to find one in the previous + # parts. This makes the result of parsing e.g. + # ("C:", "/", "a") reasonably intuitive. + for part in it: + if not part: + continue + if altsep: + part = part.replace(altsep, sep) + drv = self.splitroot(part)[0] + if drv: + break + break + if drv or root: + parsed.append(drv + root) + parsed.reverse() + return drv, root, parsed + + def join_parsed_parts(self, drv, root, parts, drv2, root2, parts2): + """ + Join the two paths represented by the respective + (drive, root, parts) tuples. Return a new (drive, root, parts) tuple. + """ + if root2: + if not drv2 and drv: + return drv, root2, [drv + root2] + parts2[1:] + elif drv2: + if drv2 == drv or self.casefold(drv2) == self.casefold(drv): + # Same drive => second path is relative to the first + return drv, root, parts + parts2[1:] + else: + # Second path is non-anchored (common case) + return drv, root, parts + parts2 + return drv2, root2, parts2 + + +class _WindowsFlavour(_Flavour): + # Reference for Windows paths can be found at + # http://msdn.microsoft.com/en-us/library/aa365247%28v=vs.85%29.aspx + + sep = '\\' + altsep = '/' + has_drv = True + pathmod = ntpath + + is_supported = (os.name == 'nt') + + drive_letters = set('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') + ext_namespace_prefix = '\\\\?\\' + + reserved_names = ( + {'CON', 'PRN', 'AUX', 'NUL'} | + {'COM%d' % i for i in range(1, 10)} | + {'LPT%d' % i for i in range(1, 10)} + ) + + # Interesting findings about extended paths: + # - '\\?\c:\a', '//?/c:\a' and '//?/c:/a' are all supported + # but '\\?\c:/a' is not + # - extended paths are always absolute; "relative" extended paths will + # fail. + + def splitroot(self, part, sep=sep): + first = part[0:1] + second = part[1:2] + if (second == sep and first == sep): + # XXX extended paths should also disable the collapsing of "." + # components (according to MSDN docs). + prefix, part = self._split_extended_path(part) + first = part[0:1] + second = part[1:2] + else: + prefix = '' + third = part[2:3] + if (second == sep and first == sep and third != sep): + # is a UNC path: + # vvvvvvvvvvvvvvvvvvvvv root + # \\machine\mountpoint\directory\etc\... + # directory ^^^^^^^^^^^^^^ + index = part.find(sep, 2) + if index != -1: + index2 = part.find(sep, index + 1) + # a UNC path can't have two slashes in a row + # (after the initial two) + if index2 != index + 1: + if index2 == -1: + index2 = len(part) + if prefix: + return prefix + part[1:index2], sep, part[index2+1:] + else: + return part[:index2], sep, part[index2+1:] + drv = root = '' + if second == ':' and first in self.drive_letters: + drv = part[:2] + part = part[2:] + first = third + if first == sep: + root = first + part = part.lstrip(sep) + return prefix + drv, root, part + + def casefold(self, s): + return s.lower() + + def casefold_parts(self, parts): + return [p.lower() for p in parts] + + def compile_pattern(self, pattern): + return re.compile(fnmatch.translate(pattern), re.IGNORECASE).fullmatch + + def resolve(self, path, strict=False): + s = str(path) + if not s: + return os.getcwd() + previous_s = None + if _getfinalpathname is not None: + if strict: + return self._ext_to_normal(_getfinalpathname(s)) + else: + tail_parts = [] # End of the path after the first one not found + while True: + try: + s = self._ext_to_normal(_getfinalpathname(s)) + except FileNotFoundError: + previous_s = s + s, tail = os.path.split(s) + tail_parts.append(tail) + if previous_s == s: + return path + else: + return os.path.join(s, *reversed(tail_parts)) + # Means fallback on absolute + return None + + def _split_extended_path(self, s, ext_prefix=ext_namespace_prefix): + prefix = '' + if s.startswith(ext_prefix): + prefix = s[:4] + s = s[4:] + if s.startswith('UNC\\'): + prefix += s[:3] + s = '\\' + s[3:] + return prefix, s + + def _ext_to_normal(self, s): + # Turn back an extended path into a normal DOS-like path + return self._split_extended_path(s)[1] + + def is_reserved(self, parts): + # NOTE: the rules for reserved names seem somewhat complicated + # (e.g. r"..\NUL" is reserved but not r"foo\NUL"). + # We err on the side of caution and return True for paths which are + # not considered reserved by Windows. + if not parts: + return False + if parts[0].startswith('\\\\'): + # UNC paths are never reserved + return False + return parts[-1].partition('.')[0].upper() in self.reserved_names + + def make_uri(self, path): + # Under Windows, file URIs use the UTF-8 encoding. + drive = path.drive + if len(drive) == 2 and drive[1] == ':': + # It's a path on a local drive => 'file:///c:/a/b' + rest = path.as_posix()[2:].lstrip('/') + return 'file:///%s/%s' % ( + drive, urlquote_from_bytes(rest.encode('utf-8'))) + else: + # It's a path on a network drive => 'file://host/share/a/b' + return 'file:' + urlquote_from_bytes(path.as_posix().encode('utf-8')) + + def gethomedir(self, username): + if 'USERPROFILE' in os.environ: + userhome = os.environ['USERPROFILE'] + elif 'HOMEPATH' in os.environ: + try: + drv = os.environ['HOMEDRIVE'] + except KeyError: + drv = '' + userhome = drv + os.environ['HOMEPATH'] + else: + raise RuntimeError("Can't determine home directory") + + if username: + # Try to guess user home directory. By default all users + # directories are located in the same place and are named by + # corresponding usernames. If current user home directory points + # to nonstandard place, this guess is likely wrong. + if os.environ['USERNAME'] != username: + drv, root, parts = self.parse_parts((userhome,)) + if parts[-1] != os.environ['USERNAME']: + raise RuntimeError("Can't determine home directory " + "for %r" % username) + parts[-1] = username + if drv or root: + userhome = drv + root + self.join(parts[1:]) + else: + userhome = self.join(parts) + return userhome + +class _PosixFlavour(_Flavour): + sep = '/' + altsep = '' + has_drv = False + pathmod = posixpath + + is_supported = (os.name != 'nt') + + def splitroot(self, part, sep=sep): + if part and part[0] == sep: + stripped_part = part.lstrip(sep) + # According to POSIX path resolution: + # http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap04.html#tag_04_11 + # "A pathname that begins with two successive slashes may be + # interpreted in an implementation-defined manner, although more + # than two leading slashes shall be treated as a single slash". + if len(part) - len(stripped_part) == 2: + return '', sep * 2, stripped_part + else: + return '', sep, stripped_part + else: + return '', '', part + + def casefold(self, s): + return s + + def casefold_parts(self, parts): + return parts + + def compile_pattern(self, pattern): + return re.compile(fnmatch.translate(pattern)).fullmatch + + def resolve(self, path, strict=False): + sep = self.sep + accessor = path._accessor + seen = {} + def _resolve(path, rest): + if rest.startswith(sep): + path = '' + + for name in rest.split(sep): + if not name or name == '.': + # current dir + continue + if name == '..': + # parent dir + path, _, _ = path.rpartition(sep) + continue + if path.endswith(sep): + newpath = path + name + else: + newpath = path + sep + name + if newpath in seen: + # Already seen this path + path = seen[newpath] + if path is not None: + # use cached value + continue + # The symlink is not resolved, so we must have a symlink loop. + raise RuntimeError("Symlink loop from %r" % newpath) + # Resolve the symbolic link + try: + target = accessor.readlink(newpath) + except OSError as e: + if e.errno != EINVAL and strict: + raise + # Not a symlink, or non-strict mode. We just leave the path + # untouched. + path = newpath + else: + seen[newpath] = None # not resolved symlink + path = _resolve(path, target) + seen[newpath] = path # resolved symlink + + return path + # NOTE: according to POSIX, getcwd() cannot contain path components + # which are symlinks. + base = '' if path.is_absolute() else os.getcwd() + return _resolve(base, str(path)) or sep + + def is_reserved(self, parts): + return False + + def make_uri(self, path): + # We represent the path using the local filesystem encoding, + # for portability to other applications. + bpath = bytes(path) + return 'file://' + urlquote_from_bytes(bpath) + + def gethomedir(self, username): + if not username: + try: + return os.environ['HOME'] + except KeyError: + import pwd + return pwd.getpwuid(os.getuid()).pw_dir + else: + import pwd + try: + return pwd.getpwnam(username).pw_dir + except KeyError: + raise RuntimeError("Can't determine home directory " + "for %r" % username) + + +_windows_flavour = _WindowsFlavour() +_posix_flavour = _PosixFlavour() + + +class _Accessor: + """An accessor implements a particular (system-specific or not) way of + accessing paths on the filesystem.""" + + +class _NormalAccessor(_Accessor): + + stat = os.stat + + lstat = os.lstat + + open = os.open + + listdir = os.listdir + + scandir = os.scandir + + chmod = os.chmod + + if hasattr(os, "lchmod"): + lchmod = os.lchmod + else: + def lchmod(self, pathobj, mode): + raise NotImplementedError("lchmod() not available on this system") + + mkdir = os.mkdir + + unlink = os.unlink + + if hasattr(os, "link"): + link_to = os.link + else: + @staticmethod + def link_to(self, target): + raise NotImplementedError("os.link() not available on this system") + + rmdir = os.rmdir + + rename = os.rename + + replace = os.replace + + if nt: + if supports_symlinks: + symlink = os.symlink + else: + def symlink(a, b, target_is_directory): + raise NotImplementedError("symlink() not available on this system") + else: + # Under POSIX, os.symlink() takes two args + @staticmethod + def symlink(a, b, target_is_directory): + return os.symlink(a, b) + + utime = os.utime + + # Helper for resolve() + def readlink(self, path): + return os.readlink(path) + + +_normal_accessor = _NormalAccessor() + + +# +# Globbing helpers +# + +def _make_selector(pattern_parts, flavour): + pat = pattern_parts[0] + child_parts = pattern_parts[1:] + if pat == '**': + cls = _RecursiveWildcardSelector + elif '**' in pat: + raise ValueError("Invalid pattern: '**' can only be an entire path component") + elif _is_wildcard_pattern(pat): + cls = _WildcardSelector + else: + cls = _PreciseSelector + return cls(pat, child_parts, flavour) + +if hasattr(functools, "lru_cache"): + _make_selector = functools.lru_cache()(_make_selector) + + +class _Selector: + """A selector matches a specific glob pattern part against the children + of a given path.""" + + def __init__(self, child_parts, flavour): + self.child_parts = child_parts + if child_parts: + self.successor = _make_selector(child_parts, flavour) + self.dironly = True + else: + self.successor = _TerminatingSelector() + self.dironly = False + + def select_from(self, parent_path): + """Iterate over all child paths of `parent_path` matched by this + selector. This can contain parent_path itself.""" + path_cls = type(parent_path) + is_dir = path_cls.is_dir + exists = path_cls.exists + scandir = parent_path._accessor.scandir + if not is_dir(parent_path): + return iter([]) + return self._select_from(parent_path, is_dir, exists, scandir) + + +class _TerminatingSelector: + + def _select_from(self, parent_path, is_dir, exists, scandir): + yield parent_path + + +class _PreciseSelector(_Selector): + + def __init__(self, name, child_parts, flavour): + self.name = name + _Selector.__init__(self, child_parts, flavour) + + def _select_from(self, parent_path, is_dir, exists, scandir): + try: + path = parent_path._make_child_relpath(self.name) + if (is_dir if self.dironly else exists)(path): + for p in self.successor._select_from(path, is_dir, exists, scandir): + yield p + except PermissionError: + return + + +class _WildcardSelector(_Selector): + + def __init__(self, pat, child_parts, flavour): + self.match = flavour.compile_pattern(pat) + _Selector.__init__(self, child_parts, flavour) + + def _select_from(self, parent_path, is_dir, exists, scandir): + try: + with scandir(parent_path) as scandir_it: + entries = list(scandir_it) + for entry in entries: + if self.dironly: + try: + # "entry.is_dir()" can raise PermissionError + # in some cases (see bpo-38894), which is not + # among the errors ignored by _ignore_error() + if not entry.is_dir(): + continue + except OSError as e: + if not _ignore_error(e): + raise + continue + name = entry.name + if self.match(name): + path = parent_path._make_child_relpath(name) + for p in self.successor._select_from(path, is_dir, exists, scandir): + yield p + except PermissionError: + return + + +class _RecursiveWildcardSelector(_Selector): + + def __init__(self, pat, child_parts, flavour): + _Selector.__init__(self, child_parts, flavour) + + def _iterate_directories(self, parent_path, is_dir, scandir): + yield parent_path + try: + with scandir(parent_path) as scandir_it: + entries = list(scandir_it) + for entry in entries: + entry_is_dir = False + try: + entry_is_dir = entry.is_dir() + except OSError as e: + if not _ignore_error(e): + raise + if entry_is_dir and not entry.is_symlink(): + path = parent_path._make_child_relpath(entry.name) + for p in self._iterate_directories(path, is_dir, scandir): + yield p + except PermissionError: + return + + def _select_from(self, parent_path, is_dir, exists, scandir): + try: + yielded = set() + try: + successor_select = self.successor._select_from + for starting_point in self._iterate_directories(parent_path, is_dir, scandir): + for p in successor_select(starting_point, is_dir, exists, scandir): + if p not in yielded: + yield p + yielded.add(p) + finally: + yielded.clear() + except PermissionError: + return + + +# +# Public API +# + +class _PathParents(Sequence): + """This object provides sequence-like access to the logical ancestors + of a path. Don't try to construct it yourself.""" + __slots__ = ('_pathcls', '_drv', '_root', '_parts') + + def __init__(self, path): + # We don't store the instance to avoid reference cycles + self._pathcls = type(path) + self._drv = path._drv + self._root = path._root + self._parts = path._parts + + def __len__(self): + if self._drv or self._root: + return len(self._parts) - 1 + else: + return len(self._parts) + + def __getitem__(self, idx): + if idx < 0 or idx >= len(self): + raise IndexError(idx) + return self._pathcls._from_parsed_parts(self._drv, self._root, + self._parts[:-idx - 1]) + + def __repr__(self): + return "<{}.parents>".format(self._pathcls.__name__) + + +class PurePath(object): + """Base class for manipulating paths without I/O. + + PurePath represents a filesystem path and offers operations which + don't imply any actual filesystem I/O. Depending on your system, + instantiating a PurePath will return either a PurePosixPath or a + PureWindowsPath object. You can also instantiate either of these classes + directly, regardless of your system. + """ + __slots__ = ( + '_drv', '_root', '_parts', + '_str', '_hash', '_pparts', '_cached_cparts', + ) + + def __new__(cls, *args): + """Construct a PurePath from one or several strings and or existing + PurePath objects. The strings and path objects are combined so as + to yield a canonicalized path, which is incorporated into the + new PurePath object. + """ + if cls is PurePath: + cls = PureWindowsPath if os.name == 'nt' else PurePosixPath + return cls._from_parts(args) + + def __reduce__(self): + # Using the parts tuple helps share interned path parts + # when pickling related paths. + return (self.__class__, tuple(self._parts)) + + @classmethod + def _parse_args(cls, args): + # This is useful when you don't want to create an instance, just + # canonicalize some constructor arguments. + parts = [] + for a in args: + if isinstance(a, PurePath): + parts += a._parts + else: + a = os.fspath(a) + if isinstance(a, str): + # Force-cast str subclasses to str (issue #21127) + parts.append(str(a)) + else: + raise TypeError( + "argument should be a str object or an os.PathLike " + "object returning str, not %r" + % type(a)) + return cls._flavour.parse_parts(parts) + + @classmethod + def _from_parts(cls, args, init=True): + # We need to call _parse_args on the instance, so as to get the + # right flavour. + self = object.__new__(cls) + drv, root, parts = self._parse_args(args) + self._drv = drv + self._root = root + self._parts = parts + if init: + self._init() + return self + + @classmethod + def _from_parsed_parts(cls, drv, root, parts, init=True): + self = object.__new__(cls) + self._drv = drv + self._root = root + self._parts = parts + if init: + self._init() + return self + + @classmethod + def _format_parsed_parts(cls, drv, root, parts): + if drv or root: + return drv + root + cls._flavour.join(parts[1:]) + else: + return cls._flavour.join(parts) + + def _init(self): + # Overridden in concrete Path + pass + + def _make_child(self, args): + drv, root, parts = self._parse_args(args) + drv, root, parts = self._flavour.join_parsed_parts( + self._drv, self._root, self._parts, drv, root, parts) + return self._from_parsed_parts(drv, root, parts) + + def __str__(self): + """Return the string representation of the path, suitable for + passing to system calls.""" + try: + return self._str + except AttributeError: + self._str = self._format_parsed_parts(self._drv, self._root, + self._parts) or '.' + return self._str + + def __fspath__(self): + return str(self) + + def as_posix(self): + """Return the string representation of the path with forward (/) + slashes.""" + f = self._flavour + return str(self).replace(f.sep, '/') + + def __bytes__(self): + """Return the bytes representation of the path. This is only + recommended to use under Unix.""" + return os.fsencode(self) + + def __repr__(self): + return "{}({!r})".format(self.__class__.__name__, self.as_posix()) + + def as_uri(self): + """Return the path as a 'file' URI.""" + if not self.is_absolute(): + raise ValueError("relative path can't be expressed as a file URI") + return self._flavour.make_uri(self) + + @property + def _cparts(self): + # Cached casefolded parts, for hashing and comparison + try: + return self._cached_cparts + except AttributeError: + self._cached_cparts = self._flavour.casefold_parts(self._parts) + return self._cached_cparts + + def __eq__(self, other): + if not isinstance(other, PurePath): + return NotImplemented + return self._cparts == other._cparts and self._flavour is other._flavour + + def __hash__(self): + try: + return self._hash + except AttributeError: + self._hash = hash(tuple(self._cparts)) + return self._hash + + def __lt__(self, other): + if not isinstance(other, PurePath) or self._flavour is not other._flavour: + return NotImplemented + return self._cparts < other._cparts + + def __le__(self, other): + if not isinstance(other, PurePath) or self._flavour is not other._flavour: + return NotImplemented + return self._cparts <= other._cparts + + def __gt__(self, other): + if not isinstance(other, PurePath) or self._flavour is not other._flavour: + return NotImplemented + return self._cparts > other._cparts + + def __ge__(self, other): + if not isinstance(other, PurePath) or self._flavour is not other._flavour: + return NotImplemented + return self._cparts >= other._cparts + + drive = property(attrgetter('_drv'), + doc="""The drive prefix (letter or UNC path), if any.""") + + root = property(attrgetter('_root'), + doc="""The root of the path, if any.""") + + @property + def anchor(self): + """The concatenation of the drive and root, or ''.""" + anchor = self._drv + self._root + return anchor + + @property + def name(self): + """The final path component, if any.""" + parts = self._parts + if len(parts) == (1 if (self._drv or self._root) else 0): + return '' + return parts[-1] + + @property + def suffix(self): + """ + The final component's last suffix, if any. + + This includes the leading period. For example: '.txt' + """ + name = self.name + i = name.rfind('.') + if 0 < i < len(name) - 1: + return name[i:] + else: + return '' + + @property + def suffixes(self): + """ + A list of the final component's suffixes, if any. + + These include the leading periods. For example: ['.tar', '.gz'] + """ + name = self.name + if name.endswith('.'): + return [] + name = name.lstrip('.') + return ['.' + suffix for suffix in name.split('.')[1:]] + + @property + def stem(self): + """The final path component, minus its last suffix.""" + name = self.name + i = name.rfind('.') + if 0 < i < len(name) - 1: + return name[:i] + else: + return name + + def with_name(self, name): + """Return a new path with the file name changed.""" + if not self.name: + raise ValueError("%r has an empty name" % (self,)) + drv, root, parts = self._flavour.parse_parts((name,)) + if (not name or name[-1] in [self._flavour.sep, self._flavour.altsep] + or drv or root or len(parts) != 1): + raise ValueError("Invalid name %r" % (name)) + return self._from_parsed_parts(self._drv, self._root, + self._parts[:-1] + [name]) + + def with_suffix(self, suffix): + """Return a new path with the file suffix changed. If the path + has no suffix, add given suffix. If the given suffix is an empty + string, remove the suffix from the path. + """ + f = self._flavour + if f.sep in suffix or f.altsep and f.altsep in suffix: + raise ValueError("Invalid suffix %r" % (suffix,)) + if suffix and not suffix.startswith('.') or suffix == '.': + raise ValueError("Invalid suffix %r" % (suffix)) + name = self.name + if not name: + raise ValueError("%r has an empty name" % (self,)) + old_suffix = self.suffix + if not old_suffix: + name = name + suffix + else: + name = name[:-len(old_suffix)] + suffix + return self._from_parsed_parts(self._drv, self._root, + self._parts[:-1] + [name]) + + def relative_to(self, *other): + """Return the relative path to another path identified by the passed + arguments. If the operation is not possible (because this is not + a subpath of the other path), raise ValueError. + """ + # For the purpose of this method, drive and root are considered + # separate parts, i.e.: + # Path('c:/').relative_to('c:') gives Path('/') + # Path('c:/').relative_to('/') raise ValueError + if not other: + raise TypeError("need at least one argument") + parts = self._parts + drv = self._drv + root = self._root + if root: + abs_parts = [drv, root] + parts[1:] + else: + abs_parts = parts + to_drv, to_root, to_parts = self._parse_args(other) + if to_root: + to_abs_parts = [to_drv, to_root] + to_parts[1:] + else: + to_abs_parts = to_parts + n = len(to_abs_parts) + cf = self._flavour.casefold_parts + if (root or drv) if n == 0 else cf(abs_parts[:n]) != cf(to_abs_parts): + formatted = self._format_parsed_parts(to_drv, to_root, to_parts) + raise ValueError("{!r} does not start with {!r}" + .format(str(self), str(formatted))) + return self._from_parsed_parts('', root if n == 1 else '', + abs_parts[n:]) + + @property + def parts(self): + """An object providing sequence-like access to the + components in the filesystem path.""" + # We cache the tuple to avoid building a new one each time .parts + # is accessed. XXX is this necessary? + try: + return self._pparts + except AttributeError: + self._pparts = tuple(self._parts) + return self._pparts + + def joinpath(self, *args): + """Combine this path with one or several arguments, and return a + new path representing either a subpath (if all arguments are relative + paths) or a totally different path (if one of the arguments is + anchored). + """ + return self._make_child(args) + + def __truediv__(self, key): + try: + return self._make_child((key,)) + except TypeError: + return NotImplemented + + def __rtruediv__(self, key): + try: + return self._from_parts([key] + self._parts) + except TypeError: + return NotImplemented + + @property + def parent(self): + """The logical parent of the path.""" + drv = self._drv + root = self._root + parts = self._parts + if len(parts) == 1 and (drv or root): + return self + return self._from_parsed_parts(drv, root, parts[:-1]) + + @property + def parents(self): + """A sequence of this path's logical parents.""" + return _PathParents(self) + + def is_absolute(self): + """True if the path is absolute (has both a root and, if applicable, + a drive).""" + if not self._root: + return False + return not self._flavour.has_drv or bool(self._drv) + + def is_reserved(self): + """Return True if the path contains one of the special names reserved + by the system, if any.""" + return self._flavour.is_reserved(self._parts) + + def match(self, path_pattern): + """ + Return True if this path matches the given pattern. + """ + cf = self._flavour.casefold + path_pattern = cf(path_pattern) + drv, root, pat_parts = self._flavour.parse_parts((path_pattern,)) + if not pat_parts: + raise ValueError("empty pattern") + if drv and drv != cf(self._drv): + return False + if root and root != cf(self._root): + return False + parts = self._cparts + if drv or root: + if len(pat_parts) != len(parts): + return False + pat_parts = pat_parts[1:] + elif len(pat_parts) > len(parts): + return False + for part, pat in zip(reversed(parts), reversed(pat_parts)): + if not fnmatch.fnmatchcase(part, pat): + return False + return True + +# Can't subclass os.PathLike from PurePath and keep the constructor +# optimizations in PurePath._parse_args(). +os.PathLike.register(PurePath) + + +class PurePosixPath(PurePath): + """PurePath subclass for non-Windows systems. + + On a POSIX system, instantiating a PurePath should return this object. + However, you can also instantiate it directly on any system. + """ + _flavour = _posix_flavour + __slots__ = () + + +class PureWindowsPath(PurePath): + """PurePath subclass for Windows systems. + + On a Windows system, instantiating a PurePath should return this object. + However, you can also instantiate it directly on any system. + """ + _flavour = _windows_flavour + __slots__ = () + + +# Filesystem-accessing classes + + +class Path(PurePath): + """PurePath subclass that can make system calls. + + Path represents a filesystem path but unlike PurePath, also offers + methods to do system calls on path objects. Depending on your system, + instantiating a Path will return either a PosixPath or a WindowsPath + object. You can also instantiate a PosixPath or WindowsPath directly, + but cannot instantiate a WindowsPath on a POSIX system or vice versa. + """ + __slots__ = ( + '_accessor', + '_closed', + ) + + def __new__(cls, *args, **kwargs): + if cls is Path: + cls = WindowsPath if os.name == 'nt' else PosixPath + self = cls._from_parts(args, init=False) + if not self._flavour.is_supported: + raise NotImplementedError("cannot instantiate %r on your system" + % (cls.__name__,)) + self._init() + return self + + def _init(self, + # Private non-constructor arguments + template=None, + ): + self._closed = False + if template is not None: + self._accessor = template._accessor + else: + self._accessor = _normal_accessor + + def _make_child_relpath(self, part): + # This is an optimization used for dir walking. `part` must be + # a single part relative to this path. + parts = self._parts + [part] + return self._from_parsed_parts(self._drv, self._root, parts) + + def __enter__(self): + if self._closed: + self._raise_closed() + return self + + def __exit__(self, t, v, tb): + self._closed = True + + def _raise_closed(self): + raise ValueError("I/O operation on closed path") + + def _opener(self, name, flags, mode=0o666): + # A stub for the opener argument to built-in open() + return self._accessor.open(self, flags, mode) + + def _raw_open(self, flags, mode=0o777): + """ + Open the file pointed by this path and return a file descriptor, + as os.open() does. + """ + if self._closed: + self._raise_closed() + return self._accessor.open(self, flags, mode) + + # Public API + + @classmethod + def cwd(cls): + """Return a new path pointing to the current working directory + (as returned by os.getcwd()). + """ + return cls(os.getcwd()) + + @classmethod + def home(cls): + """Return a new path pointing to the user's home directory (as + returned by os.path.expanduser('~')). + """ + return cls(cls()._flavour.gethomedir(None)) + + def samefile(self, other_path): + """Return whether other_path is the same or not as this file + (as returned by os.path.samefile()). + """ + st = self.stat() + try: + other_st = other_path.stat() + except AttributeError: + other_st = os.stat(other_path) + return os.path.samestat(st, other_st) + + def iterdir(self): + """Iterate over the files in this directory. Does not yield any + result for the special paths '.' and '..'. + """ + if self._closed: + self._raise_closed() + for name in self._accessor.listdir(self): + if name in {'.', '..'}: + # Yielding a path object for these makes little sense + continue + yield self._make_child_relpath(name) + if self._closed: + self._raise_closed() + + def glob(self, pattern): + """Iterate over this subtree and yield all existing files (of any + kind, including directories) matching the given relative pattern. + """ + if not pattern: + raise ValueError("Unacceptable pattern: {!r}".format(pattern)) + drv, root, pattern_parts = self._flavour.parse_parts((pattern,)) + if drv or root: + raise NotImplementedError("Non-relative patterns are unsupported") + selector = _make_selector(tuple(pattern_parts), self._flavour) + for p in selector.select_from(self): + yield p + + def rglob(self, pattern): + """Recursively yield all existing files (of any kind, including + directories) matching the given relative pattern, anywhere in + this subtree. + """ + drv, root, pattern_parts = self._flavour.parse_parts((pattern,)) + if drv or root: + raise NotImplementedError("Non-relative patterns are unsupported") + selector = _make_selector(("**",) + tuple(pattern_parts), self._flavour) + for p in selector.select_from(self): + yield p + + def absolute(self): + """Return an absolute version of this path. This function works + even if the path doesn't point to anything. + + No normalization is done, i.e. all '.' and '..' will be kept along. + Use resolve() to get the canonical path to a file. + """ + # XXX untested yet! + if self._closed: + self._raise_closed() + if self.is_absolute(): + return self + # FIXME this must defer to the specific flavour (and, under Windows, + # use nt._getfullpathname()) + obj = self._from_parts([os.getcwd()] + self._parts, init=False) + obj._init(template=self) + return obj + + def resolve(self, strict=False): + """ + Make the path absolute, resolving all symlinks on the way and also + normalizing it (for example turning slashes into backslashes under + Windows). + """ + if self._closed: + self._raise_closed() + s = self._flavour.resolve(self, strict=strict) + if s is None: + # No symlink resolution => for consistency, raise an error if + # the path doesn't exist or is forbidden + self.stat() + s = str(self.absolute()) + # Now we have no symlinks in the path, it's safe to normalize it. + normed = self._flavour.pathmod.normpath(s) + obj = self._from_parts((normed,), init=False) + obj._init(template=self) + return obj + + def stat(self): + """ + Return the result of the stat() system call on this path, like + os.stat() does. + """ + return self._accessor.stat(self) + + def owner(self): + """ + Return the login name of the file owner. + """ + import pwd + return pwd.getpwuid(self.stat().st_uid).pw_name + + def group(self): + """ + Return the group name of the file gid. + """ + import grp + return grp.getgrgid(self.stat().st_gid).gr_name + + def open(self, mode='r', buffering=-1, encoding=None, + errors=None, newline=None): + """ + Open the file pointed by this path and return a file object, as + the built-in open() function does. + """ + if self._closed: + self._raise_closed() + return io.open(self, mode, buffering, encoding, errors, newline, + opener=self._opener) + + def read_bytes(self): + """ + Open the file in bytes mode, read it, and close the file. + """ + with self.open(mode='rb') as f: + return f.read() + + def read_text(self, encoding=None, errors=None): + """ + Open the file in text mode, read it, and close the file. + """ + with self.open(mode='r', encoding=encoding, errors=errors) as f: + return f.read() + + def write_bytes(self, data): + """ + Open the file in bytes mode, write to it, and close the file. + """ + # type-check for the buffer interface before truncating the file + view = memoryview(data) + with self.open(mode='wb') as f: + return f.write(view) + + def write_text(self, data, encoding=None, errors=None): + """ + Open the file in text mode, write to it, and close the file. + """ + if not isinstance(data, str): + raise TypeError('data must be str, not %s' % + data.__class__.__name__) + with self.open(mode='w', encoding=encoding, errors=errors) as f: + return f.write(data) + + def touch(self, mode=0o666, exist_ok=True): + """ + Create this file with the given access mode, if it doesn't exist. + """ + if self._closed: + self._raise_closed() + if exist_ok: + # First try to bump modification time + # Implementation note: GNU touch uses the UTIME_NOW option of + # the utimensat() / futimens() functions. + try: + self._accessor.utime(self, None) + except OSError: + # Avoid exception chaining + pass + else: + return + flags = os.O_CREAT | os.O_WRONLY + if not exist_ok: + flags |= os.O_EXCL + fd = self._raw_open(flags, mode) + os.close(fd) + + def mkdir(self, mode=0o777, parents=False, exist_ok=False): + """ + Create a new directory at this given path. + """ + if self._closed: + self._raise_closed() + try: + self._accessor.mkdir(self, mode) + except FileNotFoundError: + if not parents or self.parent == self: + raise + self.parent.mkdir(parents=True, exist_ok=True) + self.mkdir(mode, parents=False, exist_ok=exist_ok) + except OSError: + # Cannot rely on checking for EEXIST, since the operating system + # could give priority to other errors like EACCES or EROFS + if not exist_ok or not self.is_dir(): + raise + + def chmod(self, mode): + """ + Change the permissions of the path, like os.chmod(). + """ + if self._closed: + self._raise_closed() + self._accessor.chmod(self, mode) + + def lchmod(self, mode): + """ + Like chmod(), except if the path points to a symlink, the symlink's + permissions are changed, rather than its target's. + """ + if self._closed: + self._raise_closed() + self._accessor.lchmod(self, mode) + + def unlink(self, missing_ok=False): + """ + Remove this file or link. + If the path is a directory, use rmdir() instead. + """ + if self._closed: + self._raise_closed() + try: + self._accessor.unlink(self) + except FileNotFoundError: + if not missing_ok: + raise + + def rmdir(self): + """ + Remove this directory. The directory must be empty. + """ + if self._closed: + self._raise_closed() + self._accessor.rmdir(self) + + def lstat(self): + """ + Like stat(), except if the path points to a symlink, the symlink's + status information is returned, rather than its target's. + """ + if self._closed: + self._raise_closed() + return self._accessor.lstat(self) + + def rename(self, target): + """ + Rename this path to the target path. + + The target path may be absolute or relative. Relative paths are + interpreted relative to the current working directory, *not* the + directory of the Path object. + + Returns the new Path instance pointing to the target path. + """ + if self._closed: + self._raise_closed() + self._accessor.rename(self, target) + return self.__class__(target) + + def replace(self, target): + """ + Rename this path to the target path, overwriting if that path exists. + + The target path may be absolute or relative. Relative paths are + interpreted relative to the current working directory, *not* the + directory of the Path object. + + Returns the new Path instance pointing to the target path. + """ + if self._closed: + self._raise_closed() + self._accessor.replace(self, target) + return self.__class__(target) + + def symlink_to(self, target, target_is_directory=False): + """ + Make this path a symlink pointing to the target path. + Note the order of arguments (link, target) is the reverse of os.symlink. + """ + if self._closed: + self._raise_closed() + self._accessor.symlink(target, self, target_is_directory) + + def link_to(self, target): + """ + Make the target path a hard link pointing to this path. + + Note this function does not make this path a hard link to *target*, + despite the implication of the function and argument names. The order + of arguments (target, link) is the reverse of Path.symlink_to, but + matches that of os.link. + + """ + if self._closed: + self._raise_closed() + self._accessor.link_to(self, target) + + # Convenience functions for querying the stat results + + def exists(self): + """ + Whether this path exists. + """ + try: + self.stat() + except OSError as e: + if not _ignore_error(e): + raise + return False + except ValueError: + # Non-encodable path + return False + return True + + def is_dir(self): + """ + Whether this path is a directory. + """ + try: + return S_ISDIR(self.stat().st_mode) + except OSError as e: + if not _ignore_error(e): + raise + # Path doesn't exist or is a broken symlink + # (see https://bitbucket.org/pitrou/pathlib/issue/12/) + return False + except ValueError: + # Non-encodable path + return False + + def is_file(self): + """ + Whether this path is a regular file (also True for symlinks pointing + to regular files). + """ + try: + return S_ISREG(self.stat().st_mode) + except OSError as e: + if not _ignore_error(e): + raise + # Path doesn't exist or is a broken symlink + # (see https://bitbucket.org/pitrou/pathlib/issue/12/) + return False + except ValueError: + # Non-encodable path + return False + + def is_mount(self): + """ + Check if this path is a POSIX mount point + """ + # Need to exist and be a dir + if not self.exists() or not self.is_dir(): + return False + + parent = Path(self.parent) + try: + parent_dev = parent.stat().st_dev + except OSError: + return False + + dev = self.stat().st_dev + if dev != parent_dev: + return True + ino = self.stat().st_ino + parent_ino = parent.stat().st_ino + return ino == parent_ino + + def is_symlink(self): + """ + Whether this path is a symbolic link. + """ + try: + return S_ISLNK(self.lstat().st_mode) + except OSError as e: + if not _ignore_error(e): + raise + # Path doesn't exist + return False + except ValueError: + # Non-encodable path + return False + + def is_block_device(self): + """ + Whether this path is a block device. + """ + try: + return S_ISBLK(self.stat().st_mode) + except OSError as e: + if not _ignore_error(e): + raise + # Path doesn't exist or is a broken symlink + # (see https://bitbucket.org/pitrou/pathlib/issue/12/) + return False + except ValueError: + # Non-encodable path + return False + + def is_char_device(self): + """ + Whether this path is a character device. + """ + try: + return S_ISCHR(self.stat().st_mode) + except OSError as e: + if not _ignore_error(e): + raise + # Path doesn't exist or is a broken symlink + # (see https://bitbucket.org/pitrou/pathlib/issue/12/) + return False + except ValueError: + # Non-encodable path + return False + + def is_fifo(self): + """ + Whether this path is a FIFO. + """ + try: + return S_ISFIFO(self.stat().st_mode) + except OSError as e: + if not _ignore_error(e): + raise + # Path doesn't exist or is a broken symlink + # (see https://bitbucket.org/pitrou/pathlib/issue/12/) + return False + except ValueError: + # Non-encodable path + return False + + def is_socket(self): + """ + Whether this path is a socket. + """ + try: + return S_ISSOCK(self.stat().st_mode) + except OSError as e: + if not _ignore_error(e): + raise + # Path doesn't exist or is a broken symlink + # (see https://bitbucket.org/pitrou/pathlib/issue/12/) + return False + except ValueError: + # Non-encodable path + return False + + def expanduser(self): + """ Return a new path with expanded ~ and ~user constructs + (as returned by os.path.expanduser) + """ + if (not (self._drv or self._root) and + self._parts and self._parts[0][:1] == '~'): + homedir = self._flavour.gethomedir(self._parts[0][1:]) + return self._from_parts([homedir] + self._parts[1:]) + + return self + + +class PosixPath(Path, PurePosixPath): + """Path subclass for non-Windows systems. + + On a POSIX system, instantiating a Path should return this object. + """ + __slots__ = () + +class WindowsPath(Path, PureWindowsPath): + """Path subclass for Windows systems. + + On a Windows system, instantiating a Path should return this object. + """ + __slots__ = () + + def owner(self): + raise NotImplementedError("Path.owner() is unsupported on this system") + + def group(self): + raise NotImplementedError("Path.group() is unsupported on this system") + + def is_mount(self): + raise NotImplementedError("Path.is_mount() is unsupported on this system") diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/pickle.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/pickle.py new file mode 100644 index 0000000000000000000000000000000000000000..05f30b6b168b5cb8961a2a1e55d3a0341b8b3816 --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/pickle.py @@ -0,0 +1,1807 @@ +"""Create portable serialized representations of Python objects. + +See module copyreg for a mechanism for registering custom picklers. +See module pickletools source for extensive comments. + +Classes: + + Pickler + Unpickler + +Functions: + + dump(object, file) + dumps(object) -> string + load(file) -> object + loads(string) -> object + +Misc variables: + + __version__ + format_version + compatible_formats + +""" + +from types import FunctionType +from copyreg import dispatch_table +from copyreg import _extension_registry, _inverted_registry, _extension_cache +from itertools import islice +from functools import partial +import sys +from sys import maxsize +from struct import pack, unpack +import re +import io +import codecs +import _compat_pickle + +__all__ = ["PickleError", "PicklingError", "UnpicklingError", "Pickler", + "Unpickler", "dump", "dumps", "load", "loads"] + +try: + from _pickle import PickleBuffer + __all__.append("PickleBuffer") + _HAVE_PICKLE_BUFFER = True +except ImportError: + _HAVE_PICKLE_BUFFER = False + + +# Shortcut for use in isinstance testing +bytes_types = (bytes, bytearray) + +# These are purely informational; no code uses these. +format_version = "4.0" # File format version we write +compatible_formats = ["1.0", # Original protocol 0 + "1.1", # Protocol 0 with INST added + "1.2", # Original protocol 1 + "1.3", # Protocol 1 with BINFLOAT added + "2.0", # Protocol 2 + "3.0", # Protocol 3 + "4.0", # Protocol 4 + "5.0", # Protocol 5 + ] # Old format versions we can read + +# This is the highest protocol number we know how to read. +HIGHEST_PROTOCOL = 5 + +# The protocol we write by default. May be less than HIGHEST_PROTOCOL. +# Only bump this if the oldest still supported version of Python already +# includes it. +DEFAULT_PROTOCOL = 4 + +class PickleError(Exception): + """A common base class for the other pickling exceptions.""" + pass + +class PicklingError(PickleError): + """This exception is raised when an unpicklable object is passed to the + dump() method. + + """ + pass + +class UnpicklingError(PickleError): + """This exception is raised when there is a problem unpickling an object, + such as a security violation. + + Note that other exceptions may also be raised during unpickling, including + (but not necessarily limited to) AttributeError, EOFError, ImportError, + and IndexError. + + """ + pass + +# An instance of _Stop is raised by Unpickler.load_stop() in response to +# the STOP opcode, passing the object that is the result of unpickling. +class _Stop(Exception): + def __init__(self, value): + self.value = value + +# Jython has PyStringMap; it's a dict subclass with string keys +try: + from org.python.core import PyStringMap +except ImportError: + PyStringMap = None + +# Pickle opcodes. See pickletools.py for extensive docs. The listing +# here is in kind-of alphabetical order of 1-character pickle code. +# pickletools groups them by purpose. + +MARK = b'(' # push special markobject on stack +STOP = b'.' # every pickle ends with STOP +POP = b'0' # discard topmost stack item +POP_MARK = b'1' # discard stack top through topmost markobject +DUP = b'2' # duplicate top stack item +FLOAT = b'F' # push float object; decimal string argument +INT = b'I' # push integer or bool; decimal string argument +BININT = b'J' # push four-byte signed int +BININT1 = b'K' # push 1-byte unsigned int +LONG = b'L' # push long; decimal string argument +BININT2 = b'M' # push 2-byte unsigned int +NONE = b'N' # push None +PERSID = b'P' # push persistent object; id is taken from string arg +BINPERSID = b'Q' # " " " ; " " " " stack +REDUCE = b'R' # apply callable to argtuple, both on stack +STRING = b'S' # push string; NL-terminated string argument +BINSTRING = b'T' # push string; counted binary string argument +SHORT_BINSTRING= b'U' # " " ; " " " " < 256 bytes +UNICODE = b'V' # push Unicode string; raw-unicode-escaped'd argument +BINUNICODE = b'X' # " " " ; counted UTF-8 string argument +APPEND = b'a' # append stack top to list below it +BUILD = b'b' # call __setstate__ or __dict__.update() +GLOBAL = b'c' # push self.find_class(modname, name); 2 string args +DICT = b'd' # build a dict from stack items +EMPTY_DICT = b'}' # push empty dict +APPENDS = b'e' # extend list on stack by topmost stack slice +GET = b'g' # push item from memo on stack; index is string arg +BINGET = b'h' # " " " " " " ; " " 1-byte arg +INST = b'i' # build & push class instance +LONG_BINGET = b'j' # push item from memo on stack; index is 4-byte arg +LIST = b'l' # build list from topmost stack items +EMPTY_LIST = b']' # push empty list +OBJ = b'o' # build & push class instance +PUT = b'p' # store stack top in memo; index is string arg +BINPUT = b'q' # " " " " " ; " " 1-byte arg +LONG_BINPUT = b'r' # " " " " " ; " " 4-byte arg +SETITEM = b's' # add key+value pair to dict +TUPLE = b't' # build tuple from topmost stack items +EMPTY_TUPLE = b')' # push empty tuple +SETITEMS = b'u' # modify dict by adding topmost key+value pairs +BINFLOAT = b'G' # push float; arg is 8-byte float encoding + +TRUE = b'I01\n' # not an opcode; see INT docs in pickletools.py +FALSE = b'I00\n' # not an opcode; see INT docs in pickletools.py + +# Protocol 2 + +PROTO = b'\x80' # identify pickle protocol +NEWOBJ = b'\x81' # build object by applying cls.__new__ to argtuple +EXT1 = b'\x82' # push object from extension registry; 1-byte index +EXT2 = b'\x83' # ditto, but 2-byte index +EXT4 = b'\x84' # ditto, but 4-byte index +TUPLE1 = b'\x85' # build 1-tuple from stack top +TUPLE2 = b'\x86' # build 2-tuple from two topmost stack items +TUPLE3 = b'\x87' # build 3-tuple from three topmost stack items +NEWTRUE = b'\x88' # push True +NEWFALSE = b'\x89' # push False +LONG1 = b'\x8a' # push long from < 256 bytes +LONG4 = b'\x8b' # push really big long + +_tuplesize2code = [EMPTY_TUPLE, TUPLE1, TUPLE2, TUPLE3] + +# Protocol 3 (Python 3.x) + +BINBYTES = b'B' # push bytes; counted binary string argument +SHORT_BINBYTES = b'C' # " " ; " " " " < 256 bytes + +# Protocol 4 + +SHORT_BINUNICODE = b'\x8c' # push short string; UTF-8 length < 256 bytes +BINUNICODE8 = b'\x8d' # push very long string +BINBYTES8 = b'\x8e' # push very long bytes string +EMPTY_SET = b'\x8f' # push empty set on the stack +ADDITEMS = b'\x90' # modify set by adding topmost stack items +FROZENSET = b'\x91' # build frozenset from topmost stack items +NEWOBJ_EX = b'\x92' # like NEWOBJ but work with keyword only arguments +STACK_GLOBAL = b'\x93' # same as GLOBAL but using names on the stacks +MEMOIZE = b'\x94' # store top of the stack in memo +FRAME = b'\x95' # indicate the beginning of a new frame + +# Protocol 5 + +BYTEARRAY8 = b'\x96' # push bytearray +NEXT_BUFFER = b'\x97' # push next out-of-band buffer +READONLY_BUFFER = b'\x98' # make top of stack readonly + +__all__.extend([x for x in dir() if re.match("[A-Z][A-Z0-9_]+$", x)]) + + +class _Framer: + + _FRAME_SIZE_MIN = 4 + _FRAME_SIZE_TARGET = 64 * 1024 + + def __init__(self, file_write): + self.file_write = file_write + self.current_frame = None + + def start_framing(self): + self.current_frame = io.BytesIO() + + def end_framing(self): + if self.current_frame and self.current_frame.tell() > 0: + self.commit_frame(force=True) + self.current_frame = None + + def commit_frame(self, force=False): + if self.current_frame: + f = self.current_frame + if f.tell() >= self._FRAME_SIZE_TARGET or force: + data = f.getbuffer() + write = self.file_write + if len(data) >= self._FRAME_SIZE_MIN: + # Issue a single call to the write method of the underlying + # file object for the frame opcode with the size of the + # frame. The concatenation is expected to be less expensive + # than issuing an additional call to write. + write(FRAME + pack("': + raise AttributeError("Can't get local attribute {!r} on {!r}" + .format(name, obj)) + try: + parent = obj + obj = getattr(obj, subpath) + except AttributeError: + raise AttributeError("Can't get attribute {!r} on {!r}" + .format(name, obj)) from None + return obj, parent + +def whichmodule(obj, name): + """Find the module an object belong to.""" + module_name = getattr(obj, '__module__', None) + if module_name is not None: + return module_name + # Protect the iteration by using a list copy of sys.modules against dynamic + # modules that trigger imports of other modules upon calls to getattr. + for module_name, module in sys.modules.copy().items(): + if (module_name == '__main__' + or module_name == '__mp_main__' # bpo-42406 + or module is None): + continue + try: + if _getattribute(module, name)[0] is obj: + return module_name + except AttributeError: + pass + return '__main__' + +def encode_long(x): + r"""Encode a long to a two's complement little-endian binary string. + Note that 0 is a special case, returning an empty string, to save a + byte in the LONG1 pickling context. + + >>> encode_long(0) + b'' + >>> encode_long(255) + b'\xff\x00' + >>> encode_long(32767) + b'\xff\x7f' + >>> encode_long(-256) + b'\x00\xff' + >>> encode_long(-32768) + b'\x00\x80' + >>> encode_long(-128) + b'\x80' + >>> encode_long(127) + b'\x7f' + >>> + """ + if x == 0: + return b'' + nbytes = (x.bit_length() >> 3) + 1 + result = x.to_bytes(nbytes, byteorder='little', signed=True) + if x < 0 and nbytes > 1: + if result[-1] == 0xff and (result[-2] & 0x80) != 0: + result = result[:-1] + return result + +def decode_long(data): + r"""Decode a long from a two's complement little-endian binary string. + + >>> decode_long(b'') + 0 + >>> decode_long(b"\xff\x00") + 255 + >>> decode_long(b"\xff\x7f") + 32767 + >>> decode_long(b"\x00\xff") + -256 + >>> decode_long(b"\x00\x80") + -32768 + >>> decode_long(b"\x80") + -128 + >>> decode_long(b"\x7f") + 127 + """ + return int.from_bytes(data, byteorder='little', signed=True) + + +# Pickling machinery + +class _Pickler: + + def __init__(self, file, protocol=None, *, fix_imports=True, + buffer_callback=None): + """This takes a binary file for writing a pickle data stream. + + The optional *protocol* argument tells the pickler to use the + given protocol; supported protocols are 0, 1, 2, 3, 4 and 5. + The default protocol is 4. It was introduced in Python 3.4, and + is incompatible with previous versions. + + Specifying a negative protocol version selects the highest + protocol version supported. The higher the protocol used, the + more recent the version of Python needed to read the pickle + produced. + + The *file* argument must have a write() method that accepts a + single bytes argument. It can thus be a file object opened for + binary writing, an io.BytesIO instance, or any other custom + object that meets this interface. + + If *fix_imports* is True and *protocol* is less than 3, pickle + will try to map the new Python 3 names to the old module names + used in Python 2, so that the pickle data stream is readable + with Python 2. + + If *buffer_callback* is None (the default), buffer views are + serialized into *file* as part of the pickle stream. + + If *buffer_callback* is not None, then it can be called any number + of times with a buffer view. If the callback returns a false value + (such as None), the given buffer is out-of-band; otherwise the + buffer is serialized in-band, i.e. inside the pickle stream. + + It is an error if *buffer_callback* is not None and *protocol* + is None or smaller than 5. + """ + if protocol is None: + protocol = DEFAULT_PROTOCOL + if protocol < 0: + protocol = HIGHEST_PROTOCOL + elif not 0 <= protocol <= HIGHEST_PROTOCOL: + raise ValueError("pickle protocol must be <= %d" % HIGHEST_PROTOCOL) + if buffer_callback is not None and protocol < 5: + raise ValueError("buffer_callback needs protocol >= 5") + self._buffer_callback = buffer_callback + try: + self._file_write = file.write + except AttributeError: + raise TypeError("file must have a 'write' attribute") + self.framer = _Framer(self._file_write) + self.write = self.framer.write + self._write_large_bytes = self.framer.write_large_bytes + self.memo = {} + self.proto = int(protocol) + self.bin = protocol >= 1 + self.fast = 0 + self.fix_imports = fix_imports and protocol < 3 + + def clear_memo(self): + """Clears the pickler's "memo". + + The memo is the data structure that remembers which objects the + pickler has already seen, so that shared or recursive objects + are pickled by reference and not by value. This method is + useful when re-using picklers. + """ + self.memo.clear() + + def dump(self, obj): + """Write a pickled representation of obj to the open file.""" + # Check whether Pickler was initialized correctly. This is + # only needed to mimic the behavior of _pickle.Pickler.dump(). + if not hasattr(self, "_file_write"): + raise PicklingError("Pickler.__init__() was not called by " + "%s.__init__()" % (self.__class__.__name__,)) + if self.proto >= 2: + self.write(PROTO + pack("= 4: + self.framer.start_framing() + self.save(obj) + self.write(STOP) + self.framer.end_framing() + + def memoize(self, obj): + """Store an object in the memo.""" + + # The Pickler memo is a dictionary mapping object ids to 2-tuples + # that contain the Unpickler memo key and the object being memoized. + # The memo key is written to the pickle and will become + # the key in the Unpickler's memo. The object is stored in the + # Pickler memo so that transient objects are kept alive during + # pickling. + + # The use of the Unpickler memo length as the memo key is just a + # convention. The only requirement is that the memo values be unique. + # But there appears no advantage to any other scheme, and this + # scheme allows the Unpickler memo to be implemented as a plain (but + # growable) array, indexed by memo key. + if self.fast: + return + assert id(obj) not in self.memo + idx = len(self.memo) + self.write(self.put(idx)) + self.memo[id(obj)] = idx, obj + + # Return a PUT (BINPUT, LONG_BINPUT) opcode string, with argument i. + def put(self, idx): + if self.proto >= 4: + return MEMOIZE + elif self.bin: + if idx < 256: + return BINPUT + pack("= 2 and func_name == "__newobj_ex__": + cls, args, kwargs = args + if not hasattr(cls, "__new__"): + raise PicklingError("args[0] from {} args has no __new__" + .format(func_name)) + if obj is not None and cls is not obj.__class__: + raise PicklingError("args[0] from {} args has the wrong class" + .format(func_name)) + if self.proto >= 4: + save(cls) + save(args) + save(kwargs) + write(NEWOBJ_EX) + else: + func = partial(cls.__new__, cls, *args, **kwargs) + save(func) + save(()) + write(REDUCE) + elif self.proto >= 2 and func_name == "__newobj__": + # A __reduce__ implementation can direct protocol 2 or newer to + # use the more efficient NEWOBJ opcode, while still + # allowing protocol 0 and 1 to work normally. For this to + # work, the function returned by __reduce__ should be + # called __newobj__, and its first argument should be a + # class. The implementation for __newobj__ + # should be as follows, although pickle has no way to + # verify this: + # + # def __newobj__(cls, *args): + # return cls.__new__(cls, *args) + # + # Protocols 0 and 1 will pickle a reference to __newobj__, + # while protocol 2 (and above) will pickle a reference to + # cls, the remaining args tuple, and the NEWOBJ code, + # which calls cls.__new__(cls, *args) at unpickling time + # (see load_newobj below). If __reduce__ returns a + # three-tuple, the state from the third tuple item will be + # pickled regardless of the protocol, calling __setstate__ + # at unpickling time (see load_build below). + # + # Note that no standard __newobj__ implementation exists; + # you have to provide your own. This is to enforce + # compatibility with Python 2.2 (pickles written using + # protocol 0 or 1 in Python 2.3 should be unpicklable by + # Python 2.2). + cls = args[0] + if not hasattr(cls, "__new__"): + raise PicklingError( + "args[0] from __newobj__ args has no __new__") + if obj is not None and cls is not obj.__class__: + raise PicklingError( + "args[0] from __newobj__ args has the wrong class") + args = args[1:] + save(cls) + save(args) + write(NEWOBJ) + else: + save(func) + save(args) + write(REDUCE) + + if obj is not None: + # If the object is already in the memo, this means it is + # recursive. In this case, throw away everything we put on the + # stack, and fetch the object back from the memo. + if id(obj) in self.memo: + write(POP + self.get(self.memo[id(obj)][0])) + else: + self.memoize(obj) + + # More new special cases (that work with older protocols as + # well): when __reduce__ returns a tuple with 4 or 5 items, + # the 4th and 5th item should be iterators that provide list + # items and dict items (as (key, value) tuples), or None. + + if listitems is not None: + self._batch_appends(listitems) + + if dictitems is not None: + self._batch_setitems(dictitems) + + if state is not None: + if state_setter is None: + save(state) + write(BUILD) + else: + # If a state_setter is specified, call it instead of load_build + # to update obj's with its previous state. + # First, push state_setter and its tuple of expected arguments + # (obj, state) onto the stack. + save(state_setter) + save(obj) # simple BINGET opcode as obj is already memoized. + save(state) + write(TUPLE2) + # Trigger a state_setter(obj, state) function call. + write(REDUCE) + # The purpose of state_setter is to carry-out an + # inplace modification of obj. We do not care about what the + # method might return, so its output is eventually removed from + # the stack. + write(POP) + + # Methods below this point are dispatched through the dispatch table + + dispatch = {} + + def save_none(self, obj): + self.write(NONE) + dispatch[type(None)] = save_none + + def save_bool(self, obj): + if self.proto >= 2: + self.write(NEWTRUE if obj else NEWFALSE) + else: + self.write(TRUE if obj else FALSE) + dispatch[bool] = save_bool + + def save_long(self, obj): + if self.bin: + # If the int is small enough to fit in a signed 4-byte 2's-comp + # format, we can store it more efficiently than the general + # case. + # First one- and two-byte unsigned ints: + if obj >= 0: + if obj <= 0xff: + self.write(BININT1 + pack("= 2: + encoded = encode_long(obj) + n = len(encoded) + if n < 256: + self.write(LONG1 + pack("d', obj)) + else: + self.write(FLOAT + repr(obj).encode("ascii") + b'\n') + dispatch[float] = save_float + + def save_bytes(self, obj): + if self.proto < 3: + if not obj: # bytes object is empty + self.save_reduce(bytes, (), obj=obj) + else: + self.save_reduce(codecs.encode, + (str(obj, 'latin1'), 'latin1'), obj=obj) + return + n = len(obj) + if n <= 0xff: + self.write(SHORT_BINBYTES + pack(" 0xffffffff and self.proto >= 4: + self._write_large_bytes(BINBYTES8 + pack("= self.framer._FRAME_SIZE_TARGET: + self._write_large_bytes(BINBYTES + pack("= self.framer._FRAME_SIZE_TARGET: + self._write_large_bytes(BYTEARRAY8 + pack("= 5") + with obj.raw() as m: + if not m.contiguous: + raise PicklingError("PickleBuffer can not be pickled when " + "pointing to a non-contiguous buffer") + in_band = True + if self._buffer_callback is not None: + in_band = bool(self._buffer_callback(obj)) + if in_band: + # Write data in-band + # XXX The C implementation avoids a copy here + if m.readonly: + self.save_bytes(m.tobytes()) + else: + self.save_bytearray(m.tobytes()) + else: + # Write data out-of-band + self.write(NEXT_BUFFER) + if m.readonly: + self.write(READONLY_BUFFER) + + dispatch[PickleBuffer] = save_picklebuffer + + def save_str(self, obj): + if self.bin: + encoded = obj.encode('utf-8', 'surrogatepass') + n = len(encoded) + if n <= 0xff and self.proto >= 4: + self.write(SHORT_BINUNICODE + pack(" 0xffffffff and self.proto >= 4: + self._write_large_bytes(BINUNICODE8 + pack("= self.framer._FRAME_SIZE_TARGET: + self._write_large_bytes(BINUNICODE + pack("= 2: + for element in obj: + save(element) + # Subtle. Same as in the big comment below. + if id(obj) in memo: + get = self.get(memo[id(obj)][0]) + self.write(POP * n + get) + else: + self.write(_tuplesize2code[n]) + self.memoize(obj) + return + + # proto 0 or proto 1 and tuple isn't empty, or proto > 1 and tuple + # has more than 3 elements. + write = self.write + write(MARK) + for element in obj: + save(element) + + if id(obj) in memo: + # Subtle. d was not in memo when we entered save_tuple(), so + # the process of saving the tuple's elements must have saved + # the tuple itself: the tuple is recursive. The proper action + # now is to throw away everything we put on the stack, and + # simply GET the tuple (it's already constructed). This check + # could have been done in the "for element" loop instead, but + # recursive tuples are a rare thing. + get = self.get(memo[id(obj)][0]) + if self.bin: + write(POP_MARK + get) + else: # proto 0 -- POP_MARK not available + write(POP * (n+1) + get) + return + + # No recursion. + write(TUPLE) + self.memoize(obj) + + dispatch[tuple] = save_tuple + + def save_list(self, obj): + if self.bin: + self.write(EMPTY_LIST) + else: # proto 0 -- can't use EMPTY_LIST + self.write(MARK + LIST) + + self.memoize(obj) + self._batch_appends(obj) + + dispatch[list] = save_list + + _BATCHSIZE = 1000 + + def _batch_appends(self, items): + # Helper to batch up APPENDS sequences + save = self.save + write = self.write + + if not self.bin: + for x in items: + save(x) + write(APPEND) + return + + it = iter(items) + while True: + tmp = list(islice(it, self._BATCHSIZE)) + n = len(tmp) + if n > 1: + write(MARK) + for x in tmp: + save(x) + write(APPENDS) + elif n: + save(tmp[0]) + write(APPEND) + # else tmp is empty, and we're done + if n < self._BATCHSIZE: + return + + def save_dict(self, obj): + if self.bin: + self.write(EMPTY_DICT) + else: # proto 0 -- can't use EMPTY_DICT + self.write(MARK + DICT) + + self.memoize(obj) + self._batch_setitems(obj.items()) + + dispatch[dict] = save_dict + if PyStringMap is not None: + dispatch[PyStringMap] = save_dict + + def _batch_setitems(self, items): + # Helper to batch up SETITEMS sequences; proto >= 1 only + save = self.save + write = self.write + + if not self.bin: + for k, v in items: + save(k) + save(v) + write(SETITEM) + return + + it = iter(items) + while True: + tmp = list(islice(it, self._BATCHSIZE)) + n = len(tmp) + if n > 1: + write(MARK) + for k, v in tmp: + save(k) + save(v) + write(SETITEMS) + elif n: + k, v = tmp[0] + save(k) + save(v) + write(SETITEM) + # else tmp is empty, and we're done + if n < self._BATCHSIZE: + return + + def save_set(self, obj): + save = self.save + write = self.write + + if self.proto < 4: + self.save_reduce(set, (list(obj),), obj=obj) + return + + write(EMPTY_SET) + self.memoize(obj) + + it = iter(obj) + while True: + batch = list(islice(it, self._BATCHSIZE)) + n = len(batch) + if n > 0: + write(MARK) + for item in batch: + save(item) + write(ADDITEMS) + if n < self._BATCHSIZE: + return + dispatch[set] = save_set + + def save_frozenset(self, obj): + save = self.save + write = self.write + + if self.proto < 4: + self.save_reduce(frozenset, (list(obj),), obj=obj) + return + + write(MARK) + for item in obj: + save(item) + + if id(obj) in self.memo: + # If the object is already in the memo, this means it is + # recursive. In this case, throw away everything we put on the + # stack, and fetch the object back from the memo. + write(POP_MARK + self.get(self.memo[id(obj)][0])) + return + + write(FROZENSET) + self.memoize(obj) + dispatch[frozenset] = save_frozenset + + def save_global(self, obj, name=None): + write = self.write + memo = self.memo + + if name is None: + name = getattr(obj, '__qualname__', None) + if name is None: + name = obj.__name__ + + module_name = whichmodule(obj, name) + try: + __import__(module_name, level=0) + module = sys.modules[module_name] + obj2, parent = _getattribute(module, name) + except (ImportError, KeyError, AttributeError): + raise PicklingError( + "Can't pickle %r: it's not found as %s.%s" % + (obj, module_name, name)) from None + else: + if obj2 is not obj: + raise PicklingError( + "Can't pickle %r: it's not the same object as %s.%s" % + (obj, module_name, name)) + + if self.proto >= 2: + code = _extension_registry.get((module_name, name)) + if code: + assert code > 0 + if code <= 0xff: + write(EXT1 + pack("= 3. + if self.proto >= 4: + self.save(module_name) + self.save(name) + write(STACK_GLOBAL) + elif parent is not module: + self.save_reduce(getattr, (parent, lastname)) + elif self.proto >= 3: + write(GLOBAL + bytes(module_name, "utf-8") + b'\n' + + bytes(name, "utf-8") + b'\n') + else: + if self.fix_imports: + r_name_mapping = _compat_pickle.REVERSE_NAME_MAPPING + r_import_mapping = _compat_pickle.REVERSE_IMPORT_MAPPING + if (module_name, name) in r_name_mapping: + module_name, name = r_name_mapping[(module_name, name)] + elif module_name in r_import_mapping: + module_name = r_import_mapping[module_name] + try: + write(GLOBAL + bytes(module_name, "ascii") + b'\n' + + bytes(name, "ascii") + b'\n') + except UnicodeEncodeError: + raise PicklingError( + "can't pickle global identifier '%s.%s' using " + "pickle protocol %i" % (module, name, self.proto)) from None + + self.memoize(obj) + + def save_type(self, obj): + if obj is type(None): + return self.save_reduce(type, (None,), obj=obj) + elif obj is type(NotImplemented): + return self.save_reduce(type, (NotImplemented,), obj=obj) + elif obj is type(...): + return self.save_reduce(type, (...,), obj=obj) + return self.save_global(obj) + + dispatch[FunctionType] = save_global + dispatch[type] = save_type + + +# Unpickling machinery + +class _Unpickler: + + def __init__(self, file, *, fix_imports=True, + encoding="ASCII", errors="strict", buffers=None): + """This takes a binary file for reading a pickle data stream. + + The protocol version of the pickle is detected automatically, so + no proto argument is needed. + + The argument *file* must have two methods, a read() method that + takes an integer argument, and a readline() method that requires + no arguments. Both methods should return bytes. Thus *file* + can be a binary file object opened for reading, an io.BytesIO + object, or any other custom object that meets this interface. + + The file-like object must have two methods, a read() method + that takes an integer argument, and a readline() method that + requires no arguments. Both methods should return bytes. + Thus file-like object can be a binary file object opened for + reading, a BytesIO object, or any other custom object that + meets this interface. + + If *buffers* is not None, it should be an iterable of buffer-enabled + objects that is consumed each time the pickle stream references + an out-of-band buffer view. Such buffers have been given in order + to the *buffer_callback* of a Pickler object. + + If *buffers* is None (the default), then the buffers are taken + from the pickle stream, assuming they are serialized there. + It is an error for *buffers* to be None if the pickle stream + was produced with a non-None *buffer_callback*. + + Other optional arguments are *fix_imports*, *encoding* and + *errors*, which are used to control compatibility support for + pickle stream generated by Python 2. If *fix_imports* is True, + pickle will try to map the old Python 2 names to the new names + used in Python 3. The *encoding* and *errors* tell pickle how + to decode 8-bit string instances pickled by Python 2; these + default to 'ASCII' and 'strict', respectively. *encoding* can be + 'bytes' to read theses 8-bit string instances as bytes objects. + """ + self._buffers = iter(buffers) if buffers is not None else None + self._file_readline = file.readline + self._file_read = file.read + self.memo = {} + self.encoding = encoding + self.errors = errors + self.proto = 0 + self.fix_imports = fix_imports + + def load(self): + """Read a pickled object representation from the open file. + + Return the reconstituted object hierarchy specified in the file. + """ + # Check whether Unpickler was initialized correctly. This is + # only needed to mimic the behavior of _pickle.Unpickler.dump(). + if not hasattr(self, "_file_read"): + raise UnpicklingError("Unpickler.__init__() was not called by " + "%s.__init__()" % (self.__class__.__name__,)) + self._unframer = _Unframer(self._file_read, self._file_readline) + self.read = self._unframer.read + self.readinto = self._unframer.readinto + self.readline = self._unframer.readline + self.metastack = [] + self.stack = [] + self.append = self.stack.append + self.proto = 0 + read = self.read + dispatch = self.dispatch + try: + while True: + key = read(1) + if not key: + raise EOFError + assert isinstance(key, bytes_types) + dispatch[key[0]](self) + except _Stop as stopinst: + return stopinst.value + + # Return a list of items pushed in the stack after last MARK instruction. + def pop_mark(self): + items = self.stack + self.stack = self.metastack.pop() + self.append = self.stack.append + return items + + def persistent_load(self, pid): + raise UnpicklingError("unsupported persistent id encountered") + + dispatch = {} + + def load_proto(self): + proto = self.read(1)[0] + if not 0 <= proto <= HIGHEST_PROTOCOL: + raise ValueError("unsupported pickle protocol: %d" % proto) + self.proto = proto + dispatch[PROTO[0]] = load_proto + + def load_frame(self): + frame_size, = unpack(' sys.maxsize: + raise ValueError("frame size > sys.maxsize: %d" % frame_size) + self._unframer.load_frame(frame_size) + dispatch[FRAME[0]] = load_frame + + def load_persid(self): + try: + pid = self.readline()[:-1].decode("ascii") + except UnicodeDecodeError: + raise UnpicklingError( + "persistent IDs in protocol 0 must be ASCII strings") + self.append(self.persistent_load(pid)) + dispatch[PERSID[0]] = load_persid + + def load_binpersid(self): + pid = self.stack.pop() + self.append(self.persistent_load(pid)) + dispatch[BINPERSID[0]] = load_binpersid + + def load_none(self): + self.append(None) + dispatch[NONE[0]] = load_none + + def load_false(self): + self.append(False) + dispatch[NEWFALSE[0]] = load_false + + def load_true(self): + self.append(True) + dispatch[NEWTRUE[0]] = load_true + + def load_int(self): + data = self.readline() + if data == FALSE[1:]: + val = False + elif data == TRUE[1:]: + val = True + else: + val = int(data, 0) + self.append(val) + dispatch[INT[0]] = load_int + + def load_binint(self): + self.append(unpack('d', self.read(8))[0]) + dispatch[BINFLOAT[0]] = load_binfloat + + def _decode_string(self, value): + # Used to allow strings from Python 2 to be decoded either as + # bytes or Unicode strings. This should be used only with the + # STRING, BINSTRING and SHORT_BINSTRING opcodes. + if self.encoding == "bytes": + return value + else: + return value.decode(self.encoding, self.errors) + + def load_string(self): + data = self.readline()[:-1] + # Strip outermost quotes + if len(data) >= 2 and data[0] == data[-1] and data[0] in b'"\'': + data = data[1:-1] + else: + raise UnpicklingError("the STRING opcode argument must be quoted") + self.append(self._decode_string(codecs.escape_decode(data)[0])) + dispatch[STRING[0]] = load_string + + def load_binstring(self): + # Deprecated BINSTRING uses signed 32-bit length + len, = unpack(' maxsize: + raise UnpicklingError("BINBYTES exceeds system's maximum size " + "of %d bytes" % maxsize) + self.append(self.read(len)) + dispatch[BINBYTES[0]] = load_binbytes + + def load_unicode(self): + self.append(str(self.readline()[:-1], 'raw-unicode-escape')) + dispatch[UNICODE[0]] = load_unicode + + def load_binunicode(self): + len, = unpack(' maxsize: + raise UnpicklingError("BINUNICODE exceeds system's maximum size " + "of %d bytes" % maxsize) + self.append(str(self.read(len), 'utf-8', 'surrogatepass')) + dispatch[BINUNICODE[0]] = load_binunicode + + def load_binunicode8(self): + len, = unpack(' maxsize: + raise UnpicklingError("BINUNICODE8 exceeds system's maximum size " + "of %d bytes" % maxsize) + self.append(str(self.read(len), 'utf-8', 'surrogatepass')) + dispatch[BINUNICODE8[0]] = load_binunicode8 + + def load_binbytes8(self): + len, = unpack(' maxsize: + raise UnpicklingError("BINBYTES8 exceeds system's maximum size " + "of %d bytes" % maxsize) + self.append(self.read(len)) + dispatch[BINBYTES8[0]] = load_binbytes8 + + def load_bytearray8(self): + len, = unpack(' maxsize: + raise UnpicklingError("BYTEARRAY8 exceeds system's maximum size " + "of %d bytes" % maxsize) + b = bytearray(len) + self.readinto(b) + self.append(b) + dispatch[BYTEARRAY8[0]] = load_bytearray8 + + def load_next_buffer(self): + if self._buffers is None: + raise UnpicklingError("pickle stream refers to out-of-band data " + "but no *buffers* argument was given") + try: + buf = next(self._buffers) + except StopIteration: + raise UnpicklingError("not enough out-of-band buffers") + self.append(buf) + dispatch[NEXT_BUFFER[0]] = load_next_buffer + + def load_readonly_buffer(self): + buf = self.stack[-1] + with memoryview(buf) as m: + if not m.readonly: + self.stack[-1] = m.toreadonly() + dispatch[READONLY_BUFFER[0]] = load_readonly_buffer + + def load_short_binstring(self): + len = self.read(1)[0] + data = self.read(len) + self.append(self._decode_string(data)) + dispatch[SHORT_BINSTRING[0]] = load_short_binstring + + def load_short_binbytes(self): + len = self.read(1)[0] + self.append(self.read(len)) + dispatch[SHORT_BINBYTES[0]] = load_short_binbytes + + def load_short_binunicode(self): + len = self.read(1)[0] + self.append(str(self.read(len), 'utf-8', 'surrogatepass')) + dispatch[SHORT_BINUNICODE[0]] = load_short_binunicode + + def load_tuple(self): + items = self.pop_mark() + self.append(tuple(items)) + dispatch[TUPLE[0]] = load_tuple + + def load_empty_tuple(self): + self.append(()) + dispatch[EMPTY_TUPLE[0]] = load_empty_tuple + + def load_tuple1(self): + self.stack[-1] = (self.stack[-1],) + dispatch[TUPLE1[0]] = load_tuple1 + + def load_tuple2(self): + self.stack[-2:] = [(self.stack[-2], self.stack[-1])] + dispatch[TUPLE2[0]] = load_tuple2 + + def load_tuple3(self): + self.stack[-3:] = [(self.stack[-3], self.stack[-2], self.stack[-1])] + dispatch[TUPLE3[0]] = load_tuple3 + + def load_empty_list(self): + self.append([]) + dispatch[EMPTY_LIST[0]] = load_empty_list + + def load_empty_dictionary(self): + self.append({}) + dispatch[EMPTY_DICT[0]] = load_empty_dictionary + + def load_empty_set(self): + self.append(set()) + dispatch[EMPTY_SET[0]] = load_empty_set + + def load_frozenset(self): + items = self.pop_mark() + self.append(frozenset(items)) + dispatch[FROZENSET[0]] = load_frozenset + + def load_list(self): + items = self.pop_mark() + self.append(items) + dispatch[LIST[0]] = load_list + + def load_dict(self): + items = self.pop_mark() + d = {items[i]: items[i+1] + for i in range(0, len(items), 2)} + self.append(d) + dispatch[DICT[0]] = load_dict + + # INST and OBJ differ only in how they get a class object. It's not + # only sensible to do the rest in a common routine, the two routines + # previously diverged and grew different bugs. + # klass is the class to instantiate, and k points to the topmost mark + # object, following which are the arguments for klass.__init__. + def _instantiate(self, klass, args): + if (args or not isinstance(klass, type) or + hasattr(klass, "__getinitargs__")): + try: + value = klass(*args) + except TypeError as err: + raise TypeError("in constructor for %s: %s" % + (klass.__name__, str(err)), sys.exc_info()[2]) + else: + value = klass.__new__(klass) + self.append(value) + + def load_inst(self): + module = self.readline()[:-1].decode("ascii") + name = self.readline()[:-1].decode("ascii") + klass = self.find_class(module, name) + self._instantiate(klass, self.pop_mark()) + dispatch[INST[0]] = load_inst + + def load_obj(self): + # Stack is ... markobject classobject arg1 arg2 ... + args = self.pop_mark() + cls = args.pop(0) + self._instantiate(cls, args) + dispatch[OBJ[0]] = load_obj + + def load_newobj(self): + args = self.stack.pop() + cls = self.stack.pop() + obj = cls.__new__(cls, *args) + self.append(obj) + dispatch[NEWOBJ[0]] = load_newobj + + def load_newobj_ex(self): + kwargs = self.stack.pop() + args = self.stack.pop() + cls = self.stack.pop() + obj = cls.__new__(cls, *args, **kwargs) + self.append(obj) + dispatch[NEWOBJ_EX[0]] = load_newobj_ex + + def load_global(self): + module = self.readline()[:-1].decode("utf-8") + name = self.readline()[:-1].decode("utf-8") + klass = self.find_class(module, name) + self.append(klass) + dispatch[GLOBAL[0]] = load_global + + def load_stack_global(self): + name = self.stack.pop() + module = self.stack.pop() + if type(name) is not str or type(module) is not str: + raise UnpicklingError("STACK_GLOBAL requires str") + self.append(self.find_class(module, name)) + dispatch[STACK_GLOBAL[0]] = load_stack_global + + def load_ext1(self): + code = self.read(1)[0] + self.get_extension(code) + dispatch[EXT1[0]] = load_ext1 + + def load_ext2(self): + code, = unpack('= 4: + return _getattribute(sys.modules[module], name)[0] + else: + return getattr(sys.modules[module], name) + + def load_reduce(self): + stack = self.stack + args = stack.pop() + func = stack[-1] + stack[-1] = func(*args) + dispatch[REDUCE[0]] = load_reduce + + def load_pop(self): + if self.stack: + del self.stack[-1] + else: + self.pop_mark() + dispatch[POP[0]] = load_pop + + def load_pop_mark(self): + self.pop_mark() + dispatch[POP_MARK[0]] = load_pop_mark + + def load_dup(self): + self.append(self.stack[-1]) + dispatch[DUP[0]] = load_dup + + def load_get(self): + i = int(self.readline()[:-1]) + self.append(self.memo[i]) + dispatch[GET[0]] = load_get + + def load_binget(self): + i = self.read(1)[0] + self.append(self.memo[i]) + dispatch[BINGET[0]] = load_binget + + def load_long_binget(self): + i, = unpack(' maxsize: + raise ValueError("negative LONG_BINPUT argument") + self.memo[i] = self.stack[-1] + dispatch[LONG_BINPUT[0]] = load_long_binput + + def load_memoize(self): + memo = self.memo + memo[len(memo)] = self.stack[-1] + dispatch[MEMOIZE[0]] = load_memoize + + def load_append(self): + stack = self.stack + value = stack.pop() + list = stack[-1] + list.append(value) + dispatch[APPEND[0]] = load_append + + def load_appends(self): + items = self.pop_mark() + list_obj = self.stack[-1] + try: + extend = list_obj.extend + except AttributeError: + pass + else: + extend(items) + return + # Even if the PEP 307 requires extend() and append() methods, + # fall back on append() if the object has no extend() method + # for backward compatibility. + append = list_obj.append + for item in items: + append(item) + dispatch[APPENDS[0]] = load_appends + + def load_setitem(self): + stack = self.stack + value = stack.pop() + key = stack.pop() + dict = stack[-1] + dict[key] = value + dispatch[SETITEM[0]] = load_setitem + + def load_setitems(self): + items = self.pop_mark() + dict = self.stack[-1] + for i in range(0, len(items), 2): + dict[items[i]] = items[i + 1] + dispatch[SETITEMS[0]] = load_setitems + + def load_additems(self): + items = self.pop_mark() + set_obj = self.stack[-1] + if isinstance(set_obj, set): + set_obj.update(items) + else: + add = set_obj.add + for item in items: + add(item) + dispatch[ADDITEMS[0]] = load_additems + + def load_build(self): + stack = self.stack + state = stack.pop() + inst = stack[-1] + setstate = getattr(inst, "__setstate__", None) + if setstate is not None: + setstate(state) + return + slotstate = None + if isinstance(state, tuple) and len(state) == 2: + state, slotstate = state + if state: + inst_dict = inst.__dict__ + intern = sys.intern + for k, v in state.items(): + if type(k) is str: + inst_dict[intern(k)] = v + else: + inst_dict[k] = v + if slotstate: + for k, v in slotstate.items(): + setattr(inst, k, v) + dispatch[BUILD[0]] = load_build + + def load_mark(self): + self.metastack.append(self.stack) + self.stack = [] + self.append = self.stack.append + dispatch[MARK[0]] = load_mark + + def load_stop(self): + value = self.stack.pop() + raise _Stop(value) + dispatch[STOP[0]] = load_stop + + +# Shorthands + +def _dump(obj, file, protocol=None, *, fix_imports=True, buffer_callback=None): + _Pickler(file, protocol, fix_imports=fix_imports, + buffer_callback=buffer_callback).dump(obj) + +def _dumps(obj, protocol=None, *, fix_imports=True, buffer_callback=None): + f = io.BytesIO() + _Pickler(f, protocol, fix_imports=fix_imports, + buffer_callback=buffer_callback).dump(obj) + res = f.getvalue() + assert isinstance(res, bytes_types) + return res + +def _load(file, *, fix_imports=True, encoding="ASCII", errors="strict", + buffers=None): + return _Unpickler(file, fix_imports=fix_imports, buffers=buffers, + encoding=encoding, errors=errors).load() + +def _loads(s, *, fix_imports=True, encoding="ASCII", errors="strict", + buffers=None): + if isinstance(s, str): + raise TypeError("Can't load pickle from unicode string") + file = io.BytesIO(s) + return _Unpickler(file, fix_imports=fix_imports, buffers=buffers, + encoding=encoding, errors=errors).load() + +# Use the faster _pickle if possible +try: + from _pickle import ( + PickleError, + PicklingError, + UnpicklingError, + Pickler, + Unpickler, + dump, + dumps, + load, + loads + ) +except ImportError: + Pickler, Unpickler = _Pickler, _Unpickler + dump, dumps, load, loads = _dump, _dumps, _load, _loads + +# Doctest +def _test(): + import doctest + return doctest.testmod() + +if __name__ == "__main__": + import argparse + parser = argparse.ArgumentParser( + description='display contents of the pickle files') + parser.add_argument( + 'pickle_file', type=argparse.FileType('br'), + nargs='*', help='the pickle file') + parser.add_argument( + '-t', '--test', action='store_true', + help='run self-test suite') + parser.add_argument( + '-v', action='store_true', + help='run verbosely; only affects self-test run') + args = parser.parse_args() + if args.test: + _test() + else: + if not args.pickle_file: + parser.print_help() + else: + import pprint + for f in args.pickle_file: + obj = load(f) + pprint.pprint(obj) diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/pickletools.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/pickletools.py new file mode 100644 index 0000000000000000000000000000000000000000..95706e746c9870c92996a622ef56a25c41aa88cd --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/pickletools.py @@ -0,0 +1,2890 @@ +'''"Executable documentation" for the pickle module. + +Extensive comments about the pickle protocols and pickle-machine opcodes +can be found here. Some functions meant for external use: + +genops(pickle) + Generate all the opcodes in a pickle, as (opcode, arg, position) triples. + +dis(pickle, out=None, memo=None, indentlevel=4) + Print a symbolic disassembly of a pickle. +''' + +import codecs +import io +import pickle +import re +import sys + +__all__ = ['dis', 'genops', 'optimize'] + +bytes_types = pickle.bytes_types + +# Other ideas: +# +# - A pickle verifier: read a pickle and check it exhaustively for +# well-formedness. dis() does a lot of this already. +# +# - A protocol identifier: examine a pickle and return its protocol number +# (== the highest .proto attr value among all the opcodes in the pickle). +# dis() already prints this info at the end. +# +# - A pickle optimizer: for example, tuple-building code is sometimes more +# elaborate than necessary, catering for the possibility that the tuple +# is recursive. Or lots of times a PUT is generated that's never accessed +# by a later GET. + + +# "A pickle" is a program for a virtual pickle machine (PM, but more accurately +# called an unpickling machine). It's a sequence of opcodes, interpreted by the +# PM, building an arbitrarily complex Python object. +# +# For the most part, the PM is very simple: there are no looping, testing, or +# conditional instructions, no arithmetic and no function calls. Opcodes are +# executed once each, from first to last, until a STOP opcode is reached. +# +# The PM has two data areas, "the stack" and "the memo". +# +# Many opcodes push Python objects onto the stack; e.g., INT pushes a Python +# integer object on the stack, whose value is gotten from a decimal string +# literal immediately following the INT opcode in the pickle bytestream. Other +# opcodes take Python objects off the stack. The result of unpickling is +# whatever object is left on the stack when the final STOP opcode is executed. +# +# The memo is simply an array of objects, or it can be implemented as a dict +# mapping little integers to objects. The memo serves as the PM's "long term +# memory", and the little integers indexing the memo are akin to variable +# names. Some opcodes pop a stack object into the memo at a given index, +# and others push a memo object at a given index onto the stack again. +# +# At heart, that's all the PM has. Subtleties arise for these reasons: +# +# + Object identity. Objects can be arbitrarily complex, and subobjects +# may be shared (for example, the list [a, a] refers to the same object a +# twice). It can be vital that unpickling recreate an isomorphic object +# graph, faithfully reproducing sharing. +# +# + Recursive objects. For example, after "L = []; L.append(L)", L is a +# list, and L[0] is the same list. This is related to the object identity +# point, and some sequences of pickle opcodes are subtle in order to +# get the right result in all cases. +# +# + Things pickle doesn't know everything about. Examples of things pickle +# does know everything about are Python's builtin scalar and container +# types, like ints and tuples. They generally have opcodes dedicated to +# them. For things like module references and instances of user-defined +# classes, pickle's knowledge is limited. Historically, many enhancements +# have been made to the pickle protocol in order to do a better (faster, +# and/or more compact) job on those. +# +# + Backward compatibility and micro-optimization. As explained below, +# pickle opcodes never go away, not even when better ways to do a thing +# get invented. The repertoire of the PM just keeps growing over time. +# For example, protocol 0 had two opcodes for building Python integers (INT +# and LONG), protocol 1 added three more for more-efficient pickling of short +# integers, and protocol 2 added two more for more-efficient pickling of +# long integers (before protocol 2, the only ways to pickle a Python long +# took time quadratic in the number of digits, for both pickling and +# unpickling). "Opcode bloat" isn't so much a subtlety as a source of +# wearying complication. +# +# +# Pickle protocols: +# +# For compatibility, the meaning of a pickle opcode never changes. Instead new +# pickle opcodes get added, and each version's unpickler can handle all the +# pickle opcodes in all protocol versions to date. So old pickles continue to +# be readable forever. The pickler can generally be told to restrict itself to +# the subset of opcodes available under previous protocol versions too, so that +# users can create pickles under the current version readable by older +# versions. However, a pickle does not contain its version number embedded +# within it. If an older unpickler tries to read a pickle using a later +# protocol, the result is most likely an exception due to seeing an unknown (in +# the older unpickler) opcode. +# +# The original pickle used what's now called "protocol 0", and what was called +# "text mode" before Python 2.3. The entire pickle bytestream is made up of +# printable 7-bit ASCII characters, plus the newline character, in protocol 0. +# That's why it was called text mode. Protocol 0 is small and elegant, but +# sometimes painfully inefficient. +# +# The second major set of additions is now called "protocol 1", and was called +# "binary mode" before Python 2.3. This added many opcodes with arguments +# consisting of arbitrary bytes, including NUL bytes and unprintable "high bit" +# bytes. Binary mode pickles can be substantially smaller than equivalent +# text mode pickles, and sometimes faster too; e.g., BININT represents a 4-byte +# int as 4 bytes following the opcode, which is cheaper to unpickle than the +# (perhaps) 11-character decimal string attached to INT. Protocol 1 also added +# a number of opcodes that operate on many stack elements at once (like APPENDS +# and SETITEMS), and "shortcut" opcodes (like EMPTY_DICT and EMPTY_TUPLE). +# +# The third major set of additions came in Python 2.3, and is called "protocol +# 2". This added: +# +# - A better way to pickle instances of new-style classes (NEWOBJ). +# +# - A way for a pickle to identify its protocol (PROTO). +# +# - Time- and space- efficient pickling of long ints (LONG{1,4}). +# +# - Shortcuts for small tuples (TUPLE{1,2,3}}. +# +# - Dedicated opcodes for bools (NEWTRUE, NEWFALSE). +# +# - The "extension registry", a vector of popular objects that can be pushed +# efficiently by index (EXT{1,2,4}). This is akin to the memo and GET, but +# the registry contents are predefined (there's nothing akin to the memo's +# PUT). +# +# Another independent change with Python 2.3 is the abandonment of any +# pretense that it might be safe to load pickles received from untrusted +# parties -- no sufficient security analysis has been done to guarantee +# this and there isn't a use case that warrants the expense of such an +# analysis. +# +# To this end, all tests for __safe_for_unpickling__ or for +# copyreg.safe_constructors are removed from the unpickling code. +# References to these variables in the descriptions below are to be seen +# as describing unpickling in Python 2.2 and before. + + +# Meta-rule: Descriptions are stored in instances of descriptor objects, +# with plain constructors. No meta-language is defined from which +# descriptors could be constructed. If you want, e.g., XML, write a little +# program to generate XML from the objects. + +############################################################################## +# Some pickle opcodes have an argument, following the opcode in the +# bytestream. An argument is of a specific type, described by an instance +# of ArgumentDescriptor. These are not to be confused with arguments taken +# off the stack -- ArgumentDescriptor applies only to arguments embedded in +# the opcode stream, immediately following an opcode. + +# Represents the number of bytes consumed by an argument delimited by the +# next newline character. +UP_TO_NEWLINE = -1 + +# Represents the number of bytes consumed by a two-argument opcode where +# the first argument gives the number of bytes in the second argument. +TAKEN_FROM_ARGUMENT1 = -2 # num bytes is 1-byte unsigned int +TAKEN_FROM_ARGUMENT4 = -3 # num bytes is 4-byte signed little-endian int +TAKEN_FROM_ARGUMENT4U = -4 # num bytes is 4-byte unsigned little-endian int +TAKEN_FROM_ARGUMENT8U = -5 # num bytes is 8-byte unsigned little-endian int + +class ArgumentDescriptor(object): + __slots__ = ( + # name of descriptor record, also a module global name; a string + 'name', + + # length of argument, in bytes; an int; UP_TO_NEWLINE and + # TAKEN_FROM_ARGUMENT{1,4,8} are negative values for variable-length + # cases + 'n', + + # a function taking a file-like object, reading this kind of argument + # from the object at the current position, advancing the current + # position by n bytes, and returning the value of the argument + 'reader', + + # human-readable docs for this arg descriptor; a string + 'doc', + ) + + def __init__(self, name, n, reader, doc): + assert isinstance(name, str) + self.name = name + + assert isinstance(n, int) and (n >= 0 or + n in (UP_TO_NEWLINE, + TAKEN_FROM_ARGUMENT1, + TAKEN_FROM_ARGUMENT4, + TAKEN_FROM_ARGUMENT4U, + TAKEN_FROM_ARGUMENT8U)) + self.n = n + + self.reader = reader + + assert isinstance(doc, str) + self.doc = doc + +from struct import unpack as _unpack + +def read_uint1(f): + r""" + >>> import io + >>> read_uint1(io.BytesIO(b'\xff')) + 255 + """ + + data = f.read(1) + if data: + return data[0] + raise ValueError("not enough data in stream to read uint1") + +uint1 = ArgumentDescriptor( + name='uint1', + n=1, + reader=read_uint1, + doc="One-byte unsigned integer.") + + +def read_uint2(f): + r""" + >>> import io + >>> read_uint2(io.BytesIO(b'\xff\x00')) + 255 + >>> read_uint2(io.BytesIO(b'\xff\xff')) + 65535 + """ + + data = f.read(2) + if len(data) == 2: + return _unpack(">> import io + >>> read_int4(io.BytesIO(b'\xff\x00\x00\x00')) + 255 + >>> read_int4(io.BytesIO(b'\x00\x00\x00\x80')) == -(2**31) + True + """ + + data = f.read(4) + if len(data) == 4: + return _unpack(">> import io + >>> read_uint4(io.BytesIO(b'\xff\x00\x00\x00')) + 255 + >>> read_uint4(io.BytesIO(b'\x00\x00\x00\x80')) == 2**31 + True + """ + + data = f.read(4) + if len(data) == 4: + return _unpack(">> import io + >>> read_uint8(io.BytesIO(b'\xff\x00\x00\x00\x00\x00\x00\x00')) + 255 + >>> read_uint8(io.BytesIO(b'\xff' * 8)) == 2**64-1 + True + """ + + data = f.read(8) + if len(data) == 8: + return _unpack(">> import io + >>> read_stringnl(io.BytesIO(b"'abcd'\nefg\n")) + 'abcd' + + >>> read_stringnl(io.BytesIO(b"\n")) + Traceback (most recent call last): + ... + ValueError: no string quotes around b'' + + >>> read_stringnl(io.BytesIO(b"\n"), stripquotes=False) + '' + + >>> read_stringnl(io.BytesIO(b"''\n")) + '' + + >>> read_stringnl(io.BytesIO(b'"abcd"')) + Traceback (most recent call last): + ... + ValueError: no newline found when trying to read stringnl + + Embedded escapes are undone in the result. + >>> read_stringnl(io.BytesIO(br"'a\n\\b\x00c\td'" + b"\n'e'")) + 'a\n\\b\x00c\td' + """ + + data = f.readline() + if not data.endswith(b'\n'): + raise ValueError("no newline found when trying to read stringnl") + data = data[:-1] # lose the newline + + if stripquotes: + for q in (b'"', b"'"): + if data.startswith(q): + if not data.endswith(q): + raise ValueError("strinq quote %r not found at both " + "ends of %r" % (q, data)) + data = data[1:-1] + break + else: + raise ValueError("no string quotes around %r" % data) + + if decode: + data = codecs.escape_decode(data)[0].decode("ascii") + return data + +stringnl = ArgumentDescriptor( + name='stringnl', + n=UP_TO_NEWLINE, + reader=read_stringnl, + doc="""A newline-terminated string. + + This is a repr-style string, with embedded escapes, and + bracketing quotes. + """) + +def read_stringnl_noescape(f): + return read_stringnl(f, stripquotes=False) + +stringnl_noescape = ArgumentDescriptor( + name='stringnl_noescape', + n=UP_TO_NEWLINE, + reader=read_stringnl_noescape, + doc="""A newline-terminated string. + + This is a str-style string, without embedded escapes, + or bracketing quotes. It should consist solely of + printable ASCII characters. + """) + +def read_stringnl_noescape_pair(f): + r""" + >>> import io + >>> read_stringnl_noescape_pair(io.BytesIO(b"Queue\nEmpty\njunk")) + 'Queue Empty' + """ + + return "%s %s" % (read_stringnl_noescape(f), read_stringnl_noescape(f)) + +stringnl_noescape_pair = ArgumentDescriptor( + name='stringnl_noescape_pair', + n=UP_TO_NEWLINE, + reader=read_stringnl_noescape_pair, + doc="""A pair of newline-terminated strings. + + These are str-style strings, without embedded + escapes, or bracketing quotes. They should + consist solely of printable ASCII characters. + The pair is returned as a single string, with + a single blank separating the two strings. + """) + + +def read_string1(f): + r""" + >>> import io + >>> read_string1(io.BytesIO(b"\x00")) + '' + >>> read_string1(io.BytesIO(b"\x03abcdef")) + 'abc' + """ + + n = read_uint1(f) + assert n >= 0 + data = f.read(n) + if len(data) == n: + return data.decode("latin-1") + raise ValueError("expected %d bytes in a string1, but only %d remain" % + (n, len(data))) + +string1 = ArgumentDescriptor( + name="string1", + n=TAKEN_FROM_ARGUMENT1, + reader=read_string1, + doc="""A counted string. + + The first argument is a 1-byte unsigned int giving the number + of bytes in the string, and the second argument is that many + bytes. + """) + + +def read_string4(f): + r""" + >>> import io + >>> read_string4(io.BytesIO(b"\x00\x00\x00\x00abc")) + '' + >>> read_string4(io.BytesIO(b"\x03\x00\x00\x00abcdef")) + 'abc' + >>> read_string4(io.BytesIO(b"\x00\x00\x00\x03abcdef")) + Traceback (most recent call last): + ... + ValueError: expected 50331648 bytes in a string4, but only 6 remain + """ + + n = read_int4(f) + if n < 0: + raise ValueError("string4 byte count < 0: %d" % n) + data = f.read(n) + if len(data) == n: + return data.decode("latin-1") + raise ValueError("expected %d bytes in a string4, but only %d remain" % + (n, len(data))) + +string4 = ArgumentDescriptor( + name="string4", + n=TAKEN_FROM_ARGUMENT4, + reader=read_string4, + doc="""A counted string. + + The first argument is a 4-byte little-endian signed int giving + the number of bytes in the string, and the second argument is + that many bytes. + """) + + +def read_bytes1(f): + r""" + >>> import io + >>> read_bytes1(io.BytesIO(b"\x00")) + b'' + >>> read_bytes1(io.BytesIO(b"\x03abcdef")) + b'abc' + """ + + n = read_uint1(f) + assert n >= 0 + data = f.read(n) + if len(data) == n: + return data + raise ValueError("expected %d bytes in a bytes1, but only %d remain" % + (n, len(data))) + +bytes1 = ArgumentDescriptor( + name="bytes1", + n=TAKEN_FROM_ARGUMENT1, + reader=read_bytes1, + doc="""A counted bytes string. + + The first argument is a 1-byte unsigned int giving the number + of bytes, and the second argument is that many bytes. + """) + + +def read_bytes4(f): + r""" + >>> import io + >>> read_bytes4(io.BytesIO(b"\x00\x00\x00\x00abc")) + b'' + >>> read_bytes4(io.BytesIO(b"\x03\x00\x00\x00abcdef")) + b'abc' + >>> read_bytes4(io.BytesIO(b"\x00\x00\x00\x03abcdef")) + Traceback (most recent call last): + ... + ValueError: expected 50331648 bytes in a bytes4, but only 6 remain + """ + + n = read_uint4(f) + assert n >= 0 + if n > sys.maxsize: + raise ValueError("bytes4 byte count > sys.maxsize: %d" % n) + data = f.read(n) + if len(data) == n: + return data + raise ValueError("expected %d bytes in a bytes4, but only %d remain" % + (n, len(data))) + +bytes4 = ArgumentDescriptor( + name="bytes4", + n=TAKEN_FROM_ARGUMENT4U, + reader=read_bytes4, + doc="""A counted bytes string. + + The first argument is a 4-byte little-endian unsigned int giving + the number of bytes, and the second argument is that many bytes. + """) + + +def read_bytes8(f): + r""" + >>> import io, struct, sys + >>> read_bytes8(io.BytesIO(b"\x00\x00\x00\x00\x00\x00\x00\x00abc")) + b'' + >>> read_bytes8(io.BytesIO(b"\x03\x00\x00\x00\x00\x00\x00\x00abcdef")) + b'abc' + >>> bigsize8 = struct.pack(">> read_bytes8(io.BytesIO(bigsize8 + b"abcdef")) #doctest: +ELLIPSIS + Traceback (most recent call last): + ... + ValueError: expected ... bytes in a bytes8, but only 6 remain + """ + + n = read_uint8(f) + assert n >= 0 + if n > sys.maxsize: + raise ValueError("bytes8 byte count > sys.maxsize: %d" % n) + data = f.read(n) + if len(data) == n: + return data + raise ValueError("expected %d bytes in a bytes8, but only %d remain" % + (n, len(data))) + +bytes8 = ArgumentDescriptor( + name="bytes8", + n=TAKEN_FROM_ARGUMENT8U, + reader=read_bytes8, + doc="""A counted bytes string. + + The first argument is an 8-byte little-endian unsigned int giving + the number of bytes, and the second argument is that many bytes. + """) + + +def read_bytearray8(f): + r""" + >>> import io, struct, sys + >>> read_bytearray8(io.BytesIO(b"\x00\x00\x00\x00\x00\x00\x00\x00abc")) + bytearray(b'') + >>> read_bytearray8(io.BytesIO(b"\x03\x00\x00\x00\x00\x00\x00\x00abcdef")) + bytearray(b'abc') + >>> bigsize8 = struct.pack(">> read_bytearray8(io.BytesIO(bigsize8 + b"abcdef")) #doctest: +ELLIPSIS + Traceback (most recent call last): + ... + ValueError: expected ... bytes in a bytearray8, but only 6 remain + """ + + n = read_uint8(f) + assert n >= 0 + if n > sys.maxsize: + raise ValueError("bytearray8 byte count > sys.maxsize: %d" % n) + data = f.read(n) + if len(data) == n: + return bytearray(data) + raise ValueError("expected %d bytes in a bytearray8, but only %d remain" % + (n, len(data))) + +bytearray8 = ArgumentDescriptor( + name="bytearray8", + n=TAKEN_FROM_ARGUMENT8U, + reader=read_bytearray8, + doc="""A counted bytearray. + + The first argument is an 8-byte little-endian unsigned int giving + the number of bytes, and the second argument is that many bytes. + """) + +def read_unicodestringnl(f): + r""" + >>> import io + >>> read_unicodestringnl(io.BytesIO(b"abc\\uabcd\njunk")) == 'abc\uabcd' + True + """ + + data = f.readline() + if not data.endswith(b'\n'): + raise ValueError("no newline found when trying to read " + "unicodestringnl") + data = data[:-1] # lose the newline + return str(data, 'raw-unicode-escape') + +unicodestringnl = ArgumentDescriptor( + name='unicodestringnl', + n=UP_TO_NEWLINE, + reader=read_unicodestringnl, + doc="""A newline-terminated Unicode string. + + This is raw-unicode-escape encoded, so consists of + printable ASCII characters, and may contain embedded + escape sequences. + """) + + +def read_unicodestring1(f): + r""" + >>> import io + >>> s = 'abcd\uabcd' + >>> enc = s.encode('utf-8') + >>> enc + b'abcd\xea\xaf\x8d' + >>> n = bytes([len(enc)]) # little-endian 1-byte length + >>> t = read_unicodestring1(io.BytesIO(n + enc + b'junk')) + >>> s == t + True + + >>> read_unicodestring1(io.BytesIO(n + enc[:-1])) + Traceback (most recent call last): + ... + ValueError: expected 7 bytes in a unicodestring1, but only 6 remain + """ + + n = read_uint1(f) + assert n >= 0 + data = f.read(n) + if len(data) == n: + return str(data, 'utf-8', 'surrogatepass') + raise ValueError("expected %d bytes in a unicodestring1, but only %d " + "remain" % (n, len(data))) + +unicodestring1 = ArgumentDescriptor( + name="unicodestring1", + n=TAKEN_FROM_ARGUMENT1, + reader=read_unicodestring1, + doc="""A counted Unicode string. + + The first argument is a 1-byte little-endian signed int + giving the number of bytes in the string, and the second + argument-- the UTF-8 encoding of the Unicode string -- + contains that many bytes. + """) + + +def read_unicodestring4(f): + r""" + >>> import io + >>> s = 'abcd\uabcd' + >>> enc = s.encode('utf-8') + >>> enc + b'abcd\xea\xaf\x8d' + >>> n = bytes([len(enc), 0, 0, 0]) # little-endian 4-byte length + >>> t = read_unicodestring4(io.BytesIO(n + enc + b'junk')) + >>> s == t + True + + >>> read_unicodestring4(io.BytesIO(n + enc[:-1])) + Traceback (most recent call last): + ... + ValueError: expected 7 bytes in a unicodestring4, but only 6 remain + """ + + n = read_uint4(f) + assert n >= 0 + if n > sys.maxsize: + raise ValueError("unicodestring4 byte count > sys.maxsize: %d" % n) + data = f.read(n) + if len(data) == n: + return str(data, 'utf-8', 'surrogatepass') + raise ValueError("expected %d bytes in a unicodestring4, but only %d " + "remain" % (n, len(data))) + +unicodestring4 = ArgumentDescriptor( + name="unicodestring4", + n=TAKEN_FROM_ARGUMENT4U, + reader=read_unicodestring4, + doc="""A counted Unicode string. + + The first argument is a 4-byte little-endian signed int + giving the number of bytes in the string, and the second + argument-- the UTF-8 encoding of the Unicode string -- + contains that many bytes. + """) + + +def read_unicodestring8(f): + r""" + >>> import io + >>> s = 'abcd\uabcd' + >>> enc = s.encode('utf-8') + >>> enc + b'abcd\xea\xaf\x8d' + >>> n = bytes([len(enc)]) + b'\0' * 7 # little-endian 8-byte length + >>> t = read_unicodestring8(io.BytesIO(n + enc + b'junk')) + >>> s == t + True + + >>> read_unicodestring8(io.BytesIO(n + enc[:-1])) + Traceback (most recent call last): + ... + ValueError: expected 7 bytes in a unicodestring8, but only 6 remain + """ + + n = read_uint8(f) + assert n >= 0 + if n > sys.maxsize: + raise ValueError("unicodestring8 byte count > sys.maxsize: %d" % n) + data = f.read(n) + if len(data) == n: + return str(data, 'utf-8', 'surrogatepass') + raise ValueError("expected %d bytes in a unicodestring8, but only %d " + "remain" % (n, len(data))) + +unicodestring8 = ArgumentDescriptor( + name="unicodestring8", + n=TAKEN_FROM_ARGUMENT8U, + reader=read_unicodestring8, + doc="""A counted Unicode string. + + The first argument is an 8-byte little-endian signed int + giving the number of bytes in the string, and the second + argument-- the UTF-8 encoding of the Unicode string -- + contains that many bytes. + """) + + +def read_decimalnl_short(f): + r""" + >>> import io + >>> read_decimalnl_short(io.BytesIO(b"1234\n56")) + 1234 + + >>> read_decimalnl_short(io.BytesIO(b"1234L\n56")) + Traceback (most recent call last): + ... + ValueError: invalid literal for int() with base 10: b'1234L' + """ + + s = read_stringnl(f, decode=False, stripquotes=False) + + # There's a hack for True and False here. + if s == b"00": + return False + elif s == b"01": + return True + + return int(s) + +def read_decimalnl_long(f): + r""" + >>> import io + + >>> read_decimalnl_long(io.BytesIO(b"1234L\n56")) + 1234 + + >>> read_decimalnl_long(io.BytesIO(b"123456789012345678901234L\n6")) + 123456789012345678901234 + """ + + s = read_stringnl(f, decode=False, stripquotes=False) + if s[-1:] == b'L': + s = s[:-1] + return int(s) + + +decimalnl_short = ArgumentDescriptor( + name='decimalnl_short', + n=UP_TO_NEWLINE, + reader=read_decimalnl_short, + doc="""A newline-terminated decimal integer literal. + + This never has a trailing 'L', and the integer fit + in a short Python int on the box where the pickle + was written -- but there's no guarantee it will fit + in a short Python int on the box where the pickle + is read. + """) + +decimalnl_long = ArgumentDescriptor( + name='decimalnl_long', + n=UP_TO_NEWLINE, + reader=read_decimalnl_long, + doc="""A newline-terminated decimal integer literal. + + This has a trailing 'L', and can represent integers + of any size. + """) + + +def read_floatnl(f): + r""" + >>> import io + >>> read_floatnl(io.BytesIO(b"-1.25\n6")) + -1.25 + """ + s = read_stringnl(f, decode=False, stripquotes=False) + return float(s) + +floatnl = ArgumentDescriptor( + name='floatnl', + n=UP_TO_NEWLINE, + reader=read_floatnl, + doc="""A newline-terminated decimal floating literal. + + In general this requires 17 significant digits for roundtrip + identity, and pickling then unpickling infinities, NaNs, and + minus zero doesn't work across boxes, or on some boxes even + on itself (e.g., Windows can't read the strings it produces + for infinities or NaNs). + """) + +def read_float8(f): + r""" + >>> import io, struct + >>> raw = struct.pack(">d", -1.25) + >>> raw + b'\xbf\xf4\x00\x00\x00\x00\x00\x00' + >>> read_float8(io.BytesIO(raw + b"\n")) + -1.25 + """ + + data = f.read(8) + if len(data) == 8: + return _unpack(">d", data)[0] + raise ValueError("not enough data in stream to read float8") + + +float8 = ArgumentDescriptor( + name='float8', + n=8, + reader=read_float8, + doc="""An 8-byte binary representation of a float, big-endian. + + The format is unique to Python, and shared with the struct + module (format string '>d') "in theory" (the struct and pickle + implementations don't share the code -- they should). It's + strongly related to the IEEE-754 double format, and, in normal + cases, is in fact identical to the big-endian 754 double format. + On other boxes the dynamic range is limited to that of a 754 + double, and "add a half and chop" rounding is used to reduce + the precision to 53 bits. However, even on a 754 box, + infinities, NaNs, and minus zero may not be handled correctly + (may not survive roundtrip pickling intact). + """) + +# Protocol 2 formats + +from pickle import decode_long + +def read_long1(f): + r""" + >>> import io + >>> read_long1(io.BytesIO(b"\x00")) + 0 + >>> read_long1(io.BytesIO(b"\x02\xff\x00")) + 255 + >>> read_long1(io.BytesIO(b"\x02\xff\x7f")) + 32767 + >>> read_long1(io.BytesIO(b"\x02\x00\xff")) + -256 + >>> read_long1(io.BytesIO(b"\x02\x00\x80")) + -32768 + """ + + n = read_uint1(f) + data = f.read(n) + if len(data) != n: + raise ValueError("not enough data in stream to read long1") + return decode_long(data) + +long1 = ArgumentDescriptor( + name="long1", + n=TAKEN_FROM_ARGUMENT1, + reader=read_long1, + doc="""A binary long, little-endian, using 1-byte size. + + This first reads one byte as an unsigned size, then reads that + many bytes and interprets them as a little-endian 2's-complement long. + If the size is 0, that's taken as a shortcut for the long 0L. + """) + +def read_long4(f): + r""" + >>> import io + >>> read_long4(io.BytesIO(b"\x02\x00\x00\x00\xff\x00")) + 255 + >>> read_long4(io.BytesIO(b"\x02\x00\x00\x00\xff\x7f")) + 32767 + >>> read_long4(io.BytesIO(b"\x02\x00\x00\x00\x00\xff")) + -256 + >>> read_long4(io.BytesIO(b"\x02\x00\x00\x00\x00\x80")) + -32768 + >>> read_long1(io.BytesIO(b"\x00\x00\x00\x00")) + 0 + """ + + n = read_int4(f) + if n < 0: + raise ValueError("long4 byte count < 0: %d" % n) + data = f.read(n) + if len(data) != n: + raise ValueError("not enough data in stream to read long4") + return decode_long(data) + +long4 = ArgumentDescriptor( + name="long4", + n=TAKEN_FROM_ARGUMENT4, + reader=read_long4, + doc="""A binary representation of a long, little-endian. + + This first reads four bytes as a signed size (but requires the + size to be >= 0), then reads that many bytes and interprets them + as a little-endian 2's-complement long. If the size is 0, that's taken + as a shortcut for the int 0, although LONG1 should really be used + then instead (and in any case where # of bytes < 256). + """) + + +############################################################################## +# Object descriptors. The stack used by the pickle machine holds objects, +# and in the stack_before and stack_after attributes of OpcodeInfo +# descriptors we need names to describe the various types of objects that can +# appear on the stack. + +class StackObject(object): + __slots__ = ( + # name of descriptor record, for info only + 'name', + + # type of object, or tuple of type objects (meaning the object can + # be of any type in the tuple) + 'obtype', + + # human-readable docs for this kind of stack object; a string + 'doc', + ) + + def __init__(self, name, obtype, doc): + assert isinstance(name, str) + self.name = name + + assert isinstance(obtype, type) or isinstance(obtype, tuple) + if isinstance(obtype, tuple): + for contained in obtype: + assert isinstance(contained, type) + self.obtype = obtype + + assert isinstance(doc, str) + self.doc = doc + + def __repr__(self): + return self.name + + +pyint = pylong = StackObject( + name='int', + obtype=int, + doc="A Python integer object.") + +pyinteger_or_bool = StackObject( + name='int_or_bool', + obtype=(int, bool), + doc="A Python integer or boolean object.") + +pybool = StackObject( + name='bool', + obtype=bool, + doc="A Python boolean object.") + +pyfloat = StackObject( + name='float', + obtype=float, + doc="A Python float object.") + +pybytes_or_str = pystring = StackObject( + name='bytes_or_str', + obtype=(bytes, str), + doc="A Python bytes or (Unicode) string object.") + +pybytes = StackObject( + name='bytes', + obtype=bytes, + doc="A Python bytes object.") + +pybytearray = StackObject( + name='bytearray', + obtype=bytearray, + doc="A Python bytearray object.") + +pyunicode = StackObject( + name='str', + obtype=str, + doc="A Python (Unicode) string object.") + +pynone = StackObject( + name="None", + obtype=type(None), + doc="The Python None object.") + +pytuple = StackObject( + name="tuple", + obtype=tuple, + doc="A Python tuple object.") + +pylist = StackObject( + name="list", + obtype=list, + doc="A Python list object.") + +pydict = StackObject( + name="dict", + obtype=dict, + doc="A Python dict object.") + +pyset = StackObject( + name="set", + obtype=set, + doc="A Python set object.") + +pyfrozenset = StackObject( + name="frozenset", + obtype=set, + doc="A Python frozenset object.") + +pybuffer = StackObject( + name='buffer', + obtype=object, + doc="A Python buffer-like object.") + +anyobject = StackObject( + name='any', + obtype=object, + doc="Any kind of object whatsoever.") + +markobject = StackObject( + name="mark", + obtype=StackObject, + doc="""'The mark' is a unique object. + +Opcodes that operate on a variable number of objects +generally don't embed the count of objects in the opcode, +or pull it off the stack. Instead the MARK opcode is used +to push a special marker object on the stack, and then +some other opcodes grab all the objects from the top of +the stack down to (but not including) the topmost marker +object. +""") + +stackslice = StackObject( + name="stackslice", + obtype=StackObject, + doc="""An object representing a contiguous slice of the stack. + +This is used in conjunction with markobject, to represent all +of the stack following the topmost markobject. For example, +the POP_MARK opcode changes the stack from + + [..., markobject, stackslice] +to + [...] + +No matter how many object are on the stack after the topmost +markobject, POP_MARK gets rid of all of them (including the +topmost markobject too). +""") + +############################################################################## +# Descriptors for pickle opcodes. + +class OpcodeInfo(object): + + __slots__ = ( + # symbolic name of opcode; a string + 'name', + + # the code used in a bytestream to represent the opcode; a + # one-character string + 'code', + + # If the opcode has an argument embedded in the byte string, an + # instance of ArgumentDescriptor specifying its type. Note that + # arg.reader(s) can be used to read and decode the argument from + # the bytestream s, and arg.doc documents the format of the raw + # argument bytes. If the opcode doesn't have an argument embedded + # in the bytestream, arg should be None. + 'arg', + + # what the stack looks like before this opcode runs; a list + 'stack_before', + + # what the stack looks like after this opcode runs; a list + 'stack_after', + + # the protocol number in which this opcode was introduced; an int + 'proto', + + # human-readable docs for this opcode; a string + 'doc', + ) + + def __init__(self, name, code, arg, + stack_before, stack_after, proto, doc): + assert isinstance(name, str) + self.name = name + + assert isinstance(code, str) + assert len(code) == 1 + self.code = code + + assert arg is None or isinstance(arg, ArgumentDescriptor) + self.arg = arg + + assert isinstance(stack_before, list) + for x in stack_before: + assert isinstance(x, StackObject) + self.stack_before = stack_before + + assert isinstance(stack_after, list) + for x in stack_after: + assert isinstance(x, StackObject) + self.stack_after = stack_after + + assert isinstance(proto, int) and 0 <= proto <= pickle.HIGHEST_PROTOCOL + self.proto = proto + + assert isinstance(doc, str) + self.doc = doc + +I = OpcodeInfo +opcodes = [ + + # Ways to spell integers. + + I(name='INT', + code='I', + arg=decimalnl_short, + stack_before=[], + stack_after=[pyinteger_or_bool], + proto=0, + doc="""Push an integer or bool. + + The argument is a newline-terminated decimal literal string. + + The intent may have been that this always fit in a short Python int, + but INT can be generated in pickles written on a 64-bit box that + require a Python long on a 32-bit box. The difference between this + and LONG then is that INT skips a trailing 'L', and produces a short + int whenever possible. + + Another difference is due to that, when bool was introduced as a + distinct type in 2.3, builtin names True and False were also added to + 2.2.2, mapping to ints 1 and 0. For compatibility in both directions, + True gets pickled as INT + "I01\\n", and False as INT + "I00\\n". + Leading zeroes are never produced for a genuine integer. The 2.3 + (and later) unpicklers special-case these and return bool instead; + earlier unpicklers ignore the leading "0" and return the int. + """), + + I(name='BININT', + code='J', + arg=int4, + stack_before=[], + stack_after=[pyint], + proto=1, + doc="""Push a four-byte signed integer. + + This handles the full range of Python (short) integers on a 32-bit + box, directly as binary bytes (1 for the opcode and 4 for the integer). + If the integer is non-negative and fits in 1 or 2 bytes, pickling via + BININT1 or BININT2 saves space. + """), + + I(name='BININT1', + code='K', + arg=uint1, + stack_before=[], + stack_after=[pyint], + proto=1, + doc="""Push a one-byte unsigned integer. + + This is a space optimization for pickling very small non-negative ints, + in range(256). + """), + + I(name='BININT2', + code='M', + arg=uint2, + stack_before=[], + stack_after=[pyint], + proto=1, + doc="""Push a two-byte unsigned integer. + + This is a space optimization for pickling small positive ints, in + range(256, 2**16). Integers in range(256) can also be pickled via + BININT2, but BININT1 instead saves a byte. + """), + + I(name='LONG', + code='L', + arg=decimalnl_long, + stack_before=[], + stack_after=[pyint], + proto=0, + doc="""Push a long integer. + + The same as INT, except that the literal ends with 'L', and always + unpickles to a Python long. There doesn't seem a real purpose to the + trailing 'L'. + + Note that LONG takes time quadratic in the number of digits when + unpickling (this is simply due to the nature of decimal->binary + conversion). Proto 2 added linear-time (in C; still quadratic-time + in Python) LONG1 and LONG4 opcodes. + """), + + I(name="LONG1", + code='\x8a', + arg=long1, + stack_before=[], + stack_after=[pyint], + proto=2, + doc="""Long integer using one-byte length. + + A more efficient encoding of a Python long; the long1 encoding + says it all."""), + + I(name="LONG4", + code='\x8b', + arg=long4, + stack_before=[], + stack_after=[pyint], + proto=2, + doc="""Long integer using found-byte length. + + A more efficient encoding of a Python long; the long4 encoding + says it all."""), + + # Ways to spell strings (8-bit, not Unicode). + + I(name='STRING', + code='S', + arg=stringnl, + stack_before=[], + stack_after=[pybytes_or_str], + proto=0, + doc="""Push a Python string object. + + The argument is a repr-style string, with bracketing quote characters, + and perhaps embedded escapes. The argument extends until the next + newline character. These are usually decoded into a str instance + using the encoding given to the Unpickler constructor. or the default, + 'ASCII'. If the encoding given was 'bytes' however, they will be + decoded as bytes object instead. + """), + + I(name='BINSTRING', + code='T', + arg=string4, + stack_before=[], + stack_after=[pybytes_or_str], + proto=1, + doc="""Push a Python string object. + + There are two arguments: the first is a 4-byte little-endian + signed int giving the number of bytes in the string, and the + second is that many bytes, which are taken literally as the string + content. These are usually decoded into a str instance using the + encoding given to the Unpickler constructor. or the default, + 'ASCII'. If the encoding given was 'bytes' however, they will be + decoded as bytes object instead. + """), + + I(name='SHORT_BINSTRING', + code='U', + arg=string1, + stack_before=[], + stack_after=[pybytes_or_str], + proto=1, + doc="""Push a Python string object. + + There are two arguments: the first is a 1-byte unsigned int giving + the number of bytes in the string, and the second is that many + bytes, which are taken literally as the string content. These are + usually decoded into a str instance using the encoding given to + the Unpickler constructor. or the default, 'ASCII'. If the + encoding given was 'bytes' however, they will be decoded as bytes + object instead. + """), + + # Bytes (protocol 3 and higher) + + I(name='BINBYTES', + code='B', + arg=bytes4, + stack_before=[], + stack_after=[pybytes], + proto=3, + doc="""Push a Python bytes object. + + There are two arguments: the first is a 4-byte little-endian unsigned int + giving the number of bytes, and the second is that many bytes, which are + taken literally as the bytes content. + """), + + I(name='SHORT_BINBYTES', + code='C', + arg=bytes1, + stack_before=[], + stack_after=[pybytes], + proto=3, + doc="""Push a Python bytes object. + + There are two arguments: the first is a 1-byte unsigned int giving + the number of bytes, and the second is that many bytes, which are taken + literally as the string content. + """), + + I(name='BINBYTES8', + code='\x8e', + arg=bytes8, + stack_before=[], + stack_after=[pybytes], + proto=4, + doc="""Push a Python bytes object. + + There are two arguments: the first is an 8-byte unsigned int giving + the number of bytes in the string, and the second is that many bytes, + which are taken literally as the string content. + """), + + # Bytearray (protocol 5 and higher) + + I(name='BYTEARRAY8', + code='\x96', + arg=bytearray8, + stack_before=[], + stack_after=[pybytearray], + proto=5, + doc="""Push a Python bytearray object. + + There are two arguments: the first is an 8-byte unsigned int giving + the number of bytes in the bytearray, and the second is that many bytes, + which are taken literally as the bytearray content. + """), + + # Out-of-band buffer (protocol 5 and higher) + + I(name='NEXT_BUFFER', + code='\x97', + arg=None, + stack_before=[], + stack_after=[pybuffer], + proto=5, + doc="Push an out-of-band buffer object."), + + I(name='READONLY_BUFFER', + code='\x98', + arg=None, + stack_before=[pybuffer], + stack_after=[pybuffer], + proto=5, + doc="Make an out-of-band buffer object read-only."), + + # Ways to spell None. + + I(name='NONE', + code='N', + arg=None, + stack_before=[], + stack_after=[pynone], + proto=0, + doc="Push None on the stack."), + + # Ways to spell bools, starting with proto 2. See INT for how this was + # done before proto 2. + + I(name='NEWTRUE', + code='\x88', + arg=None, + stack_before=[], + stack_after=[pybool], + proto=2, + doc="Push True onto the stack."), + + I(name='NEWFALSE', + code='\x89', + arg=None, + stack_before=[], + stack_after=[pybool], + proto=2, + doc="Push False onto the stack."), + + # Ways to spell Unicode strings. + + I(name='UNICODE', + code='V', + arg=unicodestringnl, + stack_before=[], + stack_after=[pyunicode], + proto=0, # this may be pure-text, but it's a later addition + doc="""Push a Python Unicode string object. + + The argument is a raw-unicode-escape encoding of a Unicode string, + and so may contain embedded escape sequences. The argument extends + until the next newline character. + """), + + I(name='SHORT_BINUNICODE', + code='\x8c', + arg=unicodestring1, + stack_before=[], + stack_after=[pyunicode], + proto=4, + doc="""Push a Python Unicode string object. + + There are two arguments: the first is a 1-byte little-endian signed int + giving the number of bytes in the string. The second is that many + bytes, and is the UTF-8 encoding of the Unicode string. + """), + + I(name='BINUNICODE', + code='X', + arg=unicodestring4, + stack_before=[], + stack_after=[pyunicode], + proto=1, + doc="""Push a Python Unicode string object. + + There are two arguments: the first is a 4-byte little-endian unsigned int + giving the number of bytes in the string. The second is that many + bytes, and is the UTF-8 encoding of the Unicode string. + """), + + I(name='BINUNICODE8', + code='\x8d', + arg=unicodestring8, + stack_before=[], + stack_after=[pyunicode], + proto=4, + doc="""Push a Python Unicode string object. + + There are two arguments: the first is an 8-byte little-endian signed int + giving the number of bytes in the string. The second is that many + bytes, and is the UTF-8 encoding of the Unicode string. + """), + + # Ways to spell floats. + + I(name='FLOAT', + code='F', + arg=floatnl, + stack_before=[], + stack_after=[pyfloat], + proto=0, + doc="""Newline-terminated decimal float literal. + + The argument is repr(a_float), and in general requires 17 significant + digits for roundtrip conversion to be an identity (this is so for + IEEE-754 double precision values, which is what Python float maps to + on most boxes). + + In general, FLOAT cannot be used to transport infinities, NaNs, or + minus zero across boxes (or even on a single box, if the platform C + library can't read the strings it produces for such things -- Windows + is like that), but may do less damage than BINFLOAT on boxes with + greater precision or dynamic range than IEEE-754 double. + """), + + I(name='BINFLOAT', + code='G', + arg=float8, + stack_before=[], + stack_after=[pyfloat], + proto=1, + doc="""Float stored in binary form, with 8 bytes of data. + + This generally requires less than half the space of FLOAT encoding. + In general, BINFLOAT cannot be used to transport infinities, NaNs, or + minus zero, raises an exception if the exponent exceeds the range of + an IEEE-754 double, and retains no more than 53 bits of precision (if + there are more than that, "add a half and chop" rounding is used to + cut it back to 53 significant bits). + """), + + # Ways to build lists. + + I(name='EMPTY_LIST', + code=']', + arg=None, + stack_before=[], + stack_after=[pylist], + proto=1, + doc="Push an empty list."), + + I(name='APPEND', + code='a', + arg=None, + stack_before=[pylist, anyobject], + stack_after=[pylist], + proto=0, + doc="""Append an object to a list. + + Stack before: ... pylist anyobject + Stack after: ... pylist+[anyobject] + + although pylist is really extended in-place. + """), + + I(name='APPENDS', + code='e', + arg=None, + stack_before=[pylist, markobject, stackslice], + stack_after=[pylist], + proto=1, + doc="""Extend a list by a slice of stack objects. + + Stack before: ... pylist markobject stackslice + Stack after: ... pylist+stackslice + + although pylist is really extended in-place. + """), + + I(name='LIST', + code='l', + arg=None, + stack_before=[markobject, stackslice], + stack_after=[pylist], + proto=0, + doc="""Build a list out of the topmost stack slice, after markobject. + + All the stack entries following the topmost markobject are placed into + a single Python list, which single list object replaces all of the + stack from the topmost markobject onward. For example, + + Stack before: ... markobject 1 2 3 'abc' + Stack after: ... [1, 2, 3, 'abc'] + """), + + # Ways to build tuples. + + I(name='EMPTY_TUPLE', + code=')', + arg=None, + stack_before=[], + stack_after=[pytuple], + proto=1, + doc="Push an empty tuple."), + + I(name='TUPLE', + code='t', + arg=None, + stack_before=[markobject, stackslice], + stack_after=[pytuple], + proto=0, + doc="""Build a tuple out of the topmost stack slice, after markobject. + + All the stack entries following the topmost markobject are placed into + a single Python tuple, which single tuple object replaces all of the + stack from the topmost markobject onward. For example, + + Stack before: ... markobject 1 2 3 'abc' + Stack after: ... (1, 2, 3, 'abc') + """), + + I(name='TUPLE1', + code='\x85', + arg=None, + stack_before=[anyobject], + stack_after=[pytuple], + proto=2, + doc="""Build a one-tuple out of the topmost item on the stack. + + This code pops one value off the stack and pushes a tuple of + length 1 whose one item is that value back onto it. In other + words: + + stack[-1] = tuple(stack[-1:]) + """), + + I(name='TUPLE2', + code='\x86', + arg=None, + stack_before=[anyobject, anyobject], + stack_after=[pytuple], + proto=2, + doc="""Build a two-tuple out of the top two items on the stack. + + This code pops two values off the stack and pushes a tuple of + length 2 whose items are those values back onto it. In other + words: + + stack[-2:] = [tuple(stack[-2:])] + """), + + I(name='TUPLE3', + code='\x87', + arg=None, + stack_before=[anyobject, anyobject, anyobject], + stack_after=[pytuple], + proto=2, + doc="""Build a three-tuple out of the top three items on the stack. + + This code pops three values off the stack and pushes a tuple of + length 3 whose items are those values back onto it. In other + words: + + stack[-3:] = [tuple(stack[-3:])] + """), + + # Ways to build dicts. + + I(name='EMPTY_DICT', + code='}', + arg=None, + stack_before=[], + stack_after=[pydict], + proto=1, + doc="Push an empty dict."), + + I(name='DICT', + code='d', + arg=None, + stack_before=[markobject, stackslice], + stack_after=[pydict], + proto=0, + doc="""Build a dict out of the topmost stack slice, after markobject. + + All the stack entries following the topmost markobject are placed into + a single Python dict, which single dict object replaces all of the + stack from the topmost markobject onward. The stack slice alternates + key, value, key, value, .... For example, + + Stack before: ... markobject 1 2 3 'abc' + Stack after: ... {1: 2, 3: 'abc'} + """), + + I(name='SETITEM', + code='s', + arg=None, + stack_before=[pydict, anyobject, anyobject], + stack_after=[pydict], + proto=0, + doc="""Add a key+value pair to an existing dict. + + Stack before: ... pydict key value + Stack after: ... pydict + + where pydict has been modified via pydict[key] = value. + """), + + I(name='SETITEMS', + code='u', + arg=None, + stack_before=[pydict, markobject, stackslice], + stack_after=[pydict], + proto=1, + doc="""Add an arbitrary number of key+value pairs to an existing dict. + + The slice of the stack following the topmost markobject is taken as + an alternating sequence of keys and values, added to the dict + immediately under the topmost markobject. Everything at and after the + topmost markobject is popped, leaving the mutated dict at the top + of the stack. + + Stack before: ... pydict markobject key_1 value_1 ... key_n value_n + Stack after: ... pydict + + where pydict has been modified via pydict[key_i] = value_i for i in + 1, 2, ..., n, and in that order. + """), + + # Ways to build sets + + I(name='EMPTY_SET', + code='\x8f', + arg=None, + stack_before=[], + stack_after=[pyset], + proto=4, + doc="Push an empty set."), + + I(name='ADDITEMS', + code='\x90', + arg=None, + stack_before=[pyset, markobject, stackslice], + stack_after=[pyset], + proto=4, + doc="""Add an arbitrary number of items to an existing set. + + The slice of the stack following the topmost markobject is taken as + a sequence of items, added to the set immediately under the topmost + markobject. Everything at and after the topmost markobject is popped, + leaving the mutated set at the top of the stack. + + Stack before: ... pyset markobject item_1 ... item_n + Stack after: ... pyset + + where pyset has been modified via pyset.add(item_i) = item_i for i in + 1, 2, ..., n, and in that order. + """), + + # Way to build frozensets + + I(name='FROZENSET', + code='\x91', + arg=None, + stack_before=[markobject, stackslice], + stack_after=[pyfrozenset], + proto=4, + doc="""Build a frozenset out of the topmost slice, after markobject. + + All the stack entries following the topmost markobject are placed into + a single Python frozenset, which single frozenset object replaces all + of the stack from the topmost markobject onward. For example, + + Stack before: ... markobject 1 2 3 + Stack after: ... frozenset({1, 2, 3}) + """), + + # Stack manipulation. + + I(name='POP', + code='0', + arg=None, + stack_before=[anyobject], + stack_after=[], + proto=0, + doc="Discard the top stack item, shrinking the stack by one item."), + + I(name='DUP', + code='2', + arg=None, + stack_before=[anyobject], + stack_after=[anyobject, anyobject], + proto=0, + doc="Push the top stack item onto the stack again, duplicating it."), + + I(name='MARK', + code='(', + arg=None, + stack_before=[], + stack_after=[markobject], + proto=0, + doc="""Push markobject onto the stack. + + markobject is a unique object, used by other opcodes to identify a + region of the stack containing a variable number of objects for them + to work on. See markobject.doc for more detail. + """), + + I(name='POP_MARK', + code='1', + arg=None, + stack_before=[markobject, stackslice], + stack_after=[], + proto=1, + doc="""Pop all the stack objects at and above the topmost markobject. + + When an opcode using a variable number of stack objects is done, + POP_MARK is used to remove those objects, and to remove the markobject + that delimited their starting position on the stack. + """), + + # Memo manipulation. There are really only two operations (get and put), + # each in all-text, "short binary", and "long binary" flavors. + + I(name='GET', + code='g', + arg=decimalnl_short, + stack_before=[], + stack_after=[anyobject], + proto=0, + doc="""Read an object from the memo and push it on the stack. + + The index of the memo object to push is given by the newline-terminated + decimal string following. BINGET and LONG_BINGET are space-optimized + versions. + """), + + I(name='BINGET', + code='h', + arg=uint1, + stack_before=[], + stack_after=[anyobject], + proto=1, + doc="""Read an object from the memo and push it on the stack. + + The index of the memo object to push is given by the 1-byte unsigned + integer following. + """), + + I(name='LONG_BINGET', + code='j', + arg=uint4, + stack_before=[], + stack_after=[anyobject], + proto=1, + doc="""Read an object from the memo and push it on the stack. + + The index of the memo object to push is given by the 4-byte unsigned + little-endian integer following. + """), + + I(name='PUT', + code='p', + arg=decimalnl_short, + stack_before=[], + stack_after=[], + proto=0, + doc="""Store the stack top into the memo. The stack is not popped. + + The index of the memo location to write into is given by the newline- + terminated decimal string following. BINPUT and LONG_BINPUT are + space-optimized versions. + """), + + I(name='BINPUT', + code='q', + arg=uint1, + stack_before=[], + stack_after=[], + proto=1, + doc="""Store the stack top into the memo. The stack is not popped. + + The index of the memo location to write into is given by the 1-byte + unsigned integer following. + """), + + I(name='LONG_BINPUT', + code='r', + arg=uint4, + stack_before=[], + stack_after=[], + proto=1, + doc="""Store the stack top into the memo. The stack is not popped. + + The index of the memo location to write into is given by the 4-byte + unsigned little-endian integer following. + """), + + I(name='MEMOIZE', + code='\x94', + arg=None, + stack_before=[anyobject], + stack_after=[anyobject], + proto=4, + doc="""Store the stack top into the memo. The stack is not popped. + + The index of the memo location to write is the number of + elements currently present in the memo. + """), + + # Access the extension registry (predefined objects). Akin to the GET + # family. + + I(name='EXT1', + code='\x82', + arg=uint1, + stack_before=[], + stack_after=[anyobject], + proto=2, + doc="""Extension code. + + This code and the similar EXT2 and EXT4 allow using a registry + of popular objects that are pickled by name, typically classes. + It is envisioned that through a global negotiation and + registration process, third parties can set up a mapping between + ints and object names. + + In order to guarantee pickle interchangeability, the extension + code registry ought to be global, although a range of codes may + be reserved for private use. + + EXT1 has a 1-byte integer argument. This is used to index into the + extension registry, and the object at that index is pushed on the stack. + """), + + I(name='EXT2', + code='\x83', + arg=uint2, + stack_before=[], + stack_after=[anyobject], + proto=2, + doc="""Extension code. + + See EXT1. EXT2 has a two-byte integer argument. + """), + + I(name='EXT4', + code='\x84', + arg=int4, + stack_before=[], + stack_after=[anyobject], + proto=2, + doc="""Extension code. + + See EXT1. EXT4 has a four-byte integer argument. + """), + + # Push a class object, or module function, on the stack, via its module + # and name. + + I(name='GLOBAL', + code='c', + arg=stringnl_noescape_pair, + stack_before=[], + stack_after=[anyobject], + proto=0, + doc="""Push a global object (module.attr) on the stack. + + Two newline-terminated strings follow the GLOBAL opcode. The first is + taken as a module name, and the second as a class name. The class + object module.class is pushed on the stack. More accurately, the + object returned by self.find_class(module, class) is pushed on the + stack, so unpickling subclasses can override this form of lookup. + """), + + I(name='STACK_GLOBAL', + code='\x93', + arg=None, + stack_before=[pyunicode, pyunicode], + stack_after=[anyobject], + proto=4, + doc="""Push a global object (module.attr) on the stack. + """), + + # Ways to build objects of classes pickle doesn't know about directly + # (user-defined classes). I despair of documenting this accurately + # and comprehensibly -- you really have to read the pickle code to + # find all the special cases. + + I(name='REDUCE', + code='R', + arg=None, + stack_before=[anyobject, anyobject], + stack_after=[anyobject], + proto=0, + doc="""Push an object built from a callable and an argument tuple. + + The opcode is named to remind of the __reduce__() method. + + Stack before: ... callable pytuple + Stack after: ... callable(*pytuple) + + The callable and the argument tuple are the first two items returned + by a __reduce__ method. Applying the callable to the argtuple is + supposed to reproduce the original object, or at least get it started. + If the __reduce__ method returns a 3-tuple, the last component is an + argument to be passed to the object's __setstate__, and then the REDUCE + opcode is followed by code to create setstate's argument, and then a + BUILD opcode to apply __setstate__ to that argument. + + If not isinstance(callable, type), REDUCE complains unless the + callable has been registered with the copyreg module's + safe_constructors dict, or the callable has a magic + '__safe_for_unpickling__' attribute with a true value. I'm not sure + why it does this, but I've sure seen this complaint often enough when + I didn't want to . + """), + + I(name='BUILD', + code='b', + arg=None, + stack_before=[anyobject, anyobject], + stack_after=[anyobject], + proto=0, + doc="""Finish building an object, via __setstate__ or dict update. + + Stack before: ... anyobject argument + Stack after: ... anyobject + + where anyobject may have been mutated, as follows: + + If the object has a __setstate__ method, + + anyobject.__setstate__(argument) + + is called. + + Else the argument must be a dict, the object must have a __dict__, and + the object is updated via + + anyobject.__dict__.update(argument) + """), + + I(name='INST', + code='i', + arg=stringnl_noescape_pair, + stack_before=[markobject, stackslice], + stack_after=[anyobject], + proto=0, + doc="""Build a class instance. + + This is the protocol 0 version of protocol 1's OBJ opcode. + INST is followed by two newline-terminated strings, giving a + module and class name, just as for the GLOBAL opcode (and see + GLOBAL for more details about that). self.find_class(module, name) + is used to get a class object. + + In addition, all the objects on the stack following the topmost + markobject are gathered into a tuple and popped (along with the + topmost markobject), just as for the TUPLE opcode. + + Now it gets complicated. If all of these are true: + + + The argtuple is empty (markobject was at the top of the stack + at the start). + + + The class object does not have a __getinitargs__ attribute. + + then we want to create an old-style class instance without invoking + its __init__() method (pickle has waffled on this over the years; not + calling __init__() is current wisdom). In this case, an instance of + an old-style dummy class is created, and then we try to rebind its + __class__ attribute to the desired class object. If this succeeds, + the new instance object is pushed on the stack, and we're done. + + Else (the argtuple is not empty, it's not an old-style class object, + or the class object does have a __getinitargs__ attribute), the code + first insists that the class object have a __safe_for_unpickling__ + attribute. Unlike as for the __safe_for_unpickling__ check in REDUCE, + it doesn't matter whether this attribute has a true or false value, it + only matters whether it exists (XXX this is a bug). If + __safe_for_unpickling__ doesn't exist, UnpicklingError is raised. + + Else (the class object does have a __safe_for_unpickling__ attr), + the class object obtained from INST's arguments is applied to the + argtuple obtained from the stack, and the resulting instance object + is pushed on the stack. + + NOTE: checks for __safe_for_unpickling__ went away in Python 2.3. + NOTE: the distinction between old-style and new-style classes does + not make sense in Python 3. + """), + + I(name='OBJ', + code='o', + arg=None, + stack_before=[markobject, anyobject, stackslice], + stack_after=[anyobject], + proto=1, + doc="""Build a class instance. + + This is the protocol 1 version of protocol 0's INST opcode, and is + very much like it. The major difference is that the class object + is taken off the stack, allowing it to be retrieved from the memo + repeatedly if several instances of the same class are created. This + can be much more efficient (in both time and space) than repeatedly + embedding the module and class names in INST opcodes. + + Unlike INST, OBJ takes no arguments from the opcode stream. Instead + the class object is taken off the stack, immediately above the + topmost markobject: + + Stack before: ... markobject classobject stackslice + Stack after: ... new_instance_object + + As for INST, the remainder of the stack above the markobject is + gathered into an argument tuple, and then the logic seems identical, + except that no __safe_for_unpickling__ check is done (XXX this is + a bug). See INST for the gory details. + + NOTE: In Python 2.3, INST and OBJ are identical except for how they + get the class object. That was always the intent; the implementations + had diverged for accidental reasons. + """), + + I(name='NEWOBJ', + code='\x81', + arg=None, + stack_before=[anyobject, anyobject], + stack_after=[anyobject], + proto=2, + doc="""Build an object instance. + + The stack before should be thought of as containing a class + object followed by an argument tuple (the tuple being the stack + top). Call these cls and args. They are popped off the stack, + and the value returned by cls.__new__(cls, *args) is pushed back + onto the stack. + """), + + I(name='NEWOBJ_EX', + code='\x92', + arg=None, + stack_before=[anyobject, anyobject, anyobject], + stack_after=[anyobject], + proto=4, + doc="""Build an object instance. + + The stack before should be thought of as containing a class + object followed by an argument tuple and by a keyword argument dict + (the dict being the stack top). Call these cls and args. They are + popped off the stack, and the value returned by + cls.__new__(cls, *args, *kwargs) is pushed back onto the stack. + """), + + # Machine control. + + I(name='PROTO', + code='\x80', + arg=uint1, + stack_before=[], + stack_after=[], + proto=2, + doc="""Protocol version indicator. + + For protocol 2 and above, a pickle must start with this opcode. + The argument is the protocol version, an int in range(2, 256). + """), + + I(name='STOP', + code='.', + arg=None, + stack_before=[anyobject], + stack_after=[], + proto=0, + doc="""Stop the unpickling machine. + + Every pickle ends with this opcode. The object at the top of the stack + is popped, and that's the result of unpickling. The stack should be + empty then. + """), + + # Framing support. + + I(name='FRAME', + code='\x95', + arg=uint8, + stack_before=[], + stack_after=[], + proto=4, + doc="""Indicate the beginning of a new frame. + + The unpickler may use this opcode to safely prefetch data from its + underlying stream. + """), + + # Ways to deal with persistent IDs. + + I(name='PERSID', + code='P', + arg=stringnl_noescape, + stack_before=[], + stack_after=[anyobject], + proto=0, + doc="""Push an object identified by a persistent ID. + + The pickle module doesn't define what a persistent ID means. PERSID's + argument is a newline-terminated str-style (no embedded escapes, no + bracketing quote characters) string, which *is* "the persistent ID". + The unpickler passes this string to self.persistent_load(). Whatever + object that returns is pushed on the stack. There is no implementation + of persistent_load() in Python's unpickler: it must be supplied by an + unpickler subclass. + """), + + I(name='BINPERSID', + code='Q', + arg=None, + stack_before=[anyobject], + stack_after=[anyobject], + proto=1, + doc="""Push an object identified by a persistent ID. + + Like PERSID, except the persistent ID is popped off the stack (instead + of being a string embedded in the opcode bytestream). The persistent + ID is passed to self.persistent_load(), and whatever object that + returns is pushed on the stack. See PERSID for more detail. + """), +] +del I + +# Verify uniqueness of .name and .code members. +name2i = {} +code2i = {} + +for i, d in enumerate(opcodes): + if d.name in name2i: + raise ValueError("repeated name %r at indices %d and %d" % + (d.name, name2i[d.name], i)) + if d.code in code2i: + raise ValueError("repeated code %r at indices %d and %d" % + (d.code, code2i[d.code], i)) + + name2i[d.name] = i + code2i[d.code] = i + +del name2i, code2i, i, d + +############################################################################## +# Build a code2op dict, mapping opcode characters to OpcodeInfo records. +# Also ensure we've got the same stuff as pickle.py, although the +# introspection here is dicey. + +code2op = {} +for d in opcodes: + code2op[d.code] = d +del d + +def assure_pickle_consistency(verbose=False): + + copy = code2op.copy() + for name in pickle.__all__: + if not re.match("[A-Z][A-Z0-9_]+$", name): + if verbose: + print("skipping %r: it doesn't look like an opcode name" % name) + continue + picklecode = getattr(pickle, name) + if not isinstance(picklecode, bytes) or len(picklecode) != 1: + if verbose: + print(("skipping %r: value %r doesn't look like a pickle " + "code" % (name, picklecode))) + continue + picklecode = picklecode.decode("latin-1") + if picklecode in copy: + if verbose: + print("checking name %r w/ code %r for consistency" % ( + name, picklecode)) + d = copy[picklecode] + if d.name != name: + raise ValueError("for pickle code %r, pickle.py uses name %r " + "but we're using name %r" % (picklecode, + name, + d.name)) + # Forget this one. Any left over in copy at the end are a problem + # of a different kind. + del copy[picklecode] + else: + raise ValueError("pickle.py appears to have a pickle opcode with " + "name %r and code %r, but we don't" % + (name, picklecode)) + if copy: + msg = ["we appear to have pickle opcodes that pickle.py doesn't have:"] + for code, d in copy.items(): + msg.append(" name %r with code %r" % (d.name, code)) + raise ValueError("\n".join(msg)) + +assure_pickle_consistency() +del assure_pickle_consistency + +############################################################################## +# A pickle opcode generator. + +def _genops(data, yield_end_pos=False): + if isinstance(data, bytes_types): + data = io.BytesIO(data) + + if hasattr(data, "tell"): + getpos = data.tell + else: + getpos = lambda: None + + while True: + pos = getpos() + code = data.read(1) + opcode = code2op.get(code.decode("latin-1")) + if opcode is None: + if code == b"": + raise ValueError("pickle exhausted before seeing STOP") + else: + raise ValueError("at position %s, opcode %r unknown" % ( + "" if pos is None else pos, + code)) + if opcode.arg is None: + arg = None + else: + arg = opcode.arg.reader(data) + if yield_end_pos: + yield opcode, arg, pos, getpos() + else: + yield opcode, arg, pos + if code == b'.': + assert opcode.name == 'STOP' + break + +def genops(pickle): + """Generate all the opcodes in a pickle. + + 'pickle' is a file-like object, or string, containing the pickle. + + Each opcode in the pickle is generated, from the current pickle position, + stopping after a STOP opcode is delivered. A triple is generated for + each opcode: + + opcode, arg, pos + + opcode is an OpcodeInfo record, describing the current opcode. + + If the opcode has an argument embedded in the pickle, arg is its decoded + value, as a Python object. If the opcode doesn't have an argument, arg + is None. + + If the pickle has a tell() method, pos was the value of pickle.tell() + before reading the current opcode. If the pickle is a bytes object, + it's wrapped in a BytesIO object, and the latter's tell() result is + used. Else (the pickle doesn't have a tell(), and it's not obvious how + to query its current position) pos is None. + """ + return _genops(pickle) + +############################################################################## +# A pickle optimizer. + +def optimize(p): + 'Optimize a pickle string by removing unused PUT opcodes' + put = 'PUT' + get = 'GET' + oldids = set() # set of all PUT ids + newids = {} # set of ids used by a GET opcode + opcodes = [] # (op, idx) or (pos, end_pos) + proto = 0 + protoheader = b'' + for opcode, arg, pos, end_pos in _genops(p, yield_end_pos=True): + if 'PUT' in opcode.name: + oldids.add(arg) + opcodes.append((put, arg)) + elif opcode.name == 'MEMOIZE': + idx = len(oldids) + oldids.add(idx) + opcodes.append((put, idx)) + elif 'FRAME' in opcode.name: + pass + elif 'GET' in opcode.name: + if opcode.proto > proto: + proto = opcode.proto + newids[arg] = None + opcodes.append((get, arg)) + elif opcode.name == 'PROTO': + if arg > proto: + proto = arg + if pos == 0: + protoheader = p[pos:end_pos] + else: + opcodes.append((pos, end_pos)) + else: + opcodes.append((pos, end_pos)) + del oldids + + # Copy the opcodes except for PUTS without a corresponding GET + out = io.BytesIO() + # Write the PROTO header before any framing + out.write(protoheader) + pickler = pickle._Pickler(out, proto) + if proto >= 4: + pickler.framer.start_framing() + idx = 0 + for op, arg in opcodes: + frameless = False + if op is put: + if arg not in newids: + continue + data = pickler.put(idx) + newids[arg] = idx + idx += 1 + elif op is get: + data = pickler.get(newids[arg]) + else: + data = p[op:arg] + frameless = len(data) > pickler.framer._FRAME_SIZE_TARGET + pickler.framer.commit_frame(force=frameless) + if frameless: + pickler.framer.file_write(data) + else: + pickler.write(data) + pickler.framer.end_framing() + return out.getvalue() + +############################################################################## +# A symbolic pickle disassembler. + +def dis(pickle, out=None, memo=None, indentlevel=4, annotate=0): + """Produce a symbolic disassembly of a pickle. + + 'pickle' is a file-like object, or string, containing a (at least one) + pickle. The pickle is disassembled from the current position, through + the first STOP opcode encountered. + + Optional arg 'out' is a file-like object to which the disassembly is + printed. It defaults to sys.stdout. + + Optional arg 'memo' is a Python dict, used as the pickle's memo. It + may be mutated by dis(), if the pickle contains PUT or BINPUT opcodes. + Passing the same memo object to another dis() call then allows disassembly + to proceed across multiple pickles that were all created by the same + pickler with the same memo. Ordinarily you don't need to worry about this. + + Optional arg 'indentlevel' is the number of blanks by which to indent + a new MARK level. It defaults to 4. + + Optional arg 'annotate' if nonzero instructs dis() to add short + description of the opcode on each line of disassembled output. + The value given to 'annotate' must be an integer and is used as a + hint for the column where annotation should start. The default + value is 0, meaning no annotations. + + In addition to printing the disassembly, some sanity checks are made: + + + All embedded opcode arguments "make sense". + + + Explicit and implicit pop operations have enough items on the stack. + + + When an opcode implicitly refers to a markobject, a markobject is + actually on the stack. + + + A memo entry isn't referenced before it's defined. + + + The markobject isn't stored in the memo. + + + A memo entry isn't redefined. + """ + + # Most of the hair here is for sanity checks, but most of it is needed + # anyway to detect when a protocol 0 POP takes a MARK off the stack + # (which in turn is needed to indent MARK blocks correctly). + + stack = [] # crude emulation of unpickler stack + if memo is None: + memo = {} # crude emulation of unpickler memo + maxproto = -1 # max protocol number seen + markstack = [] # bytecode positions of MARK opcodes + indentchunk = ' ' * indentlevel + errormsg = None + annocol = annotate # column hint for annotations + for opcode, arg, pos in genops(pickle): + if pos is not None: + print("%5d:" % pos, end=' ', file=out) + + line = "%-4s %s%s" % (repr(opcode.code)[1:-1], + indentchunk * len(markstack), + opcode.name) + + maxproto = max(maxproto, opcode.proto) + before = opcode.stack_before # don't mutate + after = opcode.stack_after # don't mutate + numtopop = len(before) + + # See whether a MARK should be popped. + markmsg = None + if markobject in before or (opcode.name == "POP" and + stack and + stack[-1] is markobject): + assert markobject not in after + if __debug__: + if markobject in before: + assert before[-1] is stackslice + if markstack: + markpos = markstack.pop() + if markpos is None: + markmsg = "(MARK at unknown opcode offset)" + else: + markmsg = "(MARK at %d)" % markpos + # Pop everything at and after the topmost markobject. + while stack[-1] is not markobject: + stack.pop() + stack.pop() + # Stop later code from popping too much. + try: + numtopop = before.index(markobject) + except ValueError: + assert opcode.name == "POP" + numtopop = 0 + else: + errormsg = markmsg = "no MARK exists on stack" + + # Check for correct memo usage. + if opcode.name in ("PUT", "BINPUT", "LONG_BINPUT", "MEMOIZE"): + if opcode.name == "MEMOIZE": + memo_idx = len(memo) + markmsg = "(as %d)" % memo_idx + else: + assert arg is not None + memo_idx = arg + if memo_idx in memo: + errormsg = "memo key %r already defined" % arg + elif not stack: + errormsg = "stack is empty -- can't store into memo" + elif stack[-1] is markobject: + errormsg = "can't store markobject in the memo" + else: + memo[memo_idx] = stack[-1] + elif opcode.name in ("GET", "BINGET", "LONG_BINGET"): + if arg in memo: + assert len(after) == 1 + after = [memo[arg]] # for better stack emulation + else: + errormsg = "memo key %r has never been stored into" % arg + + if arg is not None or markmsg: + # make a mild effort to align arguments + line += ' ' * (10 - len(opcode.name)) + if arg is not None: + line += ' ' + repr(arg) + if markmsg: + line += ' ' + markmsg + if annotate: + line += ' ' * (annocol - len(line)) + # make a mild effort to align annotations + annocol = len(line) + if annocol > 50: + annocol = annotate + line += ' ' + opcode.doc.split('\n', 1)[0] + print(line, file=out) + + if errormsg: + # Note that we delayed complaining until the offending opcode + # was printed. + raise ValueError(errormsg) + + # Emulate the stack effects. + if len(stack) < numtopop: + raise ValueError("tries to pop %d items from stack with " + "only %d items" % (numtopop, len(stack))) + if numtopop: + del stack[-numtopop:] + if markobject in after: + assert markobject not in before + markstack.append(pos) + + stack.extend(after) + + print("highest protocol among opcodes =", maxproto, file=out) + if stack: + raise ValueError("stack not empty after STOP: %r" % stack) + +# For use in the doctest, simply as an example of a class to pickle. +class _Example: + def __init__(self, value): + self.value = value + +_dis_test = r""" +>>> import pickle +>>> x = [1, 2, (3, 4), {b'abc': "def"}] +>>> pkl0 = pickle.dumps(x, 0) +>>> dis(pkl0) + 0: ( MARK + 1: l LIST (MARK at 0) + 2: p PUT 0 + 5: I INT 1 + 8: a APPEND + 9: I INT 2 + 12: a APPEND + 13: ( MARK + 14: I INT 3 + 17: I INT 4 + 20: t TUPLE (MARK at 13) + 21: p PUT 1 + 24: a APPEND + 25: ( MARK + 26: d DICT (MARK at 25) + 27: p PUT 2 + 30: c GLOBAL '_codecs encode' + 46: p PUT 3 + 49: ( MARK + 50: V UNICODE 'abc' + 55: p PUT 4 + 58: V UNICODE 'latin1' + 66: p PUT 5 + 69: t TUPLE (MARK at 49) + 70: p PUT 6 + 73: R REDUCE + 74: p PUT 7 + 77: V UNICODE 'def' + 82: p PUT 8 + 85: s SETITEM + 86: a APPEND + 87: . STOP +highest protocol among opcodes = 0 + +Try again with a "binary" pickle. + +>>> pkl1 = pickle.dumps(x, 1) +>>> dis(pkl1) + 0: ] EMPTY_LIST + 1: q BINPUT 0 + 3: ( MARK + 4: K BININT1 1 + 6: K BININT1 2 + 8: ( MARK + 9: K BININT1 3 + 11: K BININT1 4 + 13: t TUPLE (MARK at 8) + 14: q BINPUT 1 + 16: } EMPTY_DICT + 17: q BINPUT 2 + 19: c GLOBAL '_codecs encode' + 35: q BINPUT 3 + 37: ( MARK + 38: X BINUNICODE 'abc' + 46: q BINPUT 4 + 48: X BINUNICODE 'latin1' + 59: q BINPUT 5 + 61: t TUPLE (MARK at 37) + 62: q BINPUT 6 + 64: R REDUCE + 65: q BINPUT 7 + 67: X BINUNICODE 'def' + 75: q BINPUT 8 + 77: s SETITEM + 78: e APPENDS (MARK at 3) + 79: . STOP +highest protocol among opcodes = 1 + +Exercise the INST/OBJ/BUILD family. + +>>> import pickletools +>>> dis(pickle.dumps(pickletools.dis, 0)) + 0: c GLOBAL 'pickletools dis' + 17: p PUT 0 + 20: . STOP +highest protocol among opcodes = 0 + +>>> from pickletools import _Example +>>> x = [_Example(42)] * 2 +>>> dis(pickle.dumps(x, 0)) + 0: ( MARK + 1: l LIST (MARK at 0) + 2: p PUT 0 + 5: c GLOBAL 'copy_reg _reconstructor' + 30: p PUT 1 + 33: ( MARK + 34: c GLOBAL 'pickletools _Example' + 56: p PUT 2 + 59: c GLOBAL '__builtin__ object' + 79: p PUT 3 + 82: N NONE + 83: t TUPLE (MARK at 33) + 84: p PUT 4 + 87: R REDUCE + 88: p PUT 5 + 91: ( MARK + 92: d DICT (MARK at 91) + 93: p PUT 6 + 96: V UNICODE 'value' + 103: p PUT 7 + 106: I INT 42 + 110: s SETITEM + 111: b BUILD + 112: a APPEND + 113: g GET 5 + 116: a APPEND + 117: . STOP +highest protocol among opcodes = 0 + +>>> dis(pickle.dumps(x, 1)) + 0: ] EMPTY_LIST + 1: q BINPUT 0 + 3: ( MARK + 4: c GLOBAL 'copy_reg _reconstructor' + 29: q BINPUT 1 + 31: ( MARK + 32: c GLOBAL 'pickletools _Example' + 54: q BINPUT 2 + 56: c GLOBAL '__builtin__ object' + 76: q BINPUT 3 + 78: N NONE + 79: t TUPLE (MARK at 31) + 80: q BINPUT 4 + 82: R REDUCE + 83: q BINPUT 5 + 85: } EMPTY_DICT + 86: q BINPUT 6 + 88: X BINUNICODE 'value' + 98: q BINPUT 7 + 100: K BININT1 42 + 102: s SETITEM + 103: b BUILD + 104: h BINGET 5 + 106: e APPENDS (MARK at 3) + 107: . STOP +highest protocol among opcodes = 1 + +Try "the canonical" recursive-object test. + +>>> L = [] +>>> T = L, +>>> L.append(T) +>>> L[0] is T +True +>>> T[0] is L +True +>>> L[0][0] is L +True +>>> T[0][0] is T +True +>>> dis(pickle.dumps(L, 0)) + 0: ( MARK + 1: l LIST (MARK at 0) + 2: p PUT 0 + 5: ( MARK + 6: g GET 0 + 9: t TUPLE (MARK at 5) + 10: p PUT 1 + 13: a APPEND + 14: . STOP +highest protocol among opcodes = 0 + +>>> dis(pickle.dumps(L, 1)) + 0: ] EMPTY_LIST + 1: q BINPUT 0 + 3: ( MARK + 4: h BINGET 0 + 6: t TUPLE (MARK at 3) + 7: q BINPUT 1 + 9: a APPEND + 10: . STOP +highest protocol among opcodes = 1 + +Note that, in the protocol 0 pickle of the recursive tuple, the disassembler +has to emulate the stack in order to realize that the POP opcode at 16 gets +rid of the MARK at 0. + +>>> dis(pickle.dumps(T, 0)) + 0: ( MARK + 1: ( MARK + 2: l LIST (MARK at 1) + 3: p PUT 0 + 6: ( MARK + 7: g GET 0 + 10: t TUPLE (MARK at 6) + 11: p PUT 1 + 14: a APPEND + 15: 0 POP + 16: 0 POP (MARK at 0) + 17: g GET 1 + 20: . STOP +highest protocol among opcodes = 0 + +>>> dis(pickle.dumps(T, 1)) + 0: ( MARK + 1: ] EMPTY_LIST + 2: q BINPUT 0 + 4: ( MARK + 5: h BINGET 0 + 7: t TUPLE (MARK at 4) + 8: q BINPUT 1 + 10: a APPEND + 11: 1 POP_MARK (MARK at 0) + 12: h BINGET 1 + 14: . STOP +highest protocol among opcodes = 1 + +Try protocol 2. + +>>> dis(pickle.dumps(L, 2)) + 0: \x80 PROTO 2 + 2: ] EMPTY_LIST + 3: q BINPUT 0 + 5: h BINGET 0 + 7: \x85 TUPLE1 + 8: q BINPUT 1 + 10: a APPEND + 11: . STOP +highest protocol among opcodes = 2 + +>>> dis(pickle.dumps(T, 2)) + 0: \x80 PROTO 2 + 2: ] EMPTY_LIST + 3: q BINPUT 0 + 5: h BINGET 0 + 7: \x85 TUPLE1 + 8: q BINPUT 1 + 10: a APPEND + 11: 0 POP + 12: h BINGET 1 + 14: . STOP +highest protocol among opcodes = 2 + +Try protocol 3 with annotations: + +>>> dis(pickle.dumps(T, 3), annotate=1) + 0: \x80 PROTO 3 Protocol version indicator. + 2: ] EMPTY_LIST Push an empty list. + 3: q BINPUT 0 Store the stack top into the memo. The stack is not popped. + 5: h BINGET 0 Read an object from the memo and push it on the stack. + 7: \x85 TUPLE1 Build a one-tuple out of the topmost item on the stack. + 8: q BINPUT 1 Store the stack top into the memo. The stack is not popped. + 10: a APPEND Append an object to a list. + 11: 0 POP Discard the top stack item, shrinking the stack by one item. + 12: h BINGET 1 Read an object from the memo and push it on the stack. + 14: . STOP Stop the unpickling machine. +highest protocol among opcodes = 2 + +""" + +_memo_test = r""" +>>> import pickle +>>> import io +>>> f = io.BytesIO() +>>> p = pickle.Pickler(f, 2) +>>> x = [1, 2, 3] +>>> p.dump(x) +>>> p.dump(x) +>>> f.seek(0) +0 +>>> memo = {} +>>> dis(f, memo=memo) + 0: \x80 PROTO 2 + 2: ] EMPTY_LIST + 3: q BINPUT 0 + 5: ( MARK + 6: K BININT1 1 + 8: K BININT1 2 + 10: K BININT1 3 + 12: e APPENDS (MARK at 5) + 13: . STOP +highest protocol among opcodes = 2 +>>> dis(f, memo=memo) + 14: \x80 PROTO 2 + 16: h BINGET 0 + 18: . STOP +highest protocol among opcodes = 2 +""" + +__test__ = {'disassembler_test': _dis_test, + 'disassembler_memo_test': _memo_test, + } + +def _test(): + import doctest + return doctest.testmod() + +if __name__ == "__main__": + import argparse + parser = argparse.ArgumentParser( + description='disassemble one or more pickle files') + parser.add_argument( + 'pickle_file', type=argparse.FileType('br'), + nargs='*', help='the pickle file') + parser.add_argument( + '-o', '--output', default=sys.stdout, type=argparse.FileType('w'), + help='the file where the output should be written') + parser.add_argument( + '-m', '--memo', action='store_true', + help='preserve memo between disassemblies') + parser.add_argument( + '-l', '--indentlevel', default=4, type=int, + help='the number of blanks by which to indent a new MARK level') + parser.add_argument( + '-a', '--annotate', action='store_true', + help='annotate each line with a short opcode description') + parser.add_argument( + '-p', '--preamble', default="==> {name} <==", + help='if more than one pickle file is specified, print this before' + ' each disassembly') + parser.add_argument( + '-t', '--test', action='store_true', + help='run self-test suite') + parser.add_argument( + '-v', action='store_true', + help='run verbosely; only affects self-test run') + args = parser.parse_args() + if args.test: + _test() + else: + annotate = 30 if args.annotate else 0 + if not args.pickle_file: + parser.print_help() + elif len(args.pickle_file) == 1: + dis(args.pickle_file[0], args.output, None, + args.indentlevel, annotate) + else: + memo = {} if args.memo else None + for f in args.pickle_file: + preamble = args.preamble.format(name=f.name) + args.output.write(preamble + '\n') + dis(f, args.output, memo, args.indentlevel, annotate) diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/platform.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/platform.py new file mode 100644 index 0000000000000000000000000000000000000000..8e32b2a45e8a575d1c64fd553803fe05847885eb --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/platform.py @@ -0,0 +1,1245 @@ +#!/usr/bin/env python3 + +""" This module tries to retrieve as much platform-identifying data as + possible. It makes this information available via function APIs. + + If called from the command line, it prints the platform + information concatenated as single string to stdout. The output + format is useable as part of a filename. + +""" +# This module is maintained by Marc-Andre Lemburg . +# If you find problems, please submit bug reports/patches via the +# Python bug tracker (http://bugs.python.org) and assign them to "lemburg". +# +# Still needed: +# * support for MS-DOS (PythonDX ?) +# * support for Amiga and other still unsupported platforms running Python +# * support for additional Linux distributions +# +# Many thanks to all those who helped adding platform-specific +# checks (in no particular order): +# +# Charles G Waldman, David Arnold, Gordon McMillan, Ben Darnell, +# Jeff Bauer, Cliff Crawford, Ivan Van Laningham, Josef +# Betancourt, Randall Hopper, Karl Putland, John Farrell, Greg +# Andruk, Just van Rossum, Thomas Heller, Mark R. Levinson, Mark +# Hammond, Bill Tutt, Hans Nowak, Uwe Zessin (OpenVMS support), +# Colin Kong, Trent Mick, Guido van Rossum, Anthony Baxter, Steve +# Dower +# +# History: +# +# +# +# 1.0.8 - changed Windows support to read version from kernel32.dll +# 1.0.7 - added DEV_NULL +# 1.0.6 - added linux_distribution() +# 1.0.5 - fixed Java support to allow running the module on Jython +# 1.0.4 - added IronPython support +# 1.0.3 - added normalization of Windows system name +# 1.0.2 - added more Windows support +# 1.0.1 - reformatted to make doc.py happy +# 1.0.0 - reformatted a bit and checked into Python CVS +# 0.8.0 - added sys.version parser and various new access +# APIs (python_version(), python_compiler(), etc.) +# 0.7.2 - fixed architecture() to use sizeof(pointer) where available +# 0.7.1 - added support for Caldera OpenLinux +# 0.7.0 - some fixes for WinCE; untabified the source file +# 0.6.2 - support for OpenVMS - requires version 1.5.2-V006 or higher and +# vms_lib.getsyi() configured +# 0.6.1 - added code to prevent 'uname -p' on platforms which are +# known not to support it +# 0.6.0 - fixed win32_ver() to hopefully work on Win95,98,NT and Win2k; +# did some cleanup of the interfaces - some APIs have changed +# 0.5.5 - fixed another type in the MacOS code... should have +# used more coffee today ;-) +# 0.5.4 - fixed a few typos in the MacOS code +# 0.5.3 - added experimental MacOS support; added better popen() +# workarounds in _syscmd_ver() -- still not 100% elegant +# though +# 0.5.2 - fixed uname() to return '' instead of 'unknown' in all +# return values (the system uname command tends to return +# 'unknown' instead of just leaving the field empty) +# 0.5.1 - included code for slackware dist; added exception handlers +# to cover up situations where platforms don't have os.popen +# (e.g. Mac) or fail on socket.gethostname(); fixed libc +# detection RE +# 0.5.0 - changed the API names referring to system commands to *syscmd*; +# added java_ver(); made syscmd_ver() a private +# API (was system_ver() in previous versions) -- use uname() +# instead; extended the win32_ver() to also return processor +# type information +# 0.4.0 - added win32_ver() and modified the platform() output for WinXX +# 0.3.4 - fixed a bug in _follow_symlinks() +# 0.3.3 - fixed popen() and "file" command invocation bugs +# 0.3.2 - added architecture() API and support for it in platform() +# 0.3.1 - fixed syscmd_ver() RE to support Windows NT +# 0.3.0 - added system alias support +# 0.2.3 - removed 'wince' again... oh well. +# 0.2.2 - added 'wince' to syscmd_ver() supported platforms +# 0.2.1 - added cache logic and changed the platform string format +# 0.2.0 - changed the API to use functions instead of module globals +# since some action take too long to be run on module import +# 0.1.0 - first release +# +# You can always get the latest version of this module at: +# +# http://www.egenix.com/files/python/platform.py +# +# If that URL should fail, try contacting the author. + +__copyright__ = """ + Copyright (c) 1999-2000, Marc-Andre Lemburg; mailto:mal@lemburg.com + Copyright (c) 2000-2010, eGenix.com Software GmbH; mailto:info@egenix.com + + Permission to use, copy, modify, and distribute this software and its + documentation for any purpose and without fee or royalty is hereby granted, + provided that the above copyright notice appear in all copies and that + both that copyright notice and this permission notice appear in + supporting documentation or portions thereof, including modifications, + that you make. + + EGENIX.COM SOFTWARE GMBH DISCLAIMS ALL WARRANTIES WITH REGARD TO + THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, + INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING + FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + WITH THE USE OR PERFORMANCE OF THIS SOFTWARE ! + +""" + +__version__ = '1.0.8' + +import collections +import os +import re +import sys + +### Globals & Constants + +# Helper for comparing two version number strings. +# Based on the description of the PHP's version_compare(): +# http://php.net/manual/en/function.version-compare.php + +_ver_stages = { + # any string not found in this dict, will get 0 assigned + 'dev': 10, + 'alpha': 20, 'a': 20, + 'beta': 30, 'b': 30, + 'c': 40, + 'RC': 50, 'rc': 50, + # number, will get 100 assigned + 'pl': 200, 'p': 200, +} + +_component_re = re.compile(r'([0-9]+|[._+-])') + +def _comparable_version(version): + result = [] + for v in _component_re.split(version): + if v not in '._+-': + try: + v = int(v, 10) + t = 100 + except ValueError: + t = _ver_stages.get(v, 0) + result.extend((t, v)) + return result + +### Platform specific APIs + +_libc_search = re.compile(b'(__libc_init)' + b'|' + b'(GLIBC_([0-9.]+))' + b'|' + br'(libc(_\w+)?\.so(?:\.(\d[0-9.]*))?)', re.ASCII) + +def libc_ver(executable=None, lib='', version='', chunksize=16384): + + """ Tries to determine the libc version that the file executable + (which defaults to the Python interpreter) is linked against. + + Returns a tuple of strings (lib,version) which default to the + given parameters in case the lookup fails. + + Note that the function has intimate knowledge of how different + libc versions add symbols to the executable and thus is probably + only useable for executables compiled using gcc. + + The file is read and scanned in chunks of chunksize bytes. + + """ + if executable is None: + try: + ver = os.confstr('CS_GNU_LIBC_VERSION') + # parse 'glibc 2.28' as ('glibc', '2.28') + parts = ver.split(maxsplit=1) + if len(parts) == 2: + return tuple(parts) + except (AttributeError, ValueError, OSError): + # os.confstr() or CS_GNU_LIBC_VERSION value not available + pass + + executable = sys.executable + + V = _comparable_version + if hasattr(os.path, 'realpath'): + # Python 2.2 introduced os.path.realpath(); it is used + # here to work around problems with Cygwin not being + # able to open symlinks for reading + executable = os.path.realpath(executable) + with open(executable, 'rb') as f: + binary = f.read(chunksize) + pos = 0 + while pos < len(binary): + if b'libc' in binary or b'GLIBC' in binary: + m = _libc_search.search(binary, pos) + else: + m = None + if not m or m.end() == len(binary): + chunk = f.read(chunksize) + if chunk: + binary = binary[max(pos, len(binary) - 1000):] + chunk + pos = 0 + continue + if not m: + break + libcinit, glibc, glibcversion, so, threads, soversion = [ + s.decode('latin1') if s is not None else s + for s in m.groups()] + if libcinit and not lib: + lib = 'libc' + elif glibc: + if lib != 'glibc': + lib = 'glibc' + version = glibcversion + elif V(glibcversion) > V(version): + version = glibcversion + elif so: + if lib != 'glibc': + lib = 'libc' + if soversion and (not version or V(soversion) > V(version)): + version = soversion + if threads and version[-len(threads):] != threads: + version = version + threads + pos = m.end() + return lib, version + +def _norm_version(version, build=''): + + """ Normalize the version and build strings and return a single + version string using the format major.minor.build (or patchlevel). + """ + l = version.split('.') + if build: + l.append(build) + try: + strings = list(map(str, map(int, l))) + except ValueError: + strings = l + version = '.'.join(strings[:3]) + return version + +_ver_output = re.compile(r'(?:([\w ]+) ([\w.]+) ' + r'.*' + r'\[.* ([\d.]+)\])') + +# Examples of VER command output: +# +# Windows 2000: Microsoft Windows 2000 [Version 5.00.2195] +# Windows XP: Microsoft Windows XP [Version 5.1.2600] +# Windows Vista: Microsoft Windows [Version 6.0.6002] +# +# Note that the "Version" string gets localized on different +# Windows versions. + +def _syscmd_ver(system='', release='', version='', + + supported_platforms=('win32', 'win16', 'dos')): + + """ Tries to figure out the OS version used and returns + a tuple (system, release, version). + + It uses the "ver" shell command for this which is known + to exists on Windows, DOS. XXX Others too ? + + In case this fails, the given parameters are used as + defaults. + + """ + if sys.platform not in supported_platforms: + return system, release, version + + # Try some common cmd strings + import subprocess + for cmd in ('ver', 'command /c ver', 'cmd /c ver'): + try: + info = subprocess.check_output(cmd, + stderr=subprocess.DEVNULL, + text=True, + shell=True) + except (OSError, subprocess.CalledProcessError) as why: + #print('Command %s failed: %s' % (cmd, why)) + continue + else: + break + else: + return system, release, version + + # Parse the output + info = info.strip() + m = _ver_output.match(info) + if m is not None: + system, release, version = m.groups() + # Strip trailing dots from version and release + if release[-1] == '.': + release = release[:-1] + if version[-1] == '.': + version = version[:-1] + # Normalize the version and build strings (eliminating additional + # zeros) + version = _norm_version(version) + return system, release, version + +_WIN32_CLIENT_RELEASES = { + (5, 0): "2000", + (5, 1): "XP", + # Strictly, 5.2 client is XP 64-bit, but platform.py historically + # has always called it 2003 Server + (5, 2): "2003Server", + (5, None): "post2003", + + (6, 0): "Vista", + (6, 1): "7", + (6, 2): "8", + (6, 3): "8.1", + (6, None): "post8.1", + + (10, 0): "10", + (10, None): "post10", +} + +# Server release name lookup will default to client names if necessary +_WIN32_SERVER_RELEASES = { + (5, 2): "2003Server", + + (6, 0): "2008Server", + (6, 1): "2008ServerR2", + (6, 2): "2012Server", + (6, 3): "2012ServerR2", + (6, None): "post2012ServerR2", +} + +def win32_is_iot(): + return win32_edition() in ('IoTUAP', 'NanoServer', 'WindowsCoreHeadless', 'IoTEdgeOS') + +def win32_edition(): + try: + try: + import winreg + except ImportError: + import _winreg as winreg + except ImportError: + pass + else: + try: + cvkey = r'SOFTWARE\Microsoft\Windows NT\CurrentVersion' + with winreg.OpenKeyEx(winreg.HKEY_LOCAL_MACHINE, cvkey) as key: + return winreg.QueryValueEx(key, 'EditionId')[0] + except OSError: + pass + + return None + +def win32_ver(release='', version='', csd='', ptype=''): + try: + from sys import getwindowsversion + except ImportError: + return release, version, csd, ptype + + winver = getwindowsversion() + try: + major, minor, build = map(int, _syscmd_ver()[2].split('.')) + except ValueError: + major, minor, build = winver.platform_version or winver[:3] + version = '{0}.{1}.{2}'.format(major, minor, build) + + release = (_WIN32_CLIENT_RELEASES.get((major, minor)) or + _WIN32_CLIENT_RELEASES.get((major, None)) or + release) + + # getwindowsversion() reflect the compatibility mode Python is + # running under, and so the service pack value is only going to be + # valid if the versions match. + if winver[:2] == (major, minor): + try: + csd = 'SP{}'.format(winver.service_pack_major) + except AttributeError: + if csd[:13] == 'Service Pack ': + csd = 'SP' + csd[13:] + + # VER_NT_SERVER = 3 + if getattr(winver, 'product_type', None) == 3: + release = (_WIN32_SERVER_RELEASES.get((major, minor)) or + _WIN32_SERVER_RELEASES.get((major, None)) or + release) + + try: + try: + import winreg + except ImportError: + import _winreg as winreg + except ImportError: + pass + else: + try: + cvkey = r'SOFTWARE\Microsoft\Windows NT\CurrentVersion' + with winreg.OpenKeyEx(winreg.HKEY_LOCAL_MACHINE, cvkey) as key: + ptype = winreg.QueryValueEx(key, 'CurrentType')[0] + except OSError: + pass + + return release, version, csd, ptype + + +def _mac_ver_xml(): + fn = '/System/Library/CoreServices/SystemVersion.plist' + if not os.path.exists(fn): + if 'SDKROOT' in os.environ: + fn = os.environ['SDKROOT'] + fn + if not os.path.exists(fn): + return None + else: + return None + + try: + import plistlib + except ImportError: + return None + + with open(fn, 'rb') as f: + pl = plistlib.load(f) + release = pl['ProductVersion'] + versioninfo = ('', '', '') + machine = os.uname().machine + if machine in ('ppc', 'Power Macintosh'): + # Canonical name + machine = 'PowerPC' + + return release, versioninfo, machine + + +def mac_ver(release='', versioninfo=('', '', ''), machine=''): + + """ Get macOS version information and return it as tuple (release, + versioninfo, machine) with versioninfo being a tuple (version, + dev_stage, non_release_version). + + Entries which cannot be determined are set to the parameter values + which default to ''. All tuple entries are strings. + """ + + # First try reading the information from an XML file which should + # always be present + info = _mac_ver_xml() + if info is not None: + return info + + # If that also doesn't work return the default values + return release, versioninfo, machine + +def _java_getprop(name, default): + + from java.lang import System + try: + value = System.getProperty(name) + if value is None: + return default + return value + except AttributeError: + return default + +def java_ver(release='', vendor='', vminfo=('', '', ''), osinfo=('', '', '')): + + """ Version interface for Jython. + + Returns a tuple (release, vendor, vminfo, osinfo) with vminfo being + a tuple (vm_name, vm_release, vm_vendor) and osinfo being a + tuple (os_name, os_version, os_arch). + + Values which cannot be determined are set to the defaults + given as parameters (which all default to ''). + + """ + # Import the needed APIs + try: + import java.lang + except ImportError: + return release, vendor, vminfo, osinfo + + vendor = _java_getprop('java.vendor', vendor) + release = _java_getprop('java.version', release) + vm_name, vm_release, vm_vendor = vminfo + vm_name = _java_getprop('java.vm.name', vm_name) + vm_vendor = _java_getprop('java.vm.vendor', vm_vendor) + vm_release = _java_getprop('java.vm.version', vm_release) + vminfo = vm_name, vm_release, vm_vendor + os_name, os_version, os_arch = osinfo + os_arch = _java_getprop('java.os.arch', os_arch) + os_name = _java_getprop('java.os.name', os_name) + os_version = _java_getprop('java.os.version', os_version) + osinfo = os_name, os_version, os_arch + + return release, vendor, vminfo, osinfo + +### System name aliasing + +def system_alias(system, release, version): + + """ Returns (system, release, version) aliased to common + marketing names used for some systems. + + It also does some reordering of the information in some cases + where it would otherwise cause confusion. + + """ + if system == 'SunOS': + # Sun's OS + if release < '5': + # These releases use the old name SunOS + return system, release, version + # Modify release (marketing release = SunOS release - 3) + l = release.split('.') + if l: + try: + major = int(l[0]) + except ValueError: + pass + else: + major = major - 3 + l[0] = str(major) + release = '.'.join(l) + if release < '6': + system = 'Solaris' + else: + # XXX Whatever the new SunOS marketing name is... + system = 'Solaris' + + elif system == 'IRIX64': + # IRIX reports IRIX64 on platforms with 64-bit support; yet it + # is really a version and not a different platform, since 32-bit + # apps are also supported.. + system = 'IRIX' + if version: + version = version + ' (64bit)' + else: + version = '64bit' + + elif system in ('win32', 'win16'): + # In case one of the other tricks + system = 'Windows' + + # bpo-35516: Don't replace Darwin with macOS since input release and + # version arguments can be different than the currently running version. + + return system, release, version + +### Various internal helpers + +def _platform(*args): + + """ Helper to format the platform string in a filename + compatible format e.g. "system-version-machine". + """ + # Format the platform string + platform = '-'.join(x.strip() for x in filter(len, args)) + + # Cleanup some possible filename obstacles... + platform = platform.replace(' ', '_') + platform = platform.replace('/', '-') + platform = platform.replace('\\', '-') + platform = platform.replace(':', '-') + platform = platform.replace(';', '-') + platform = platform.replace('"', '-') + platform = platform.replace('(', '-') + platform = platform.replace(')', '-') + + # No need to report 'unknown' information... + platform = platform.replace('unknown', '') + + # Fold '--'s and remove trailing '-' + while 1: + cleaned = platform.replace('--', '-') + if cleaned == platform: + break + platform = cleaned + while platform[-1] == '-': + platform = platform[:-1] + + return platform + +def _node(default=''): + + """ Helper to determine the node name of this machine. + """ + try: + import socket + except ImportError: + # No sockets... + return default + try: + return socket.gethostname() + except OSError: + # Still not working... + return default + +def _follow_symlinks(filepath): + + """ In case filepath is a symlink, follow it until a + real file is reached. + """ + filepath = os.path.abspath(filepath) + while os.path.islink(filepath): + filepath = os.path.normpath( + os.path.join(os.path.dirname(filepath), os.readlink(filepath))) + return filepath + +def _syscmd_uname(option, default=''): + + """ Interface to the system's uname command. + """ + if sys.platform in ('dos', 'win32', 'win16'): + # XXX Others too ? + return default + + import subprocess + try: + output = subprocess.check_output(('uname', option), + stderr=subprocess.DEVNULL, + text=True) + except (OSError, subprocess.CalledProcessError): + return default + return (output.strip() or default) + +def _syscmd_file(target, default=''): + + """ Interface to the system's file command. + + The function uses the -b option of the file command to have it + omit the filename in its output. Follow the symlinks. It returns + default in case the command should fail. + + """ + if sys.platform in ('dos', 'win32', 'win16'): + # XXX Others too ? + return default + + import subprocess + target = _follow_symlinks(target) + # "file" output is locale dependent: force the usage of the C locale + # to get deterministic behavior. + env = dict(os.environ, LC_ALL='C') + try: + # -b: do not prepend filenames to output lines (brief mode) + output = subprocess.check_output(['file', '-b', target], + stderr=subprocess.DEVNULL, + env=env) + except (OSError, subprocess.CalledProcessError): + return default + if not output: + return default + # With the C locale, the output should be mostly ASCII-compatible. + # Decode from Latin-1 to prevent Unicode decode error. + return output.decode('latin-1') + +### Information about the used architecture + +# Default values for architecture; non-empty strings override the +# defaults given as parameters +_default_architecture = { + 'win32': ('', 'WindowsPE'), + 'win16': ('', 'Windows'), + 'dos': ('', 'MSDOS'), +} + +def architecture(executable=sys.executable, bits='', linkage=''): + + """ Queries the given executable (defaults to the Python interpreter + binary) for various architecture information. + + Returns a tuple (bits, linkage) which contains information about + the bit architecture and the linkage format used for the + executable. Both values are returned as strings. + + Values that cannot be determined are returned as given by the + parameter presets. If bits is given as '', the sizeof(pointer) + (or sizeof(long) on Python version < 1.5.2) is used as + indicator for the supported pointer size. + + The function relies on the system's "file" command to do the + actual work. This is available on most if not all Unix + platforms. On some non-Unix platforms where the "file" command + does not exist and the executable is set to the Python interpreter + binary defaults from _default_architecture are used. + + """ + # Use the sizeof(pointer) as default number of bits if nothing + # else is given as default. + if not bits: + import struct + size = struct.calcsize('P') + bits = str(size * 8) + 'bit' + + # Get data from the 'file' system command + if executable: + fileout = _syscmd_file(executable, '') + else: + fileout = '' + + if not fileout and \ + executable == sys.executable: + # "file" command did not return anything; we'll try to provide + # some sensible defaults then... + if sys.platform in _default_architecture: + b, l = _default_architecture[sys.platform] + if b: + bits = b + if l: + linkage = l + return bits, linkage + + if 'executable' not in fileout and 'shared object' not in fileout: + # Format not supported + return bits, linkage + + # Bits + if '32-bit' in fileout: + bits = '32bit' + elif 'N32' in fileout: + # On Irix only + bits = 'n32bit' + elif '64-bit' in fileout: + bits = '64bit' + + # Linkage + if 'ELF' in fileout: + linkage = 'ELF' + elif 'PE' in fileout: + # E.g. Windows uses this format + if 'Windows' in fileout: + linkage = 'WindowsPE' + else: + linkage = 'PE' + elif 'COFF' in fileout: + linkage = 'COFF' + elif 'MS-DOS' in fileout: + linkage = 'MSDOS' + else: + # XXX the A.OUT format also falls under this class... + pass + + return bits, linkage + +### Portable uname() interface + +uname_result = collections.namedtuple("uname_result", + "system node release version machine processor") + +_uname_cache = None + +def uname(): + + """ Fairly portable uname interface. Returns a tuple + of strings (system, node, release, version, machine, processor) + identifying the underlying platform. + + Note that unlike the os.uname function this also returns + possible processor information as an additional tuple entry. + + Entries which cannot be determined are set to ''. + + """ + global _uname_cache + no_os_uname = 0 + + if _uname_cache is not None: + return _uname_cache + + processor = '' + + # Get some infos from the builtin os.uname API... + try: + system, node, release, version, machine = os.uname() + except AttributeError: + no_os_uname = 1 + + if no_os_uname or not list(filter(None, (system, node, release, version, machine))): + # Hmm, no there is either no uname or uname has returned + #'unknowns'... we'll have to poke around the system then. + if no_os_uname: + system = sys.platform + release = '' + version = '' + node = _node() + machine = '' + + use_syscmd_ver = 1 + + # Try win32_ver() on win32 platforms + if system == 'win32': + release, version, csd, ptype = win32_ver() + if release and version: + use_syscmd_ver = 0 + # Try to use the PROCESSOR_* environment variables + # available on Win XP and later; see + # http://support.microsoft.com/kb/888731 and + # http://www.geocities.com/rick_lively/MANUALS/ENV/MSWIN/PROCESSI.HTM + if not machine: + # WOW64 processes mask the native architecture + if "PROCESSOR_ARCHITEW6432" in os.environ: + machine = os.environ.get("PROCESSOR_ARCHITEW6432", '') + else: + machine = os.environ.get('PROCESSOR_ARCHITECTURE', '') + if not processor: + processor = os.environ.get('PROCESSOR_IDENTIFIER', machine) + + # Try the 'ver' system command available on some + # platforms + if use_syscmd_ver: + system, release, version = _syscmd_ver(system) + # Normalize system to what win32_ver() normally returns + # (_syscmd_ver() tends to return the vendor name as well) + if system == 'Microsoft Windows': + system = 'Windows' + elif system == 'Microsoft' and release == 'Windows': + # Under Windows Vista and Windows Server 2008, + # Microsoft changed the output of the ver command. The + # release is no longer printed. This causes the + # system and release to be misidentified. + system = 'Windows' + if '6.0' == version[:3]: + release = 'Vista' + else: + release = '' + + # In case we still don't know anything useful, we'll try to + # help ourselves + if system in ('win32', 'win16'): + if not version: + if system == 'win32': + version = '32bit' + else: + version = '16bit' + system = 'Windows' + + elif system[:4] == 'java': + release, vendor, vminfo, osinfo = java_ver() + system = 'Java' + version = ', '.join(vminfo) + if not version: + version = vendor + + # System specific extensions + if system == 'OpenVMS': + # OpenVMS seems to have release and version mixed up + if not release or release == '0': + release = version + version = '' + # Get processor information + try: + import vms_lib + except ImportError: + pass + else: + csid, cpu_number = vms_lib.getsyi('SYI$_CPU', 0) + if (cpu_number >= 128): + processor = 'Alpha' + else: + processor = 'VAX' + if not processor: + # Get processor information from the uname system command + processor = _syscmd_uname('-p', '') + + #If any unknowns still exist, replace them with ''s, which are more portable + if system == 'unknown': + system = '' + if node == 'unknown': + node = '' + if release == 'unknown': + release = '' + if version == 'unknown': + version = '' + if machine == 'unknown': + machine = '' + if processor == 'unknown': + processor = '' + + # normalize name + if system == 'Microsoft' and release == 'Windows': + system = 'Windows' + release = 'Vista' + + _uname_cache = uname_result(system, node, release, version, + machine, processor) + return _uname_cache + +### Direct interfaces to some of the uname() return values + +def system(): + + """ Returns the system/OS name, e.g. 'Linux', 'Windows' or 'Java'. + + An empty string is returned if the value cannot be determined. + + """ + return uname().system + +def node(): + + """ Returns the computer's network name (which may not be fully + qualified) + + An empty string is returned if the value cannot be determined. + + """ + return uname().node + +def release(): + + """ Returns the system's release, e.g. '2.2.0' or 'NT' + + An empty string is returned if the value cannot be determined. + + """ + return uname().release + +def version(): + + """ Returns the system's release version, e.g. '#3 on degas' + + An empty string is returned if the value cannot be determined. + + """ + return uname().version + +def machine(): + + """ Returns the machine type, e.g. 'i386' + + An empty string is returned if the value cannot be determined. + + """ + return uname().machine + +def processor(): + + """ Returns the (true) processor name, e.g. 'amdk6' + + An empty string is returned if the value cannot be + determined. Note that many platforms do not provide this + information or simply return the same value as for machine(), + e.g. NetBSD does this. + + """ + return uname().processor + +### Various APIs for extracting information from sys.version + +_sys_version_parser = re.compile( + r'([\w.+]+)\s*' # "version" + r'(?:\|[^|]*\|)?\s*' # version extra + r'\(#?([^,]+)' # "(#buildno" + r'(?:,\s*([\w ]*)' # ", builddate" + r'(?:,\s*([\w :]*))?)?\)\s*' # ", buildtime)" + r'\[([^\]]+)\]?', re.ASCII) # "[compiler]" + +_ironpython_sys_version_parser = re.compile( + r'IronPython\s*' + r'([\d\.]+)' + r'(?: \(([\d\.]+)\))?' + r' on (.NET [\d\.]+)', re.ASCII) + +# IronPython covering 2.6 and 2.7 +_ironpython26_sys_version_parser = re.compile( + r'([\d.]+)\s*' + r'\(IronPython\s*' + r'[\d.]+\s*' + r'\(([\d.]+)\) on ([\w.]+ [\d.]+(?: \(\d+-bit\))?)\)' +) + +_pypy_sys_version_parser = re.compile( + r'([\w.+]+)\s*' + r'\(#?([^,]+),\s*([\w ]+),\s*([\w :]+)\)\s*' + r'\[PyPy [^\]]+\]?') + +_sys_version_cache = {} + +def _sys_version(sys_version=None): + + """ Returns a parsed version of Python's sys.version as tuple + (name, version, branch, revision, buildno, builddate, compiler) + referring to the Python implementation name, version, branch, + revision, build number, build date/time as string and the compiler + identification string. + + Note that unlike the Python sys.version, the returned value + for the Python version will always include the patchlevel (it + defaults to '.0'). + + The function returns empty strings for tuple entries that + cannot be determined. + + sys_version may be given to parse an alternative version + string, e.g. if the version was read from a different Python + interpreter. + + """ + # Get the Python version + if sys_version is None: + sys_version = sys.version + + # Try the cache first + result = _sys_version_cache.get(sys_version, None) + if result is not None: + return result + + # Parse it + if 'IronPython' in sys_version: + # IronPython + name = 'IronPython' + if sys_version.startswith('IronPython'): + match = _ironpython_sys_version_parser.match(sys_version) + else: + match = _ironpython26_sys_version_parser.match(sys_version) + + if match is None: + raise ValueError( + 'failed to parse IronPython sys.version: %s' % + repr(sys_version)) + + version, alt_version, compiler = match.groups() + buildno = '' + builddate = '' + + elif sys.platform.startswith('java'): + # Jython + name = 'Jython' + match = _sys_version_parser.match(sys_version) + if match is None: + raise ValueError( + 'failed to parse Jython sys.version: %s' % + repr(sys_version)) + version, buildno, builddate, buildtime, _ = match.groups() + if builddate is None: + builddate = '' + compiler = sys.platform + + elif "PyPy" in sys_version: + # PyPy + name = "PyPy" + match = _pypy_sys_version_parser.match(sys_version) + if match is None: + raise ValueError("failed to parse PyPy sys.version: %s" % + repr(sys_version)) + version, buildno, builddate, buildtime = match.groups() + compiler = "" + + else: + # CPython + match = _sys_version_parser.match(sys_version) + if match is None: + raise ValueError( + 'failed to parse CPython sys.version: %s' % + repr(sys_version)) + version, buildno, builddate, buildtime, compiler = \ + match.groups() + name = 'CPython' + if builddate is None: + builddate = '' + elif buildtime: + builddate = builddate + ' ' + buildtime + + if hasattr(sys, '_git'): + _, branch, revision = sys._git + elif hasattr(sys, '_mercurial'): + _, branch, revision = sys._mercurial + else: + branch = '' + revision = '' + + # Add the patchlevel version if missing + l = version.split('.') + if len(l) == 2: + l.append('0') + version = '.'.join(l) + + # Build and cache the result + result = (name, version, branch, revision, buildno, builddate, compiler) + _sys_version_cache[sys_version] = result + return result + +def python_implementation(): + + """ Returns a string identifying the Python implementation. + + Currently, the following implementations are identified: + 'CPython' (C implementation of Python), + 'IronPython' (.NET implementation of Python), + 'Jython' (Java implementation of Python), + 'PyPy' (Python implementation of Python). + + """ + return _sys_version()[0] + +def python_version(): + + """ Returns the Python version as string 'major.minor.patchlevel' + + Note that unlike the Python sys.version, the returned value + will always include the patchlevel (it defaults to 0). + + """ + return _sys_version()[1] + +def python_version_tuple(): + + """ Returns the Python version as tuple (major, minor, patchlevel) + of strings. + + Note that unlike the Python sys.version, the returned value + will always include the patchlevel (it defaults to 0). + + """ + return tuple(_sys_version()[1].split('.')) + +def python_branch(): + + """ Returns a string identifying the Python implementation + branch. + + For CPython this is the SCM branch from which the + Python binary was built. + + If not available, an empty string is returned. + + """ + + return _sys_version()[2] + +def python_revision(): + + """ Returns a string identifying the Python implementation + revision. + + For CPython this is the SCM revision from which the + Python binary was built. + + If not available, an empty string is returned. + + """ + return _sys_version()[3] + +def python_build(): + + """ Returns a tuple (buildno, builddate) stating the Python + build number and date as strings. + + """ + return _sys_version()[4:6] + +def python_compiler(): + + """ Returns a string identifying the compiler used for compiling + Python. + + """ + return _sys_version()[6] + +### The Opus Magnum of platform strings :-) + +_platform_cache = {} + +def platform(aliased=0, terse=0): + + """ Returns a single string identifying the underlying platform + with as much useful information as possible (but no more :). + + The output is intended to be human readable rather than + machine parseable. It may look different on different + platforms and this is intended. + + If "aliased" is true, the function will use aliases for + various platforms that report system names which differ from + their common names, e.g. SunOS will be reported as + Solaris. The system_alias() function is used to implement + this. + + Setting terse to true causes the function to return only the + absolute minimum information needed to identify the platform. + + """ + result = _platform_cache.get((aliased, terse), None) + if result is not None: + return result + + # Get uname information and then apply platform specific cosmetics + # to it... + system, node, release, version, machine, processor = uname() + if machine == processor: + processor = '' + if aliased: + system, release, version = system_alias(system, release, version) + + if system == 'Darwin': + # macOS (darwin kernel) + macos_release = mac_ver()[0] + if macos_release: + system = 'macOS' + release = macos_release + + if system == 'Windows': + # MS platforms + rel, vers, csd, ptype = win32_ver(version) + if terse: + platform = _platform(system, release) + else: + platform = _platform(system, release, version, csd) + + elif system in ('Linux',): + # check for libc vs. glibc + libcname, libcversion = libc_ver(sys.executable) + platform = _platform(system, release, machine, processor, + 'with', + libcname+libcversion) + elif system == 'Java': + # Java platforms + r, v, vminfo, (os_name, os_version, os_arch) = java_ver() + if terse or not os_name: + platform = _platform(system, release, version) + else: + platform = _platform(system, release, version, + 'on', + os_name, os_version, os_arch) + + else: + # Generic handler + if terse: + platform = _platform(system, release) + else: + bits, linkage = architecture(sys.executable) + platform = _platform(system, release, machine, + processor, bits, linkage) + + _platform_cache[(aliased, terse)] = platform + return platform + +### Command line interface + +if __name__ == '__main__': + # Default is to print the aliased verbose platform string + terse = ('terse' in sys.argv or '--terse' in sys.argv) + aliased = (not 'nonaliased' in sys.argv and not '--nonaliased' in sys.argv) + print(platform(aliased, terse)) + sys.exit(0) diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/poplib.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/poplib.py new file mode 100644 index 0000000000000000000000000000000000000000..e3bd2ab1ebc8bd2b08da1631993d344d3e8b5cc1 --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/poplib.py @@ -0,0 +1,481 @@ +"""A POP3 client class. + +Based on the J. Myers POP3 draft, Jan. 96 +""" + +# Author: David Ascher +# [heavily stealing from nntplib.py] +# Updated: Piers Lauder [Jul '97] +# String method conversion and test jig improvements by ESR, February 2001. +# Added the POP3_SSL class. Methods loosely based on IMAP_SSL. Hector Urtubia Aug 2003 + +# Example (see the test function at the end of this file) + +# Imports + +import errno +import re +import socket +import sys + +try: + import ssl + HAVE_SSL = True +except ImportError: + HAVE_SSL = False + +__all__ = ["POP3","error_proto"] + +# Exception raised when an error or invalid response is received: + +class error_proto(Exception): pass + +# Standard Port +POP3_PORT = 110 + +# POP SSL PORT +POP3_SSL_PORT = 995 + +# Line terminators (we always output CRLF, but accept any of CRLF, LFCR, LF) +CR = b'\r' +LF = b'\n' +CRLF = CR+LF + +# maximal line length when calling readline(). This is to prevent +# reading arbitrary length lines. RFC 1939 limits POP3 line length to +# 512 characters, including CRLF. We have selected 2048 just to be on +# the safe side. +_MAXLINE = 2048 + + +class POP3: + + """This class supports both the minimal and optional command sets. + Arguments can be strings or integers (where appropriate) + (e.g.: retr(1) and retr('1') both work equally well. + + Minimal Command Set: + USER name user(name) + PASS string pass_(string) + STAT stat() + LIST [msg] list(msg = None) + RETR msg retr(msg) + DELE msg dele(msg) + NOOP noop() + RSET rset() + QUIT quit() + + Optional Commands (some servers support these): + RPOP name rpop(name) + APOP name digest apop(name, digest) + TOP msg n top(msg, n) + UIDL [msg] uidl(msg = None) + CAPA capa() + STLS stls() + UTF8 utf8() + + Raises one exception: 'error_proto'. + + Instantiate with: + POP3(hostname, port=110) + + NB: the POP protocol locks the mailbox from user + authorization until QUIT, so be sure to get in, suck + the messages, and quit, each time you access the + mailbox. + + POP is a line-based protocol, which means large mail + messages consume lots of python cycles reading them + line-by-line. + + If it's available on your mail server, use IMAP4 + instead, it doesn't suffer from the two problems + above. + """ + + encoding = 'UTF-8' + + def __init__(self, host, port=POP3_PORT, + timeout=socket._GLOBAL_DEFAULT_TIMEOUT): + self.host = host + self.port = port + self._tls_established = False + sys.audit("poplib.connect", self, host, port) + self.sock = self._create_socket(timeout) + self.file = self.sock.makefile('rb') + self._debugging = 0 + self.welcome = self._getresp() + + def _create_socket(self, timeout): + return socket.create_connection((self.host, self.port), timeout) + + def _putline(self, line): + if self._debugging > 1: print('*put*', repr(line)) + sys.audit("poplib.putline", self, line) + self.sock.sendall(line + CRLF) + + + # Internal: send one command to the server (through _putline()) + + def _putcmd(self, line): + if self._debugging: print('*cmd*', repr(line)) + line = bytes(line, self.encoding) + self._putline(line) + + + # Internal: return one line from the server, stripping CRLF. + # This is where all the CPU time of this module is consumed. + # Raise error_proto('-ERR EOF') if the connection is closed. + + def _getline(self): + line = self.file.readline(_MAXLINE + 1) + if len(line) > _MAXLINE: + raise error_proto('line too long') + + if self._debugging > 1: print('*get*', repr(line)) + if not line: raise error_proto('-ERR EOF') + octets = len(line) + # server can send any combination of CR & LF + # however, 'readline()' returns lines ending in LF + # so only possibilities are ...LF, ...CRLF, CR...LF + if line[-2:] == CRLF: + return line[:-2], octets + if line[:1] == CR: + return line[1:-1], octets + return line[:-1], octets + + + # Internal: get a response from the server. + # Raise 'error_proto' if the response doesn't start with '+'. + + def _getresp(self): + resp, o = self._getline() + if self._debugging > 1: print('*resp*', repr(resp)) + if not resp.startswith(b'+'): + raise error_proto(resp) + return resp + + + # Internal: get a response plus following text from the server. + + def _getlongresp(self): + resp = self._getresp() + list = []; octets = 0 + line, o = self._getline() + while line != b'.': + if line.startswith(b'..'): + o = o-1 + line = line[1:] + octets = octets + o + list.append(line) + line, o = self._getline() + return resp, list, octets + + + # Internal: send a command and get the response + + def _shortcmd(self, line): + self._putcmd(line) + return self._getresp() + + + # Internal: send a command and get the response plus following text + + def _longcmd(self, line): + self._putcmd(line) + return self._getlongresp() + + + # These can be useful: + + def getwelcome(self): + return self.welcome + + + def set_debuglevel(self, level): + self._debugging = level + + + # Here are all the POP commands: + + def user(self, user): + """Send user name, return response + + (should indicate password required). + """ + return self._shortcmd('USER %s' % user) + + + def pass_(self, pswd): + """Send password, return response + + (response includes message count, mailbox size). + + NB: mailbox is locked by server from here to 'quit()' + """ + return self._shortcmd('PASS %s' % pswd) + + + def stat(self): + """Get mailbox status. + + Result is tuple of 2 ints (message count, mailbox size) + """ + retval = self._shortcmd('STAT') + rets = retval.split() + if self._debugging: print('*stat*', repr(rets)) + numMessages = int(rets[1]) + sizeMessages = int(rets[2]) + return (numMessages, sizeMessages) + + + def list(self, which=None): + """Request listing, return result. + + Result without a message number argument is in form + ['response', ['mesg_num octets', ...], octets]. + + Result when a message number argument is given is a + single response: the "scan listing" for that message. + """ + if which is not None: + return self._shortcmd('LIST %s' % which) + return self._longcmd('LIST') + + + def retr(self, which): + """Retrieve whole message number 'which'. + + Result is in form ['response', ['line', ...], octets]. + """ + return self._longcmd('RETR %s' % which) + + + def dele(self, which): + """Delete message number 'which'. + + Result is 'response'. + """ + return self._shortcmd('DELE %s' % which) + + + def noop(self): + """Does nothing. + + One supposes the response indicates the server is alive. + """ + return self._shortcmd('NOOP') + + + def rset(self): + """Unmark all messages marked for deletion.""" + return self._shortcmd('RSET') + + + def quit(self): + """Signoff: commit changes on server, unlock mailbox, close connection.""" + resp = self._shortcmd('QUIT') + self.close() + return resp + + def close(self): + """Close the connection without assuming anything about it.""" + try: + file = self.file + self.file = None + if file is not None: + file.close() + finally: + sock = self.sock + self.sock = None + if sock is not None: + try: + sock.shutdown(socket.SHUT_RDWR) + except OSError as exc: + # The server might already have closed the connection. + # On Windows, this may result in WSAEINVAL (error 10022): + # An invalid operation was attempted. + if (exc.errno != errno.ENOTCONN + and getattr(exc, 'winerror', 0) != 10022): + raise + finally: + sock.close() + + #__del__ = quit + + + # optional commands: + + def rpop(self, user): + """Not sure what this does.""" + return self._shortcmd('RPOP %s' % user) + + + timestamp = re.compile(br'\+OK.[^<]*(<.*>)') + + def apop(self, user, password): + """Authorisation + + - only possible if server has supplied a timestamp in initial greeting. + + Args: + user - mailbox user; + password - mailbox password. + + NB: mailbox is locked by server from here to 'quit()' + """ + secret = bytes(password, self.encoding) + m = self.timestamp.match(self.welcome) + if not m: + raise error_proto('-ERR APOP not supported by server') + import hashlib + digest = m.group(1)+secret + digest = hashlib.md5(digest).hexdigest() + return self._shortcmd('APOP %s %s' % (user, digest)) + + + def top(self, which, howmuch): + """Retrieve message header of message number 'which' + and first 'howmuch' lines of message body. + + Result is in form ['response', ['line', ...], octets]. + """ + return self._longcmd('TOP %s %s' % (which, howmuch)) + + + def uidl(self, which=None): + """Return message digest (unique id) list. + + If 'which', result contains unique id for that message + in the form 'response mesgnum uid', otherwise result is + the list ['response', ['mesgnum uid', ...], octets] + """ + if which is not None: + return self._shortcmd('UIDL %s' % which) + return self._longcmd('UIDL') + + + def utf8(self): + """Try to enter UTF-8 mode (see RFC 6856). Returns server response. + """ + return self._shortcmd('UTF8') + + + def capa(self): + """Return server capabilities (RFC 2449) as a dictionary + >>> c=poplib.POP3('localhost') + >>> c.capa() + {'IMPLEMENTATION': ['Cyrus', 'POP3', 'server', 'v2.2.12'], + 'TOP': [], 'LOGIN-DELAY': ['0'], 'AUTH-RESP-CODE': [], + 'EXPIRE': ['NEVER'], 'USER': [], 'STLS': [], 'PIPELINING': [], + 'UIDL': [], 'RESP-CODES': []} + >>> + + Really, according to RFC 2449, the cyrus folks should avoid + having the implementation split into multiple arguments... + """ + def _parsecap(line): + lst = line.decode('ascii').split() + return lst[0], lst[1:] + + caps = {} + try: + resp = self._longcmd('CAPA') + rawcaps = resp[1] + for capline in rawcaps: + capnm, capargs = _parsecap(capline) + caps[capnm] = capargs + except error_proto as _err: + raise error_proto('-ERR CAPA not supported by server') + return caps + + + def stls(self, context=None): + """Start a TLS session on the active connection as specified in RFC 2595. + + context - a ssl.SSLContext + """ + if not HAVE_SSL: + raise error_proto('-ERR TLS support missing') + if self._tls_established: + raise error_proto('-ERR TLS session already established') + caps = self.capa() + if not 'STLS' in caps: + raise error_proto('-ERR STLS not supported by server') + if context is None: + context = ssl._create_stdlib_context() + resp = self._shortcmd('STLS') + self.sock = context.wrap_socket(self.sock, + server_hostname=self.host) + self.file = self.sock.makefile('rb') + self._tls_established = True + return resp + + +if HAVE_SSL: + + class POP3_SSL(POP3): + """POP3 client class over SSL connection + + Instantiate with: POP3_SSL(hostname, port=995, keyfile=None, certfile=None, + context=None) + + hostname - the hostname of the pop3 over ssl server + port - port number + keyfile - PEM formatted file that contains your private key + certfile - PEM formatted certificate chain file + context - a ssl.SSLContext + + See the methods of the parent class POP3 for more documentation. + """ + + def __init__(self, host, port=POP3_SSL_PORT, keyfile=None, certfile=None, + timeout=socket._GLOBAL_DEFAULT_TIMEOUT, context=None): + if context is not None and keyfile is not None: + raise ValueError("context and keyfile arguments are mutually " + "exclusive") + if context is not None and certfile is not None: + raise ValueError("context and certfile arguments are mutually " + "exclusive") + if keyfile is not None or certfile is not None: + import warnings + warnings.warn("keyfile and certfile are deprecated, use a " + "custom context instead", DeprecationWarning, 2) + self.keyfile = keyfile + self.certfile = certfile + if context is None: + context = ssl._create_stdlib_context(certfile=certfile, + keyfile=keyfile) + self.context = context + POP3.__init__(self, host, port, timeout) + + def _create_socket(self, timeout): + sock = POP3._create_socket(self, timeout) + sock = self.context.wrap_socket(sock, + server_hostname=self.host) + return sock + + def stls(self, keyfile=None, certfile=None, context=None): + """The method unconditionally raises an exception since the + STLS command doesn't make any sense on an already established + SSL/TLS session. + """ + raise error_proto('-ERR TLS session already established') + + __all__.append("POP3_SSL") + +if __name__ == "__main__": + import sys + a = POP3(sys.argv[1]) + print(a.getwelcome()) + a.user(sys.argv[2]) + a.pass_(sys.argv[3]) + a.list() + (numMsgs, totalSize) = a.stat() + for i in range(1, numMsgs + 1): + (header, msg, octets) = a.retr(i) + print("Message %d:" % i) + for line in msg: + print(' ' + line) + print('-----------------------') + a.quit() diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/posixpath.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/posixpath.py new file mode 100644 index 0000000000000000000000000000000000000000..ecb4e5a8f7072c52effe4606b7406190a2dd7787 --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/posixpath.py @@ -0,0 +1,525 @@ +"""Common operations on Posix pathnames. + +Instead of importing this module directly, import os and refer to +this module as os.path. The "os.path" name is an alias for this +module on Posix systems; on other systems (e.g. Windows), +os.path provides the same operations in a manner specific to that +platform, and is an alias to another module (e.g. ntpath). + +Some of this can actually be useful on non-Posix systems too, e.g. +for manipulation of the pathname component of URLs. +""" + +# Strings representing various path-related bits and pieces. +# These are primarily for export; internally, they are hardcoded. +# Should be set before imports for resolving cyclic dependency. +curdir = '.' +pardir = '..' +extsep = '.' +sep = '/' +pathsep = ':' +defpath = '/bin:/usr/bin' +altsep = None +devnull = '/dev/null' + +import os +import sys +import stat +import genericpath +from genericpath import * + +__all__ = ["normcase","isabs","join","splitdrive","split","splitext", + "basename","dirname","commonprefix","getsize","getmtime", + "getatime","getctime","islink","exists","lexists","isdir","isfile", + "ismount", "expanduser","expandvars","normpath","abspath", + "samefile","sameopenfile","samestat", + "curdir","pardir","sep","pathsep","defpath","altsep","extsep", + "devnull","realpath","supports_unicode_filenames","relpath", + "commonpath"] + + +def _get_sep(path): + if isinstance(path, bytes): + return b'/' + else: + return '/' + +# Normalize the case of a pathname. Trivial in Posix, string.lower on Mac. +# On MS-DOS this may also turn slashes into backslashes; however, other +# normalizations (such as optimizing '../' away) are not allowed +# (another function should be defined to do that). + +def normcase(s): + """Normalize case of pathname. Has no effect under Posix""" + return os.fspath(s) + + +# Return whether a path is absolute. +# Trivial in Posix, harder on the Mac or MS-DOS. + +def isabs(s): + """Test whether a path is absolute""" + s = os.fspath(s) + sep = _get_sep(s) + return s.startswith(sep) + + +# Join pathnames. +# Ignore the previous parts if a part is absolute. +# Insert a '/' unless the first part is empty or already ends in '/'. + +def join(a, *p): + """Join two or more pathname components, inserting '/' as needed. + If any component is an absolute path, all previous path components + will be discarded. An empty last part will result in a path that + ends with a separator.""" + a = os.fspath(a) + sep = _get_sep(a) + path = a + try: + if not p: + path[:0] + sep #23780: Ensure compatible data type even if p is null. + for b in map(os.fspath, p): + if b.startswith(sep): + path = b + elif not path or path.endswith(sep): + path += b + else: + path += sep + b + except (TypeError, AttributeError, BytesWarning): + genericpath._check_arg_types('join', a, *p) + raise + return path + + +# Split a path in head (everything up to the last '/') and tail (the +# rest). If the path ends in '/', tail will be empty. If there is no +# '/' in the path, head will be empty. +# Trailing '/'es are stripped from head unless it is the root. + +def split(p): + """Split a pathname. Returns tuple "(head, tail)" where "tail" is + everything after the final slash. Either part may be empty.""" + p = os.fspath(p) + sep = _get_sep(p) + i = p.rfind(sep) + 1 + head, tail = p[:i], p[i:] + if head and head != sep*len(head): + head = head.rstrip(sep) + return head, tail + + +# Split a path in root and extension. +# The extension is everything starting at the last dot in the last +# pathname component; the root is everything before that. +# It is always true that root + ext == p. + +def splitext(p): + p = os.fspath(p) + if isinstance(p, bytes): + sep = b'/' + extsep = b'.' + else: + sep = '/' + extsep = '.' + return genericpath._splitext(p, sep, None, extsep) +splitext.__doc__ = genericpath._splitext.__doc__ + +# Split a pathname into a drive specification and the rest of the +# path. Useful on DOS/Windows/NT; on Unix, the drive is always empty. + +def splitdrive(p): + """Split a pathname into drive and path. On Posix, drive is always + empty.""" + p = os.fspath(p) + return p[:0], p + + +# Return the tail (basename) part of a path, same as split(path)[1]. + +def basename(p): + """Returns the final component of a pathname""" + p = os.fspath(p) + sep = _get_sep(p) + i = p.rfind(sep) + 1 + return p[i:] + + +# Return the head (dirname) part of a path, same as split(path)[0]. + +def dirname(p): + """Returns the directory component of a pathname""" + p = os.fspath(p) + sep = _get_sep(p) + i = p.rfind(sep) + 1 + head = p[:i] + if head and head != sep*len(head): + head = head.rstrip(sep) + return head + + +# Is a path a symbolic link? +# This will always return false on systems where os.lstat doesn't exist. + +def islink(path): + """Test whether a path is a symbolic link""" + try: + st = os.lstat(path) + except (OSError, ValueError, AttributeError): + return False + return stat.S_ISLNK(st.st_mode) + +# Being true for dangling symbolic links is also useful. + +def lexists(path): + """Test whether a path exists. Returns True for broken symbolic links""" + try: + os.lstat(path) + except (OSError, ValueError): + return False + return True + + +# Is a path a mount point? +# (Does this work for all UNIXes? Is it even guaranteed to work by Posix?) + +def ismount(path): + """Test whether a path is a mount point""" + try: + s1 = os.lstat(path) + except (OSError, ValueError): + # It doesn't exist -- so not a mount point. :-) + return False + else: + # A symlink can never be a mount point + if stat.S_ISLNK(s1.st_mode): + return False + + if isinstance(path, bytes): + parent = join(path, b'..') + else: + parent = join(path, '..') + parent = realpath(parent) + try: + s2 = os.lstat(parent) + except (OSError, ValueError): + return False + + dev1 = s1.st_dev + dev2 = s2.st_dev + if dev1 != dev2: + return True # path/.. on a different device as path + ino1 = s1.st_ino + ino2 = s2.st_ino + if ino1 == ino2: + return True # path/.. is the same i-node as path + return False + + +# Expand paths beginning with '~' or '~user'. +# '~' means $HOME; '~user' means that user's home directory. +# If the path doesn't begin with '~', or if the user or $HOME is unknown, +# the path is returned unchanged (leaving error reporting to whatever +# function is called with the expanded path as argument). +# See also module 'glob' for expansion of *, ? and [...] in pathnames. +# (A function should also be defined to do full *sh-style environment +# variable expansion.) + +def expanduser(path): + """Expand ~ and ~user constructions. If user or $HOME is unknown, + do nothing.""" + path = os.fspath(path) + if isinstance(path, bytes): + tilde = b'~' + else: + tilde = '~' + if not path.startswith(tilde): + return path + sep = _get_sep(path) + i = path.find(sep, 1) + if i < 0: + i = len(path) + if i == 1: + if 'HOME' not in os.environ: + import pwd + try: + userhome = pwd.getpwuid(os.getuid()).pw_dir + except KeyError: + # bpo-10496: if the current user identifier doesn't exist in the + # password database, return the path unchanged + return path + else: + userhome = os.environ['HOME'] + else: + import pwd + name = path[1:i] + if isinstance(name, bytes): + name = str(name, 'ASCII') + try: + pwent = pwd.getpwnam(name) + except KeyError: + # bpo-10496: if the user name from the path doesn't exist in the + # password database, return the path unchanged + return path + userhome = pwent.pw_dir + if isinstance(path, bytes): + userhome = os.fsencode(userhome) + root = b'/' + else: + root = '/' + userhome = userhome.rstrip(root) + return (userhome + path[i:]) or root + + +# Expand paths containing shell variable substitutions. +# This expands the forms $variable and ${variable} only. +# Non-existent variables are left unchanged. + +_varprog = None +_varprogb = None + +def expandvars(path): + """Expand shell variables of form $var and ${var}. Unknown variables + are left unchanged.""" + path = os.fspath(path) + global _varprog, _varprogb + if isinstance(path, bytes): + if b'$' not in path: + return path + if not _varprogb: + import re + _varprogb = re.compile(br'\$(\w+|\{[^}]*\})', re.ASCII) + search = _varprogb.search + start = b'{' + end = b'}' + environ = getattr(os, 'environb', None) + else: + if '$' not in path: + return path + if not _varprog: + import re + _varprog = re.compile(r'\$(\w+|\{[^}]*\})', re.ASCII) + search = _varprog.search + start = '{' + end = '}' + environ = os.environ + i = 0 + while True: + m = search(path, i) + if not m: + break + i, j = m.span(0) + name = m.group(1) + if name.startswith(start) and name.endswith(end): + name = name[1:-1] + try: + if environ is None: + value = os.fsencode(os.environ[os.fsdecode(name)]) + else: + value = environ[name] + except KeyError: + i = j + else: + tail = path[j:] + path = path[:i] + value + i = len(path) + path += tail + return path + + +# Normalize a path, e.g. A//B, A/./B and A/foo/../B all become A/B. +# It should be understood that this may change the meaning of the path +# if it contains symbolic links! + +def normpath(path): + """Normalize path, eliminating double slashes, etc.""" + path = os.fspath(path) + if isinstance(path, bytes): + sep = b'/' + empty = b'' + dot = b'.' + dotdot = b'..' + else: + sep = '/' + empty = '' + dot = '.' + dotdot = '..' + if path == empty: + return dot + initial_slashes = path.startswith(sep) + # POSIX allows one or two initial slashes, but treats three or more + # as single slash. + if (initial_slashes and + path.startswith(sep*2) and not path.startswith(sep*3)): + initial_slashes = 2 + comps = path.split(sep) + new_comps = [] + for comp in comps: + if comp in (empty, dot): + continue + if (comp != dotdot or (not initial_slashes and not new_comps) or + (new_comps and new_comps[-1] == dotdot)): + new_comps.append(comp) + elif new_comps: + new_comps.pop() + comps = new_comps + path = sep.join(comps) + if initial_slashes: + path = sep*initial_slashes + path + return path or dot + + +def abspath(path): + """Return an absolute path.""" + path = os.fspath(path) + if not isabs(path): + if isinstance(path, bytes): + cwd = os.getcwdb() + else: + cwd = os.getcwd() + path = join(cwd, path) + return normpath(path) + + +# Return a canonical path (i.e. the absolute location of a file on the +# filesystem). + +def realpath(filename): + """Return the canonical path of the specified filename, eliminating any +symbolic links encountered in the path.""" + filename = os.fspath(filename) + path, ok = _joinrealpath(filename[:0], filename, {}) + return abspath(path) + +# Join two paths, normalizing and eliminating any symbolic links +# encountered in the second path. +def _joinrealpath(path, rest, seen): + if isinstance(path, bytes): + sep = b'/' + curdir = b'.' + pardir = b'..' + else: + sep = '/' + curdir = '.' + pardir = '..' + + if isabs(rest): + rest = rest[1:] + path = sep + + while rest: + name, _, rest = rest.partition(sep) + if not name or name == curdir: + # current dir + continue + if name == pardir: + # parent dir + if path: + path, name = split(path) + if name == pardir: + path = join(path, pardir, pardir) + else: + path = pardir + continue + newpath = join(path, name) + if not islink(newpath): + path = newpath + continue + # Resolve the symbolic link + if newpath in seen: + # Already seen this path + path = seen[newpath] + if path is not None: + # use cached value + continue + # The symlink is not resolved, so we must have a symlink loop. + # Return already resolved part + rest of the path unchanged. + return join(newpath, rest), False + seen[newpath] = None # not resolved symlink + path, ok = _joinrealpath(path, os.readlink(newpath), seen) + if not ok: + return join(path, rest), False + seen[newpath] = path # resolved symlink + + return path, True + + +supports_unicode_filenames = (sys.platform == 'darwin') + +def relpath(path, start=None): + """Return a relative version of a path""" + + if not path: + raise ValueError("no path specified") + + path = os.fspath(path) + if isinstance(path, bytes): + curdir = b'.' + sep = b'/' + pardir = b'..' + else: + curdir = '.' + sep = '/' + pardir = '..' + + if start is None: + start = curdir + else: + start = os.fspath(start) + + try: + start_list = [x for x in abspath(start).split(sep) if x] + path_list = [x for x in abspath(path).split(sep) if x] + # Work out how much of the filepath is shared by start and path. + i = len(commonprefix([start_list, path_list])) + + rel_list = [pardir] * (len(start_list)-i) + path_list[i:] + if not rel_list: + return curdir + return join(*rel_list) + except (TypeError, AttributeError, BytesWarning, DeprecationWarning): + genericpath._check_arg_types('relpath', path, start) + raise + + +# Return the longest common sub-path of the sequence of paths given as input. +# The paths are not normalized before comparing them (this is the +# responsibility of the caller). Any trailing separator is stripped from the +# returned path. + +def commonpath(paths): + """Given a sequence of path names, returns the longest common sub-path.""" + + if not paths: + raise ValueError('commonpath() arg is an empty sequence') + + paths = tuple(map(os.fspath, paths)) + if isinstance(paths[0], bytes): + sep = b'/' + curdir = b'.' + else: + sep = '/' + curdir = '.' + + try: + split_paths = [path.split(sep) for path in paths] + + try: + isabs, = set(p[:1] == sep for p in paths) + except ValueError: + raise ValueError("Can't mix absolute and relative paths") from None + + split_paths = [[c for c in s if c and c != curdir] for s in split_paths] + s1 = min(split_paths) + s2 = max(split_paths) + common = s1 + for i, c in enumerate(s1): + if c != s2[i]: + common = s1[:i] + break + + prefix = sep if isabs else sep[:0] + return prefix + sep.join(common) + except (TypeError, AttributeError): + genericpath._check_arg_types('commonpath', *paths) + raise diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/pyclbr.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/pyclbr.py new file mode 100644 index 0000000000000000000000000000000000000000..99a17343fb61fdaa0f13422fa0325b95aeb88a4b --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/pyclbr.py @@ -0,0 +1,404 @@ +"""Parse a Python module and describe its classes and functions. + +Parse enough of a Python file to recognize imports and class and +function definitions, and to find out the superclasses of a class. + +The interface consists of a single function: + readmodule_ex(module, path=None) +where module is the name of a Python module, and path is an optional +list of directories where the module is to be searched. If present, +path is prepended to the system search path sys.path. The return value +is a dictionary. The keys of the dictionary are the names of the +classes and functions defined in the module (including classes that are +defined via the from XXX import YYY construct). The values are +instances of classes Class and Function. One special key/value pair is +present for packages: the key '__path__' has a list as its value which +contains the package search path. + +Classes and Functions have a common superclass: _Object. Every instance +has the following attributes: + module -- name of the module; + name -- name of the object; + file -- file in which the object is defined; + lineno -- line in the file where the object's definition starts; + parent -- parent of this object, if any; + children -- nested objects contained in this object. +The 'children' attribute is a dictionary mapping names to objects. + +Instances of Function describe functions with the attributes from _Object. + +Instances of Class describe classes with the attributes from _Object, +plus the following: + super -- list of super classes (Class instances if possible); + methods -- mapping of method names to beginning line numbers. +If the name of a super class is not recognized, the corresponding +entry in the list of super classes is not a class instance but a +string giving the name of the super class. Since import statements +are recognized and imported modules are scanned as well, this +shouldn't happen often. +""" + +import io +import sys +import importlib.util +import tokenize +from token import NAME, DEDENT, OP + +__all__ = ["readmodule", "readmodule_ex", "Class", "Function"] + +_modules = {} # Initialize cache of modules we've seen. + + +class _Object: + "Information about Python class or function." + def __init__(self, module, name, file, lineno, parent): + self.module = module + self.name = name + self.file = file + self.lineno = lineno + self.parent = parent + self.children = {} + + def _addchild(self, name, obj): + self.children[name] = obj + + +class Function(_Object): + "Information about a Python function, including methods." + def __init__(self, module, name, file, lineno, parent=None): + _Object.__init__(self, module, name, file, lineno, parent) + + +class Class(_Object): + "Information about a Python class." + def __init__(self, module, name, super, file, lineno, parent=None): + _Object.__init__(self, module, name, file, lineno, parent) + self.super = [] if super is None else super + self.methods = {} + + def _addmethod(self, name, lineno): + self.methods[name] = lineno + + +def _nest_function(ob, func_name, lineno): + "Return a Function after nesting within ob." + newfunc = Function(ob.module, func_name, ob.file, lineno, ob) + ob._addchild(func_name, newfunc) + if isinstance(ob, Class): + ob._addmethod(func_name, lineno) + return newfunc + +def _nest_class(ob, class_name, lineno, super=None): + "Return a Class after nesting within ob." + newclass = Class(ob.module, class_name, super, ob.file, lineno, ob) + ob._addchild(class_name, newclass) + return newclass + +def readmodule(module, path=None): + """Return Class objects for the top-level classes in module. + + This is the original interface, before Functions were added. + """ + + res = {} + for key, value in _readmodule(module, path or []).items(): + if isinstance(value, Class): + res[key] = value + return res + +def readmodule_ex(module, path=None): + """Return a dictionary with all functions and classes in module. + + Search for module in PATH + sys.path. + If possible, include imported superclasses. + Do this by reading source, without importing (and executing) it. + """ + return _readmodule(module, path or []) + +def _readmodule(module, path, inpackage=None): + """Do the hard work for readmodule[_ex]. + + If inpackage is given, it must be the dotted name of the package in + which we are searching for a submodule, and then PATH must be the + package search path; otherwise, we are searching for a top-level + module, and path is combined with sys.path. + """ + # Compute the full module name (prepending inpackage if set). + if inpackage is not None: + fullmodule = "%s.%s" % (inpackage, module) + else: + fullmodule = module + + # Check in the cache. + if fullmodule in _modules: + return _modules[fullmodule] + + # Initialize the dict for this module's contents. + tree = {} + + # Check if it is a built-in module; we don't do much for these. + if module in sys.builtin_module_names and inpackage is None: + _modules[module] = tree + return tree + + # Check for a dotted module name. + i = module.rfind('.') + if i >= 0: + package = module[:i] + submodule = module[i+1:] + parent = _readmodule(package, path, inpackage) + if inpackage is not None: + package = "%s.%s" % (inpackage, package) + if not '__path__' in parent: + raise ImportError('No package named {}'.format(package)) + return _readmodule(submodule, parent['__path__'], package) + + # Search the path for the module. + f = None + if inpackage is not None: + search_path = path + else: + search_path = path + sys.path + spec = importlib.util._find_spec_from_path(fullmodule, search_path) + if spec is None: + raise ModuleNotFoundError(f"no module named {fullmodule!r}", name=fullmodule) + _modules[fullmodule] = tree + # Is module a package? + if spec.submodule_search_locations is not None: + tree['__path__'] = spec.submodule_search_locations + try: + source = spec.loader.get_source(fullmodule) + except (AttributeError, ImportError): + # If module is not Python source, we cannot do anything. + return tree + else: + if source is None: + return tree + + fname = spec.loader.get_filename(fullmodule) + return _create_tree(fullmodule, path, fname, source, tree, inpackage) + + +def _create_tree(fullmodule, path, fname, source, tree, inpackage): + """Return the tree for a particular module. + + fullmodule (full module name), inpackage+module, becomes o.module. + path is passed to recursive calls of _readmodule. + fname becomes o.file. + source is tokenized. Imports cause recursive calls to _readmodule. + tree is {} or {'__path__': }. + inpackage, None or string, is passed to recursive calls of _readmodule. + + The effect of recursive calls is mutation of global _modules. + """ + f = io.StringIO(source) + + stack = [] # Initialize stack of (class, indent) pairs. + + g = tokenize.generate_tokens(f.readline) + try: + for tokentype, token, start, _end, _line in g: + if tokentype == DEDENT: + lineno, thisindent = start + # Close previous nested classes and defs. + while stack and stack[-1][1] >= thisindent: + del stack[-1] + elif token == 'def': + lineno, thisindent = start + # Close previous nested classes and defs. + while stack and stack[-1][1] >= thisindent: + del stack[-1] + tokentype, func_name, start = next(g)[0:3] + if tokentype != NAME: + continue # Skip def with syntax error. + cur_func = None + if stack: + cur_obj = stack[-1][0] + cur_func = _nest_function(cur_obj, func_name, lineno) + else: + # It is just a function. + cur_func = Function(fullmodule, func_name, fname, lineno) + tree[func_name] = cur_func + stack.append((cur_func, thisindent)) + elif token == 'class': + lineno, thisindent = start + # Close previous nested classes and defs. + while stack and stack[-1][1] >= thisindent: + del stack[-1] + tokentype, class_name, start = next(g)[0:3] + if tokentype != NAME: + continue # Skip class with syntax error. + # Parse what follows the class name. + tokentype, token, start = next(g)[0:3] + inherit = None + if token == '(': + names = [] # Initialize list of superclasses. + level = 1 + super = [] # Tokens making up current superclass. + while True: + tokentype, token, start = next(g)[0:3] + if token in (')', ',') and level == 1: + n = "".join(super) + if n in tree: + # We know this super class. + n = tree[n] + else: + c = n.split('.') + if len(c) > 1: + # Super class form is module.class: + # look in module for class. + m = c[-2] + c = c[-1] + if m in _modules: + d = _modules[m] + if c in d: + n = d[c] + names.append(n) + super = [] + if token == '(': + level += 1 + elif token == ')': + level -= 1 + if level == 0: + break + elif token == ',' and level == 1: + pass + # Only use NAME and OP (== dot) tokens for type name. + elif tokentype in (NAME, OP) and level == 1: + super.append(token) + # Expressions in the base list are not supported. + inherit = names + if stack: + cur_obj = stack[-1][0] + cur_class = _nest_class( + cur_obj, class_name, lineno, inherit) + else: + cur_class = Class(fullmodule, class_name, inherit, + fname, lineno) + tree[class_name] = cur_class + stack.append((cur_class, thisindent)) + elif token == 'import' and start[1] == 0: + modules = _getnamelist(g) + for mod, _mod2 in modules: + try: + # Recursively read the imported module. + if inpackage is None: + _readmodule(mod, path) + else: + try: + _readmodule(mod, path, inpackage) + except ImportError: + _readmodule(mod, []) + except: + # If we can't find or parse the imported module, + # too bad -- don't die here. + pass + elif token == 'from' and start[1] == 0: + mod, token = _getname(g) + if not mod or token != "import": + continue + names = _getnamelist(g) + try: + # Recursively read the imported module. + d = _readmodule(mod, path, inpackage) + except: + # If we can't find or parse the imported module, + # too bad -- don't die here. + continue + # Add any classes that were defined in the imported module + # to our name space if they were mentioned in the list. + for n, n2 in names: + if n in d: + tree[n2 or n] = d[n] + elif n == '*': + # Don't add names that start with _. + for n in d: + if n[0] != '_': + tree[n] = d[n] + except StopIteration: + pass + + f.close() + return tree + + +def _getnamelist(g): + """Return list of (dotted-name, as-name or None) tuples for token source g. + + An as-name is the name that follows 'as' in an as clause. + """ + names = [] + while True: + name, token = _getname(g) + if not name: + break + if token == 'as': + name2, token = _getname(g) + else: + name2 = None + names.append((name, name2)) + while token != "," and "\n" not in token: + token = next(g)[1] + if token != ",": + break + return names + + +def _getname(g): + "Return (dotted-name or None, next-token) tuple for token source g." + parts = [] + tokentype, token = next(g)[0:2] + if tokentype != NAME and token != '*': + return (None, token) + parts.append(token) + while True: + tokentype, token = next(g)[0:2] + if token != '.': + break + tokentype, token = next(g)[0:2] + if tokentype != NAME: + break + parts.append(token) + return (".".join(parts), token) + + +def _main(): + "Print module output (default this file) for quick visual check." + import os + try: + mod = sys.argv[1] + except: + mod = __file__ + if os.path.exists(mod): + path = [os.path.dirname(mod)] + mod = os.path.basename(mod) + if mod.lower().endswith(".py"): + mod = mod[:-3] + else: + path = [] + tree = readmodule_ex(mod, path) + lineno_key = lambda a: getattr(a, 'lineno', 0) + objs = sorted(tree.values(), key=lineno_key, reverse=True) + indent_level = 2 + while objs: + obj = objs.pop() + if isinstance(obj, list): + # Value is a __path__ key. + continue + if not hasattr(obj, 'indent'): + obj.indent = 0 + + if isinstance(obj, _Object): + new_objs = sorted(obj.children.values(), + key=lineno_key, reverse=True) + for ob in new_objs: + ob.indent = obj.indent + indent_level + objs.extend(new_objs) + if isinstance(obj, Class): + print("{}class {} {} {}" + .format(' ' * obj.indent, obj.name, obj.super, obj.lineno)) + elif isinstance(obj, Function): + print("{}def {} {}".format(' ' * obj.indent, obj.name, obj.lineno)) + +if __name__ == "__main__": + _main() diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/rlcompleter.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/rlcompleter.py new file mode 100644 index 0000000000000000000000000000000000000000..bca4a7bc5218a90d7ad145ae1d57e9eb489daeda --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/rlcompleter.py @@ -0,0 +1,205 @@ +"""Word completion for GNU readline. + +The completer completes keywords, built-ins and globals in a selectable +namespace (which defaults to __main__); when completing NAME.NAME..., it +evaluates (!) the expression up to the last dot and completes its attributes. + +It's very cool to do "import sys" type "sys.", hit the completion key (twice), +and see the list of names defined by the sys module! + +Tip: to use the tab key as the completion key, call + + readline.parse_and_bind("tab: complete") + +Notes: + +- Exceptions raised by the completer function are *ignored* (and generally cause + the completion to fail). This is a feature -- since readline sets the tty + device in raw (or cbreak) mode, printing a traceback wouldn't work well + without some complicated hoopla to save, reset and restore the tty state. + +- The evaluation of the NAME.NAME... form may cause arbitrary application + defined code to be executed if an object with a __getattr__ hook is found. + Since it is the responsibility of the application (or the user) to enable this + feature, I consider this an acceptable risk. More complicated expressions + (e.g. function calls or indexing operations) are *not* evaluated. + +- When the original stdin is not a tty device, GNU readline is never + used, and this module (and the readline module) are silently inactive. + +""" + +import atexit +import builtins +import __main__ + +__all__ = ["Completer"] + +class Completer: + def __init__(self, namespace = None): + """Create a new completer for the command line. + + Completer([namespace]) -> completer instance. + + If unspecified, the default namespace where completions are performed + is __main__ (technically, __main__.__dict__). Namespaces should be + given as dictionaries. + + Completer instances should be used as the completion mechanism of + readline via the set_completer() call: + + readline.set_completer(Completer(my_namespace).complete) + """ + + if namespace and not isinstance(namespace, dict): + raise TypeError('namespace must be a dictionary') + + # Don't bind to namespace quite yet, but flag whether the user wants a + # specific namespace or to use __main__.__dict__. This will allow us + # to bind to __main__.__dict__ at completion time, not now. + if namespace is None: + self.use_main_ns = 1 + else: + self.use_main_ns = 0 + self.namespace = namespace + + def complete(self, text, state): + """Return the next possible completion for 'text'. + + This is called successively with state == 0, 1, 2, ... until it + returns None. The completion should begin with 'text'. + + """ + if self.use_main_ns: + self.namespace = __main__.__dict__ + + if not text.strip(): + if state == 0: + if _readline_available: + readline.insert_text('\t') + readline.redisplay() + return '' + else: + return '\t' + else: + return None + + if state == 0: + if "." in text: + self.matches = self.attr_matches(text) + else: + self.matches = self.global_matches(text) + try: + return self.matches[state] + except IndexError: + return None + + def _callable_postfix(self, val, word): + if callable(val): + word = word + "(" + return word + + def global_matches(self, text): + """Compute matches when text is a simple name. + + Return a list of all keywords, built-in functions and names currently + defined in self.namespace that match. + + """ + import keyword + matches = [] + seen = {"__builtins__"} + n = len(text) + for word in keyword.kwlist: + if word[:n] == text: + seen.add(word) + if word in {'finally', 'try'}: + word = word + ':' + elif word not in {'False', 'None', 'True', + 'break', 'continue', 'pass', + 'else'}: + word = word + ' ' + matches.append(word) + for nspace in [self.namespace, builtins.__dict__]: + for word, val in nspace.items(): + if word[:n] == text and word not in seen: + seen.add(word) + matches.append(self._callable_postfix(val, word)) + return matches + + def attr_matches(self, text): + """Compute matches when text contains a dot. + + Assuming the text is of the form NAME.NAME....[NAME], and is + evaluable in self.namespace, it will be evaluated and its attributes + (as revealed by dir()) are used as possible completions. (For class + instances, class members are also considered.) + + WARNING: this can still invoke arbitrary C code, if an object + with a __getattr__ hook is evaluated. + + """ + import re + m = re.match(r"(\w+(\.\w+)*)\.(\w*)", text) + if not m: + return [] + expr, attr = m.group(1, 3) + try: + thisobject = eval(expr, self.namespace) + except Exception: + return [] + + # get the content of the object, except __builtins__ + words = set(dir(thisobject)) + words.discard("__builtins__") + + if hasattr(thisobject, '__class__'): + words.add('__class__') + words.update(get_class_members(thisobject.__class__)) + matches = [] + n = len(attr) + if attr == '': + noprefix = '_' + elif attr == '_': + noprefix = '__' + else: + noprefix = None + while True: + for word in words: + if (word[:n] == attr and + not (noprefix and word[:n+1] == noprefix)): + match = "%s.%s" % (expr, word) + try: + val = getattr(thisobject, word) + except Exception: + pass # Include even if attribute not set + else: + match = self._callable_postfix(val, match) + matches.append(match) + if matches or not noprefix: + break + if noprefix == '_': + noprefix = '__' + else: + noprefix = None + matches.sort() + return matches + +def get_class_members(klass): + ret = dir(klass) + if hasattr(klass,'__bases__'): + for base in klass.__bases__: + ret = ret + get_class_members(base) + return ret + +try: + import readline +except ImportError: + _readline_available = False +else: + readline.set_completer(Completer().complete) + # Release references early at shutdown (the readline module's + # contents are quasi-immortal, and the completer function holds a + # reference to globals). + atexit.register(lambda: readline.set_completer(None)) + _readline_available = True diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/secrets.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/secrets.py new file mode 100644 index 0000000000000000000000000000000000000000..130434229e96a96dfacf1fc8b90dc98ef80d9bec --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/secrets.py @@ -0,0 +1,73 @@ +"""Generate cryptographically strong pseudo-random numbers suitable for +managing secrets such as account authentication, tokens, and similar. + +See PEP 506 for more information. +https://www.python.org/dev/peps/pep-0506/ + +""" + +__all__ = ['choice', 'randbelow', 'randbits', 'SystemRandom', + 'token_bytes', 'token_hex', 'token_urlsafe', + 'compare_digest', + ] + + +import base64 +import binascii +import os + +from hmac import compare_digest +from random import SystemRandom + +_sysrand = SystemRandom() + +randbits = _sysrand.getrandbits +choice = _sysrand.choice + +def randbelow(exclusive_upper_bound): + """Return a random int in the range [0, n).""" + if exclusive_upper_bound <= 0: + raise ValueError("Upper bound must be positive.") + return _sysrand._randbelow(exclusive_upper_bound) + +DEFAULT_ENTROPY = 32 # number of bytes to return by default + +def token_bytes(nbytes=None): + """Return a random byte string containing *nbytes* bytes. + + If *nbytes* is ``None`` or not supplied, a reasonable + default is used. + + >>> token_bytes(16) #doctest:+SKIP + b'\\xebr\\x17D*t\\xae\\xd4\\xe3S\\xb6\\xe2\\xebP1\\x8b' + + """ + if nbytes is None: + nbytes = DEFAULT_ENTROPY + return os.urandom(nbytes) + +def token_hex(nbytes=None): + """Return a random text string, in hexadecimal. + + The string has *nbytes* random bytes, each byte converted to two + hex digits. If *nbytes* is ``None`` or not supplied, a reasonable + default is used. + + >>> token_hex(16) #doctest:+SKIP + 'f9bf78b9a18ce6d46a0cd2b0b86df9da' + + """ + return binascii.hexlify(token_bytes(nbytes)).decode('ascii') + +def token_urlsafe(nbytes=None): + """Return a random URL-safe text string, in Base64 encoding. + + The string has *nbytes* random bytes. If *nbytes* is ``None`` + or not supplied, a reasonable default is used. + + >>> token_urlsafe(16) #doctest:+SKIP + 'Drmhze6EPcv0fN_81Bj-nA' + + """ + tok = token_bytes(nbytes) + return base64.urlsafe_b64encode(tok).rstrip(b'=').decode('ascii') diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/shelve.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/shelve.py new file mode 100644 index 0000000000000000000000000000000000000000..5d443a0fa8d4f14c56b1ca2497a621f0d02683d7 --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/shelve.py @@ -0,0 +1,243 @@ +"""Manage shelves of pickled objects. + +A "shelf" is a persistent, dictionary-like object. The difference +with dbm databases is that the values (not the keys!) in a shelf can +be essentially arbitrary Python objects -- anything that the "pickle" +module can handle. This includes most class instances, recursive data +types, and objects containing lots of shared sub-objects. The keys +are ordinary strings. + +To summarize the interface (key is a string, data is an arbitrary +object): + + import shelve + d = shelve.open(filename) # open, with (g)dbm filename -- no suffix + + d[key] = data # store data at key (overwrites old data if + # using an existing key) + data = d[key] # retrieve a COPY of the data at key (raise + # KeyError if no such key) -- NOTE that this + # access returns a *copy* of the entry! + del d[key] # delete data stored at key (raises KeyError + # if no such key) + flag = key in d # true if the key exists + list = d.keys() # a list of all existing keys (slow!) + + d.close() # close it + +Dependent on the implementation, closing a persistent dictionary may +or may not be necessary to flush changes to disk. + +Normally, d[key] returns a COPY of the entry. This needs care when +mutable entries are mutated: for example, if d[key] is a list, + d[key].append(anitem) +does NOT modify the entry d[key] itself, as stored in the persistent +mapping -- it only modifies the copy, which is then immediately +discarded, so that the append has NO effect whatsoever. To append an +item to d[key] in a way that will affect the persistent mapping, use: + data = d[key] + data.append(anitem) + d[key] = data + +To avoid the problem with mutable entries, you may pass the keyword +argument writeback=True in the call to shelve.open. When you use: + d = shelve.open(filename, writeback=True) +then d keeps a cache of all entries you access, and writes them all back +to the persistent mapping when you call d.close(). This ensures that +such usage as d[key].append(anitem) works as intended. + +However, using keyword argument writeback=True may consume vast amount +of memory for the cache, and it may make d.close() very slow, if you +access many of d's entries after opening it in this way: d has no way to +check which of the entries you access are mutable and/or which ones you +actually mutate, so it must cache, and write back at close, all of the +entries that you access. You can call d.sync() to write back all the +entries in the cache, and empty the cache (d.sync() also synchronizes +the persistent dictionary on disk, if feasible). +""" + +from pickle import Pickler, Unpickler +from io import BytesIO + +import collections.abc + +__all__ = ["Shelf", "BsdDbShelf", "DbfilenameShelf", "open"] + +class _ClosedDict(collections.abc.MutableMapping): + 'Marker for a closed dict. Access attempts raise a ValueError.' + + def closed(self, *args): + raise ValueError('invalid operation on closed shelf') + __iter__ = __len__ = __getitem__ = __setitem__ = __delitem__ = keys = closed + + def __repr__(self): + return '' + + +class Shelf(collections.abc.MutableMapping): + """Base class for shelf implementations. + + This is initialized with a dictionary-like object. + See the module's __doc__ string for an overview of the interface. + """ + + def __init__(self, dict, protocol=None, writeback=False, + keyencoding="utf-8"): + self.dict = dict + if protocol is None: + protocol = 3 + self._protocol = protocol + self.writeback = writeback + self.cache = {} + self.keyencoding = keyencoding + + def __iter__(self): + for k in self.dict.keys(): + yield k.decode(self.keyencoding) + + def __len__(self): + return len(self.dict) + + def __contains__(self, key): + return key.encode(self.keyencoding) in self.dict + + def get(self, key, default=None): + if key.encode(self.keyencoding) in self.dict: + return self[key] + return default + + def __getitem__(self, key): + try: + value = self.cache[key] + except KeyError: + f = BytesIO(self.dict[key.encode(self.keyencoding)]) + value = Unpickler(f).load() + if self.writeback: + self.cache[key] = value + return value + + def __setitem__(self, key, value): + if self.writeback: + self.cache[key] = value + f = BytesIO() + p = Pickler(f, self._protocol) + p.dump(value) + self.dict[key.encode(self.keyencoding)] = f.getvalue() + + def __delitem__(self, key): + del self.dict[key.encode(self.keyencoding)] + try: + del self.cache[key] + except KeyError: + pass + + def __enter__(self): + return self + + def __exit__(self, type, value, traceback): + self.close() + + def close(self): + if self.dict is None: + return + try: + self.sync() + try: + self.dict.close() + except AttributeError: + pass + finally: + # Catch errors that may happen when close is called from __del__ + # because CPython is in interpreter shutdown. + try: + self.dict = _ClosedDict() + except: + self.dict = None + + def __del__(self): + if not hasattr(self, 'writeback'): + # __init__ didn't succeed, so don't bother closing + # see http://bugs.python.org/issue1339007 for details + return + self.close() + + def sync(self): + if self.writeback and self.cache: + self.writeback = False + for key, entry in self.cache.items(): + self[key] = entry + self.writeback = True + self.cache = {} + if hasattr(self.dict, 'sync'): + self.dict.sync() + + +class BsdDbShelf(Shelf): + """Shelf implementation using the "BSD" db interface. + + This adds methods first(), next(), previous(), last() and + set_location() that have no counterpart in [g]dbm databases. + + The actual database must be opened using one of the "bsddb" + modules "open" routines (i.e. bsddb.hashopen, bsddb.btopen or + bsddb.rnopen) and passed to the constructor. + + See the module's __doc__ string for an overview of the interface. + """ + + def __init__(self, dict, protocol=None, writeback=False, + keyencoding="utf-8"): + Shelf.__init__(self, dict, protocol, writeback, keyencoding) + + def set_location(self, key): + (key, value) = self.dict.set_location(key) + f = BytesIO(value) + return (key.decode(self.keyencoding), Unpickler(f).load()) + + def next(self): + (key, value) = next(self.dict) + f = BytesIO(value) + return (key.decode(self.keyencoding), Unpickler(f).load()) + + def previous(self): + (key, value) = self.dict.previous() + f = BytesIO(value) + return (key.decode(self.keyencoding), Unpickler(f).load()) + + def first(self): + (key, value) = self.dict.first() + f = BytesIO(value) + return (key.decode(self.keyencoding), Unpickler(f).load()) + + def last(self): + (key, value) = self.dict.last() + f = BytesIO(value) + return (key.decode(self.keyencoding), Unpickler(f).load()) + + +class DbfilenameShelf(Shelf): + """Shelf implementation using the "dbm" generic dbm interface. + + This is initialized with the filename for the dbm database. + See the module's __doc__ string for an overview of the interface. + """ + + def __init__(self, filename, flag='c', protocol=None, writeback=False): + import dbm + Shelf.__init__(self, dbm.open(filename, flag), protocol, writeback) + + +def open(filename, flag='c', protocol=None, writeback=False): + """Open a persistent dictionary for reading and writing. + + The filename parameter is the base filename for the underlying + database. As a side-effect, an extension may be added to the + filename and more than one file may be created. The optional flag + parameter has the same interpretation as the flag parameter of + dbm.open(). The optional protocol parameter specifies the + version of the pickle protocol. + + See the module's __doc__ string for an overview of the interface. + """ + + return DbfilenameShelf(filename, flag, protocol, writeback) diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/site.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/site.py new file mode 100644 index 0000000000000000000000000000000000000000..9fa21cca3866747ab3e074777e90e497c35a348d --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/site.py @@ -0,0 +1,634 @@ +"""Append module search paths for third-party packages to sys.path. + +**************************************************************** +* This module is automatically imported during initialization. * +**************************************************************** + +This will append site-specific paths to the module search path. On +Unix (including Mac OSX), it starts with sys.prefix and +sys.exec_prefix (if different) and appends +lib/python/site-packages. +On other platforms (such as Windows), it tries each of the +prefixes directly, as well as with lib/site-packages appended. The +resulting directories, if they exist, are appended to sys.path, and +also inspected for path configuration files. + +If a file named "pyvenv.cfg" exists one directory above sys.executable, +sys.prefix and sys.exec_prefix are set to that directory and +it is also checked for site-packages (sys.base_prefix and +sys.base_exec_prefix will always be the "real" prefixes of the Python +installation). If "pyvenv.cfg" (a bootstrap configuration file) contains +the key "include-system-site-packages" set to anything other than "false" +(case-insensitive), the system-level prefixes will still also be +searched for site-packages; otherwise they won't. + +All of the resulting site-specific directories, if they exist, are +appended to sys.path, and also inspected for path configuration +files. + +A path configuration file is a file whose name has the form +.pth; its contents are additional directories (one per line) +to be added to sys.path. Non-existing directories (or +non-directories) are never added to sys.path; no directory is added to +sys.path more than once. Blank lines and lines beginning with +'#' are skipped. Lines starting with 'import' are executed. + +For example, suppose sys.prefix and sys.exec_prefix are set to +/usr/local and there is a directory /usr/local/lib/python2.5/site-packages +with three subdirectories, foo, bar and spam, and two path +configuration files, foo.pth and bar.pth. Assume foo.pth contains the +following: + + # foo package configuration + foo + bar + bletch + +and bar.pth contains: + + # bar package configuration + bar + +Then the following directories are added to sys.path, in this order: + + /usr/local/lib/python2.5/site-packages/bar + /usr/local/lib/python2.5/site-packages/foo + +Note that bletch is omitted because it doesn't exist; bar precedes foo +because bar.pth comes alphabetically before foo.pth; and spam is +omitted because it is not mentioned in either path configuration file. + +The readline module is also automatically configured to enable +completion for systems that support it. This can be overridden in +sitecustomize, usercustomize or PYTHONSTARTUP. Starting Python in +isolated mode (-I) disables automatic readline configuration. + +After these operations, an attempt is made to import a module +named sitecustomize, which can perform arbitrary additional +site-specific customizations. If this import fails with an +ImportError exception, it is silently ignored. +""" + +import sys +import os +import builtins +import _sitebuiltins +import io + +# Prefixes for site-packages; add additional prefixes like /usr/local here +PREFIXES = [sys.prefix, sys.exec_prefix] +# Enable per user site-packages directory +# set it to False to disable the feature or True to force the feature +ENABLE_USER_SITE = None + +# for distutils.commands.install +# These values are initialized by the getuserbase() and getusersitepackages() +# functions, through the main() function when Python starts. +USER_SITE = None +USER_BASE = None + + +def makepath(*paths): + dir = os.path.join(*paths) + try: + dir = os.path.abspath(dir) + except OSError: + pass + return dir, os.path.normcase(dir) + + +def abs_paths(): + """Set all module __file__ and __cached__ attributes to an absolute path""" + for m in set(sys.modules.values()): + if (getattr(getattr(m, '__loader__', None), '__module__', None) not in + ('_frozen_importlib', '_frozen_importlib_external')): + continue # don't mess with a PEP 302-supplied __file__ + try: + m.__file__ = os.path.abspath(m.__file__) + except (AttributeError, OSError, TypeError): + pass + try: + m.__cached__ = os.path.abspath(m.__cached__) + except (AttributeError, OSError, TypeError): + pass + + +def removeduppaths(): + """ Remove duplicate entries from sys.path along with making them + absolute""" + # This ensures that the initial path provided by the interpreter contains + # only absolute pathnames, even if we're running from the build directory. + L = [] + known_paths = set() + for dir in sys.path: + # Filter out duplicate paths (on case-insensitive file systems also + # if they only differ in case); turn relative paths into absolute + # paths. + dir, dircase = makepath(dir) + if dircase not in known_paths: + L.append(dir) + known_paths.add(dircase) + sys.path[:] = L + return known_paths + + +def _init_pathinfo(): + """Return a set containing all existing file system items from sys.path.""" + d = set() + for item in sys.path: + try: + if os.path.exists(item): + _, itemcase = makepath(item) + d.add(itemcase) + except TypeError: + continue + return d + + +def addpackage(sitedir, name, known_paths): + """Process a .pth file within the site-packages directory: + For each line in the file, either combine it with sitedir to a path + and add that to known_paths, or execute it if it starts with 'import '. + """ + if known_paths is None: + known_paths = _init_pathinfo() + reset = True + else: + reset = False + fullname = os.path.join(sitedir, name) + try: + f = io.TextIOWrapper(io.open_code(fullname)) + except OSError: + return + with f: + for n, line in enumerate(f): + if line.startswith("#"): + continue + try: + if line.startswith(("import ", "import\t")): + exec(line) + continue + line = line.rstrip() + dir, dircase = makepath(sitedir, line) + if not dircase in known_paths and os.path.exists(dir): + sys.path.append(dir) + known_paths.add(dircase) + except Exception: + print("Error processing line {:d} of {}:\n".format(n+1, fullname), + file=sys.stderr) + import traceback + for record in traceback.format_exception(*sys.exc_info()): + for line in record.splitlines(): + print(' '+line, file=sys.stderr) + print("\nRemainder of file ignored", file=sys.stderr) + break + if reset: + known_paths = None + return known_paths + + +def addsitedir(sitedir, known_paths=None): + """Add 'sitedir' argument to sys.path if missing and handle .pth files in + 'sitedir'""" + if known_paths is None: + known_paths = _init_pathinfo() + reset = True + else: + reset = False + sitedir, sitedircase = makepath(sitedir) + if not sitedircase in known_paths: + sys.path.append(sitedir) # Add path component + known_paths.add(sitedircase) + try: + names = os.listdir(sitedir) + except OSError: + return + names = [name for name in names if name.endswith(".pth")] + for name in sorted(names): + addpackage(sitedir, name, known_paths) + if reset: + known_paths = None + return known_paths + + +def check_enableusersite(): + """Check if user site directory is safe for inclusion + + The function tests for the command line flag (including environment var), + process uid/gid equal to effective uid/gid. + + None: Disabled for security reasons + False: Disabled by user (command line option) + True: Safe and enabled + """ + if sys.flags.no_user_site: + return False + + if hasattr(os, "getuid") and hasattr(os, "geteuid"): + # check process uid == effective uid + if os.geteuid() != os.getuid(): + return None + if hasattr(os, "getgid") and hasattr(os, "getegid"): + # check process gid == effective gid + if os.getegid() != os.getgid(): + return None + + return True + + +# NOTE: sysconfig and it's dependencies are relatively large but site module +# needs very limited part of them. +# To speedup startup time, we have copy of them. +# +# See https://bugs.python.org/issue29585 + +# Copy of sysconfig._getuserbase() +def _getuserbase(): + env_base = os.environ.get("PYTHONUSERBASE", None) + if env_base: + return env_base + + def joinuser(*args): + return os.path.expanduser(os.path.join(*args)) + + if os.name == "nt": + base = os.environ.get("APPDATA") or "~" + return joinuser(base, "Python") + + if sys.platform == "darwin" and sys._framework: + return joinuser("~", "Library", sys._framework, + "%d.%d" % sys.version_info[:2]) + + return joinuser("~", ".local") + + +# Same to sysconfig.get_path('purelib', os.name+'_user') +def _get_path(userbase): + version = sys.version_info + + if os.name == 'nt': + return f'{userbase}\\Python{version[0]}{version[1]}\\site-packages' + + if sys.platform == 'darwin' and sys._framework: + return f'{userbase}/lib/python/site-packages' + + return f'{userbase}/lib/python{version[0]}.{version[1]}/site-packages' + + +def getuserbase(): + """Returns the `user base` directory path. + + The `user base` directory can be used to store data. If the global + variable ``USER_BASE`` is not initialized yet, this function will also set + it. + """ + global USER_BASE + if USER_BASE is None: + USER_BASE = _getuserbase() + return USER_BASE + + +def getusersitepackages(): + """Returns the user-specific site-packages directory path. + + If the global variable ``USER_SITE`` is not initialized yet, this + function will also set it. + """ + global USER_SITE + userbase = getuserbase() # this will also set USER_BASE + + if USER_SITE is None: + USER_SITE = _get_path(userbase) + + return USER_SITE + +def addusersitepackages(known_paths): + """Add a per user site-package to sys.path + + Each user has its own python directory with site-packages in the + home directory. + """ + # get the per user site-package path + # this call will also make sure USER_BASE and USER_SITE are set + user_site = getusersitepackages() + + if ENABLE_USER_SITE and os.path.isdir(user_site): + addsitedir(user_site, known_paths) + return known_paths + +def getsitepackages(prefixes=None): + """Returns a list containing all global site-packages directories. + + For each directory present in ``prefixes`` (or the global ``PREFIXES``), + this function will find its `site-packages` subdirectory depending on the + system environment, and will return a list of full paths. + """ + sitepackages = [] + seen = set() + + if prefixes is None: + prefixes = PREFIXES + + for prefix in prefixes: + if not prefix or prefix in seen: + continue + seen.add(prefix) + + if os.sep == '/': + sitepackages.append(os.path.join(prefix, "lib", + "python%d.%d" % sys.version_info[:2], + "site-packages")) + else: + sitepackages.append(prefix) + sitepackages.append(os.path.join(prefix, "lib", "site-packages")) + return sitepackages + +def addsitepackages(known_paths, prefixes=None): + """Add site-packages to sys.path""" + for sitedir in getsitepackages(prefixes): + if os.path.isdir(sitedir): + addsitedir(sitedir, known_paths) + + return known_paths + +def setquit(): + """Define new builtins 'quit' and 'exit'. + + These are objects which make the interpreter exit when called. + The repr of each object contains a hint at how it works. + + """ + if os.sep == '\\': + eof = 'Ctrl-Z plus Return' + else: + eof = 'Ctrl-D (i.e. EOF)' + + builtins.quit = _sitebuiltins.Quitter('quit', eof) + builtins.exit = _sitebuiltins.Quitter('exit', eof) + + +def setcopyright(): + """Set 'copyright' and 'credits' in builtins""" + builtins.copyright = _sitebuiltins._Printer("copyright", sys.copyright) + if sys.platform[:4] == 'java': + builtins.credits = _sitebuiltins._Printer( + "credits", + "Jython is maintained by the Jython developers (www.jython.org).") + else: + builtins.credits = _sitebuiltins._Printer("credits", """\ + Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands + for supporting Python development. See www.python.org for more information.""") + files, dirs = [], [] + # Not all modules are required to have a __file__ attribute. See + # PEP 420 for more details. + if hasattr(os, '__file__'): + here = os.path.dirname(os.__file__) + files.extend(["LICENSE.txt", "LICENSE"]) + dirs.extend([os.path.join(here, os.pardir), here, os.curdir]) + builtins.license = _sitebuiltins._Printer( + "license", + "See https://www.python.org/psf/license/", + files, dirs) + + +def sethelper(): + builtins.help = _sitebuiltins._Helper() + +def enablerlcompleter(): + """Enable default readline configuration on interactive prompts, by + registering a sys.__interactivehook__. + + If the readline module can be imported, the hook will set the Tab key + as completion key and register ~/.python_history as history file. + This can be overridden in the sitecustomize or usercustomize module, + or in a PYTHONSTARTUP file. + """ + def register_readline(): + import atexit + try: + import readline + import rlcompleter + except ImportError: + return + + # Reading the initialization (config) file may not be enough to set a + # completion key, so we set one first and then read the file. + readline_doc = getattr(readline, '__doc__', '') + if readline_doc is not None and 'libedit' in readline_doc: + readline.parse_and_bind('bind ^I rl_complete') + else: + readline.parse_and_bind('tab: complete') + + try: + readline.read_init_file() + except OSError: + # An OSError here could have many causes, but the most likely one + # is that there's no .inputrc file (or .editrc file in the case of + # Mac OS X + libedit) in the expected location. In that case, we + # want to ignore the exception. + pass + + if readline.get_current_history_length() == 0: + # If no history was loaded, default to .python_history. + # The guard is necessary to avoid doubling history size at + # each interpreter exit when readline was already configured + # through a PYTHONSTARTUP hook, see: + # http://bugs.python.org/issue5845#msg198636 + history = os.path.join(os.path.expanduser('~'), + '.python_history') + try: + readline.read_history_file(history) + except OSError: + pass + + def write_history(): + try: + readline.write_history_file(history) + except OSError: + # bpo-19891, bpo-41193: Home directory does not exist + # or is not writable, or the filesystem is read-only. + pass + + atexit.register(write_history) + + sys.__interactivehook__ = register_readline + +def venv(known_paths): + global PREFIXES, ENABLE_USER_SITE + + env = os.environ + if sys.platform == 'darwin' and '__PYVENV_LAUNCHER__' in env: + executable = sys._base_executable = os.environ['__PYVENV_LAUNCHER__'] + else: + executable = sys.executable + exe_dir, _ = os.path.split(os.path.abspath(executable)) + site_prefix = os.path.dirname(exe_dir) + sys._home = None + conf_basename = 'pyvenv.cfg' + candidate_confs = [ + conffile for conffile in ( + os.path.join(exe_dir, conf_basename), + os.path.join(site_prefix, conf_basename) + ) + if os.path.isfile(conffile) + ] + + if candidate_confs: + virtual_conf = candidate_confs[0] + system_site = "true" + # Issue 25185: Use UTF-8, as that's what the venv module uses when + # writing the file. + with open(virtual_conf, encoding='utf-8') as f: + for line in f: + if '=' in line: + key, _, value = line.partition('=') + key = key.strip().lower() + value = value.strip() + if key == 'include-system-site-packages': + system_site = value.lower() + elif key == 'home': + sys._home = value + + sys.prefix = sys.exec_prefix = site_prefix + + # Doing this here ensures venv takes precedence over user-site + addsitepackages(known_paths, [sys.prefix]) + + # addsitepackages will process site_prefix again if its in PREFIXES, + # but that's ok; known_paths will prevent anything being added twice + if system_site == "true": + PREFIXES.insert(0, sys.prefix) + else: + PREFIXES = [sys.prefix] + ENABLE_USER_SITE = False + + return known_paths + + +def execsitecustomize(): + """Run custom site specific code, if available.""" + try: + try: + import sitecustomize + except ImportError as exc: + if exc.name == 'sitecustomize': + pass + else: + raise + except Exception as err: + if sys.flags.verbose: + sys.excepthook(*sys.exc_info()) + else: + sys.stderr.write( + "Error in sitecustomize; set PYTHONVERBOSE for traceback:\n" + "%s: %s\n" % + (err.__class__.__name__, err)) + + +def execusercustomize(): + """Run custom user specific code, if available.""" + try: + try: + import usercustomize + except ImportError as exc: + if exc.name == 'usercustomize': + pass + else: + raise + except Exception as err: + if sys.flags.verbose: + sys.excepthook(*sys.exc_info()) + else: + sys.stderr.write( + "Error in usercustomize; set PYTHONVERBOSE for traceback:\n" + "%s: %s\n" % + (err.__class__.__name__, err)) + + +def main(): + """Add standard site-specific directories to the module search path. + + This function is called automatically when this module is imported, + unless the python interpreter was started with the -S flag. + """ + global ENABLE_USER_SITE + + orig_path = sys.path[:] + known_paths = removeduppaths() + if orig_path != sys.path: + # removeduppaths() might make sys.path absolute. + # fix __file__ and __cached__ of already imported modules too. + abs_paths() + + known_paths = venv(known_paths) + if ENABLE_USER_SITE is None: + ENABLE_USER_SITE = check_enableusersite() + known_paths = addusersitepackages(known_paths) + known_paths = addsitepackages(known_paths) + setquit() + setcopyright() + sethelper() + if not sys.flags.isolated: + enablerlcompleter() + execsitecustomize() + if ENABLE_USER_SITE: + execusercustomize() + +# Prevent extending of sys.path when python was started with -S and +# site is imported later. +if not sys.flags.no_site: + main() + +def _script(): + help = """\ + %s [--user-base] [--user-site] + + Without arguments print some useful information + With arguments print the value of USER_BASE and/or USER_SITE separated + by '%s'. + + Exit codes with --user-base or --user-site: + 0 - user site directory is enabled + 1 - user site directory is disabled by user + 2 - uses site directory is disabled by super user + or for security reasons + >2 - unknown error + """ + args = sys.argv[1:] + if not args: + user_base = getuserbase() + user_site = getusersitepackages() + print("sys.path = [") + for dir in sys.path: + print(" %r," % (dir,)) + print("]") + print("USER_BASE: %r (%s)" % (user_base, + "exists" if os.path.isdir(user_base) else "doesn't exist")) + print("USER_SITE: %r (%s)" % (user_site, + "exists" if os.path.isdir(user_site) else "doesn't exist")) + print("ENABLE_USER_SITE: %r" % ENABLE_USER_SITE) + sys.exit(0) + + buffer = [] + if '--user-base' in args: + buffer.append(USER_BASE) + if '--user-site' in args: + buffer.append(USER_SITE) + + if buffer: + print(os.pathsep.join(buffer)) + if ENABLE_USER_SITE: + sys.exit(0) + elif ENABLE_USER_SITE is False: + sys.exit(1) + elif ENABLE_USER_SITE is None: + sys.exit(2) + else: + sys.exit(3) + else: + import textwrap + print(textwrap.dedent(help % (sys.argv[0], os.pathsep))) + sys.exit(10) + +if __name__ == '__main__': + _script() diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/smtplib.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/smtplib.py new file mode 100644 index 0000000000000000000000000000000000000000..9c390db0bfa7d1d67fc0dd99e4ff9a1515b308e6 --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/smtplib.py @@ -0,0 +1,1134 @@ +#! /usr/bin/env python3 + +'''SMTP/ESMTP client class. + +This should follow RFC 821 (SMTP), RFC 1869 (ESMTP), RFC 2554 (SMTP +Authentication) and RFC 2487 (Secure SMTP over TLS). + +Notes: + +Please remember, when doing ESMTP, that the names of the SMTP service +extensions are NOT the same thing as the option keywords for the RCPT +and MAIL commands! + +Example: + + >>> import smtplib + >>> s=smtplib.SMTP("localhost") + >>> print(s.help()) + This is Sendmail version 8.8.4 + Topics: + HELO EHLO MAIL RCPT DATA + RSET NOOP QUIT HELP VRFY + EXPN VERB ETRN DSN + For more info use "HELP ". + To report bugs in the implementation send email to + sendmail-bugs@sendmail.org. + For local information send email to Postmaster at your site. + End of HELP info + >>> s.putcmd("vrfy","someone@here") + >>> s.getreply() + (250, "Somebody OverHere ") + >>> s.quit() +''' + +# Author: The Dragon De Monsyne +# ESMTP support, test code and doc fixes added by +# Eric S. Raymond +# Better RFC 821 compliance (MAIL and RCPT, and CRLF in data) +# by Carey Evans , for picky mail servers. +# RFC 2554 (authentication) support by Gerhard Haering . +# +# This was modified from the Python 1.5 library HTTP lib. + +import socket +import io +import re +import email.utils +import email.message +import email.generator +import base64 +import hmac +import copy +import datetime +import sys +from email.base64mime import body_encode as encode_base64 + +__all__ = ["SMTPException", "SMTPNotSupportedError", "SMTPServerDisconnected", "SMTPResponseException", + "SMTPSenderRefused", "SMTPRecipientsRefused", "SMTPDataError", + "SMTPConnectError", "SMTPHeloError", "SMTPAuthenticationError", + "quoteaddr", "quotedata", "SMTP"] + +SMTP_PORT = 25 +SMTP_SSL_PORT = 465 +CRLF = "\r\n" +bCRLF = b"\r\n" +_MAXLINE = 8192 # more than 8 times larger than RFC 821, 4.5.3 +_MAXCHALLENGE = 5 # Maximum number of AUTH challenges sent + +OLDSTYLE_AUTH = re.compile(r"auth=(.*)", re.I) + +# Exception classes used by this module. +class SMTPException(OSError): + """Base class for all exceptions raised by this module.""" + +class SMTPNotSupportedError(SMTPException): + """The command or option is not supported by the SMTP server. + + This exception is raised when an attempt is made to run a command or a + command with an option which is not supported by the server. + """ + +class SMTPServerDisconnected(SMTPException): + """Not connected to any SMTP server. + + This exception is raised when the server unexpectedly disconnects, + or when an attempt is made to use the SMTP instance before + connecting it to a server. + """ + +class SMTPResponseException(SMTPException): + """Base class for all exceptions that include an SMTP error code. + + These exceptions are generated in some instances when the SMTP + server returns an error code. The error code is stored in the + `smtp_code' attribute of the error, and the `smtp_error' attribute + is set to the error message. + """ + + def __init__(self, code, msg): + self.smtp_code = code + self.smtp_error = msg + self.args = (code, msg) + +class SMTPSenderRefused(SMTPResponseException): + """Sender address refused. + + In addition to the attributes set by on all SMTPResponseException + exceptions, this sets `sender' to the string that the SMTP refused. + """ + + def __init__(self, code, msg, sender): + self.smtp_code = code + self.smtp_error = msg + self.sender = sender + self.args = (code, msg, sender) + +class SMTPRecipientsRefused(SMTPException): + """All recipient addresses refused. + + The errors for each recipient are accessible through the attribute + 'recipients', which is a dictionary of exactly the same sort as + SMTP.sendmail() returns. + """ + + def __init__(self, recipients): + self.recipients = recipients + self.args = (recipients,) + + +class SMTPDataError(SMTPResponseException): + """The SMTP server didn't accept the data.""" + +class SMTPConnectError(SMTPResponseException): + """Error during connection establishment.""" + +class SMTPHeloError(SMTPResponseException): + """The server refused our HELO reply.""" + +class SMTPAuthenticationError(SMTPResponseException): + """Authentication error. + + Most probably the server didn't accept the username/password + combination provided. + """ + +def quoteaddr(addrstring): + """Quote a subset of the email addresses defined by RFC 821. + + Should be able to handle anything email.utils.parseaddr can handle. + """ + displayname, addr = email.utils.parseaddr(addrstring) + if (displayname, addr) == ('', ''): + # parseaddr couldn't parse it, use it as is and hope for the best. + if addrstring.strip().startswith('<'): + return addrstring + return "<%s>" % addrstring + return "<%s>" % addr + +def _addr_only(addrstring): + displayname, addr = email.utils.parseaddr(addrstring) + if (displayname, addr) == ('', ''): + # parseaddr couldn't parse it, so use it as is. + return addrstring + return addr + +# Legacy method kept for backward compatibility. +def quotedata(data): + """Quote data for email. + + Double leading '.', and change Unix newline '\\n', or Mac '\\r' into + Internet CRLF end-of-line. + """ + return re.sub(r'(?m)^\.', '..', + re.sub(r'(?:\r\n|\n|\r(?!\n))', CRLF, data)) + +def _quote_periods(bindata): + return re.sub(br'(?m)^\.', b'..', bindata) + +def _fix_eols(data): + return re.sub(r'(?:\r\n|\n|\r(?!\n))', CRLF, data) + +try: + import ssl +except ImportError: + _have_ssl = False +else: + _have_ssl = True + + +class SMTP: + """This class manages a connection to an SMTP or ESMTP server. + SMTP Objects: + SMTP objects have the following attributes: + helo_resp + This is the message given by the server in response to the + most recent HELO command. + + ehlo_resp + This is the message given by the server in response to the + most recent EHLO command. This is usually multiline. + + does_esmtp + This is a True value _after you do an EHLO command_, if the + server supports ESMTP. + + esmtp_features + This is a dictionary, which, if the server supports ESMTP, + will _after you do an EHLO command_, contain the names of the + SMTP service extensions this server supports, and their + parameters (if any). + + Note, all extension names are mapped to lower case in the + dictionary. + + See each method's docstrings for details. In general, there is a + method of the same name to perform each SMTP command. There is also a + method called 'sendmail' that will do an entire mail transaction. + """ + debuglevel = 0 + + sock = None + file = None + helo_resp = None + ehlo_msg = "ehlo" + ehlo_resp = None + does_esmtp = 0 + default_port = SMTP_PORT + + def __init__(self, host='', port=0, local_hostname=None, + timeout=socket._GLOBAL_DEFAULT_TIMEOUT, + source_address=None): + """Initialize a new instance. + + If specified, `host` is the name of the remote host to which to + connect. If specified, `port` specifies the port to which to connect. + By default, smtplib.SMTP_PORT is used. If a host is specified the + connect method is called, and if it returns anything other than a + success code an SMTPConnectError is raised. If specified, + `local_hostname` is used as the FQDN of the local host in the HELO/EHLO + command. Otherwise, the local hostname is found using + socket.getfqdn(). The `source_address` parameter takes a 2-tuple (host, + port) for the socket to bind to as its source address before + connecting. If the host is '' and port is 0, the OS default behavior + will be used. + + """ + self._host = host + self.timeout = timeout + self.esmtp_features = {} + self.command_encoding = 'ascii' + self.source_address = source_address + self._auth_challenge_count = 0 + + if host: + (code, msg) = self.connect(host, port) + if code != 220: + self.close() + raise SMTPConnectError(code, msg) + if local_hostname is not None: + self.local_hostname = local_hostname + else: + # RFC 2821 says we should use the fqdn in the EHLO/HELO verb, and + # if that can't be calculated, that we should use a domain literal + # instead (essentially an encoded IP address like [A.B.C.D]). + fqdn = socket.getfqdn() + if '.' in fqdn: + self.local_hostname = fqdn + else: + # We can't find an fqdn hostname, so use a domain literal + addr = '127.0.0.1' + try: + addr = socket.gethostbyname(socket.gethostname()) + except socket.gaierror: + pass + self.local_hostname = '[%s]' % addr + + def __enter__(self): + return self + + def __exit__(self, *args): + try: + code, message = self.docmd("QUIT") + if code != 221: + raise SMTPResponseException(code, message) + except SMTPServerDisconnected: + pass + finally: + self.close() + + def set_debuglevel(self, debuglevel): + """Set the debug output level. + + A non-false value results in debug messages for connection and for all + messages sent to and received from the server. + + """ + self.debuglevel = debuglevel + + def _print_debug(self, *args): + if self.debuglevel > 1: + print(datetime.datetime.now().time(), *args, file=sys.stderr) + else: + print(*args, file=sys.stderr) + + def _get_socket(self, host, port, timeout): + # This makes it simpler for SMTP_SSL to use the SMTP connect code + # and just alter the socket connection bit. + if self.debuglevel > 0: + self._print_debug('connect: to', (host, port), self.source_address) + return socket.create_connection((host, port), timeout, + self.source_address) + + def connect(self, host='localhost', port=0, source_address=None): + """Connect to a host on a given port. + + If the hostname ends with a colon (`:') followed by a number, and + there is no port specified, that suffix will be stripped off and the + number interpreted as the port number to use. + + Note: This method is automatically invoked by __init__, if a host is + specified during instantiation. + + """ + + if source_address: + self.source_address = source_address + + if not port and (host.find(':') == host.rfind(':')): + i = host.rfind(':') + if i >= 0: + host, port = host[:i], host[i + 1:] + try: + port = int(port) + except ValueError: + raise OSError("nonnumeric port") + if not port: + port = self.default_port + sys.audit("smtplib.connect", self, host, port) + self.sock = self._get_socket(host, port, self.timeout) + self.file = None + (code, msg) = self.getreply() + if self.debuglevel > 0: + self._print_debug('connect:', repr(msg)) + return (code, msg) + + def send(self, s): + """Send `s' to the server.""" + if self.debuglevel > 0: + self._print_debug('send:', repr(s)) + if self.sock: + if isinstance(s, str): + # send is used by the 'data' command, where command_encoding + # should not be used, but 'data' needs to convert the string to + # binary itself anyway, so that's not a problem. + s = s.encode(self.command_encoding) + sys.audit("smtplib.send", self, s) + try: + self.sock.sendall(s) + except OSError: + self.close() + raise SMTPServerDisconnected('Server not connected') + else: + raise SMTPServerDisconnected('please run connect() first') + + def putcmd(self, cmd, args=""): + """Send a command to the server.""" + if args == "": + s = cmd + else: + s = f'{cmd} {args}' + if '\r' in s or '\n' in s: + s = s.replace('\n', '\\n').replace('\r', '\\r') + raise ValueError( + f'command and arguments contain prohibited newline characters: {s}' + ) + self.send(f'{s}{CRLF}') + + def getreply(self): + """Get a reply from the server. + + Returns a tuple consisting of: + + - server response code (e.g. '250', or such, if all goes well) + Note: returns -1 if it can't read response code. + + - server response string corresponding to response code (multiline + responses are converted to a single, multiline string). + + Raises SMTPServerDisconnected if end-of-file is reached. + """ + resp = [] + if self.file is None: + self.file = self.sock.makefile('rb') + while 1: + try: + line = self.file.readline(_MAXLINE + 1) + except OSError as e: + self.close() + raise SMTPServerDisconnected("Connection unexpectedly closed: " + + str(e)) + if not line: + self.close() + raise SMTPServerDisconnected("Connection unexpectedly closed") + if self.debuglevel > 0: + self._print_debug('reply:', repr(line)) + if len(line) > _MAXLINE: + self.close() + raise SMTPResponseException(500, "Line too long.") + resp.append(line[4:].strip(b' \t\r\n')) + code = line[:3] + # Check that the error code is syntactically correct. + # Don't attempt to read a continuation line if it is broken. + try: + errcode = int(code) + except ValueError: + errcode = -1 + break + # Check if multiline response. + if line[3:4] != b"-": + break + + errmsg = b"\n".join(resp) + if self.debuglevel > 0: + self._print_debug('reply: retcode (%s); Msg: %a' % (errcode, errmsg)) + return errcode, errmsg + + def docmd(self, cmd, args=""): + """Send a command, and return its response code.""" + self.putcmd(cmd, args) + return self.getreply() + + # std smtp commands + def helo(self, name=''): + """SMTP 'helo' command. + Hostname to send for this command defaults to the FQDN of the local + host. + """ + self.putcmd("helo", name or self.local_hostname) + (code, msg) = self.getreply() + self.helo_resp = msg + return (code, msg) + + def ehlo(self, name=''): + """ SMTP 'ehlo' command. + Hostname to send for this command defaults to the FQDN of the local + host. + """ + self.esmtp_features = {} + self.putcmd(self.ehlo_msg, name or self.local_hostname) + (code, msg) = self.getreply() + # According to RFC1869 some (badly written) + # MTA's will disconnect on an ehlo. Toss an exception if + # that happens -ddm + if code == -1 and len(msg) == 0: + self.close() + raise SMTPServerDisconnected("Server not connected") + self.ehlo_resp = msg + if code != 250: + return (code, msg) + self.does_esmtp = 1 + #parse the ehlo response -ddm + assert isinstance(self.ehlo_resp, bytes), repr(self.ehlo_resp) + resp = self.ehlo_resp.decode("latin-1").split('\n') + del resp[0] + for each in resp: + # To be able to communicate with as many SMTP servers as possible, + # we have to take the old-style auth advertisement into account, + # because: + # 1) Else our SMTP feature parser gets confused. + # 2) There are some servers that only advertise the auth methods we + # support using the old style. + auth_match = OLDSTYLE_AUTH.match(each) + if auth_match: + # This doesn't remove duplicates, but that's no problem + self.esmtp_features["auth"] = self.esmtp_features.get("auth", "") \ + + " " + auth_match.groups(0)[0] + continue + + # RFC 1869 requires a space between ehlo keyword and parameters. + # It's actually stricter, in that only spaces are allowed between + # parameters, but were not going to check for that here. Note + # that the space isn't present if there are no parameters. + m = re.match(r'(?P[A-Za-z0-9][A-Za-z0-9\-]*) ?', each) + if m: + feature = m.group("feature").lower() + params = m.string[m.end("feature"):].strip() + if feature == "auth": + self.esmtp_features[feature] = self.esmtp_features.get(feature, "") \ + + " " + params + else: + self.esmtp_features[feature] = params + return (code, msg) + + def has_extn(self, opt): + """Does the server support a given SMTP service extension?""" + return opt.lower() in self.esmtp_features + + def help(self, args=''): + """SMTP 'help' command. + Returns help text from server.""" + self.putcmd("help", args) + return self.getreply()[1] + + def rset(self): + """SMTP 'rset' command -- resets session.""" + self.command_encoding = 'ascii' + return self.docmd("rset") + + def _rset(self): + """Internal 'rset' command which ignores any SMTPServerDisconnected error. + + Used internally in the library, since the server disconnected error + should appear to the application when the *next* command is issued, if + we are doing an internal "safety" reset. + """ + try: + self.rset() + except SMTPServerDisconnected: + pass + + def noop(self): + """SMTP 'noop' command -- doesn't do anything :>""" + return self.docmd("noop") + + def mail(self, sender, options=()): + """SMTP 'mail' command -- begins mail xfer session. + + This method may raise the following exceptions: + + SMTPNotSupportedError The options parameter includes 'SMTPUTF8' + but the SMTPUTF8 extension is not supported by + the server. + """ + optionlist = '' + if options and self.does_esmtp: + if any(x.lower()=='smtputf8' for x in options): + if self.has_extn('smtputf8'): + self.command_encoding = 'utf-8' + else: + raise SMTPNotSupportedError( + 'SMTPUTF8 not supported by server') + optionlist = ' ' + ' '.join(options) + self.putcmd("mail", "FROM:%s%s" % (quoteaddr(sender), optionlist)) + return self.getreply() + + def rcpt(self, recip, options=()): + """SMTP 'rcpt' command -- indicates 1 recipient for this mail.""" + optionlist = '' + if options and self.does_esmtp: + optionlist = ' ' + ' '.join(options) + self.putcmd("rcpt", "TO:%s%s" % (quoteaddr(recip), optionlist)) + return self.getreply() + + def data(self, msg): + """SMTP 'DATA' command -- sends message data to server. + + Automatically quotes lines beginning with a period per rfc821. + Raises SMTPDataError if there is an unexpected reply to the + DATA command; the return value from this method is the final + response code received when the all data is sent. If msg + is a string, lone '\\r' and '\\n' characters are converted to + '\\r\\n' characters. If msg is bytes, it is transmitted as is. + """ + self.putcmd("data") + (code, repl) = self.getreply() + if self.debuglevel > 0: + self._print_debug('data:', (code, repl)) + if code != 354: + raise SMTPDataError(code, repl) + else: + if isinstance(msg, str): + msg = _fix_eols(msg).encode('ascii') + q = _quote_periods(msg) + if q[-2:] != bCRLF: + q = q + bCRLF + q = q + b"." + bCRLF + self.send(q) + (code, msg) = self.getreply() + if self.debuglevel > 0: + self._print_debug('data:', (code, msg)) + return (code, msg) + + def verify(self, address): + """SMTP 'verify' command -- checks for address validity.""" + self.putcmd("vrfy", _addr_only(address)) + return self.getreply() + # a.k.a. + vrfy = verify + + def expn(self, address): + """SMTP 'expn' command -- expands a mailing list.""" + self.putcmd("expn", _addr_only(address)) + return self.getreply() + + # some useful methods + + def ehlo_or_helo_if_needed(self): + """Call self.ehlo() and/or self.helo() if needed. + + If there has been no previous EHLO or HELO command this session, this + method tries ESMTP EHLO first. + + This method may raise the following exceptions: + + SMTPHeloError The server didn't reply properly to + the helo greeting. + """ + if self.helo_resp is None and self.ehlo_resp is None: + if not (200 <= self.ehlo()[0] <= 299): + (code, resp) = self.helo() + if not (200 <= code <= 299): + raise SMTPHeloError(code, resp) + + def auth(self, mechanism, authobject, *, initial_response_ok=True): + """Authentication command - requires response processing. + + 'mechanism' specifies which authentication mechanism is to + be used - the valid values are those listed in the 'auth' + element of 'esmtp_features'. + + 'authobject' must be a callable object taking a single argument: + + data = authobject(challenge) + + It will be called to process the server's challenge response; the + challenge argument it is passed will be a bytes. It should return + an ASCII string that will be base64 encoded and sent to the server. + + Keyword arguments: + - initial_response_ok: Allow sending the RFC 4954 initial-response + to the AUTH command, if the authentication methods supports it. + """ + # RFC 4954 allows auth methods to provide an initial response. Not all + # methods support it. By definition, if they return something other + # than None when challenge is None, then they do. See issue #15014. + mechanism = mechanism.upper() + initial_response = (authobject() if initial_response_ok else None) + if initial_response is not None: + response = encode_base64(initial_response.encode('ascii'), eol='') + (code, resp) = self.docmd("AUTH", mechanism + " " + response) + self._auth_challenge_count = 1 + else: + (code, resp) = self.docmd("AUTH", mechanism) + self._auth_challenge_count = 0 + # If server responds with a challenge, send the response. + while code == 334: + self._auth_challenge_count += 1 + challenge = base64.decodebytes(resp) + response = encode_base64( + authobject(challenge).encode('ascii'), eol='') + (code, resp) = self.docmd(response) + # If server keeps sending challenges, something is wrong. + if self._auth_challenge_count > _MAXCHALLENGE: + raise SMTPException( + "Server AUTH mechanism infinite loop. Last response: " + + repr((code, resp)) + ) + if code in (235, 503): + return (code, resp) + raise SMTPAuthenticationError(code, resp) + + def auth_cram_md5(self, challenge=None): + """ Authobject to use with CRAM-MD5 authentication. Requires self.user + and self.password to be set.""" + # CRAM-MD5 does not support initial-response. + if challenge is None: + return None + return self.user + " " + hmac.HMAC( + self.password.encode('ascii'), challenge, 'md5').hexdigest() + + def auth_plain(self, challenge=None): + """ Authobject to use with PLAIN authentication. Requires self.user and + self.password to be set.""" + return "\0%s\0%s" % (self.user, self.password) + + def auth_login(self, challenge=None): + """ Authobject to use with LOGIN authentication. Requires self.user and + self.password to be set.""" + if challenge is None or self._auth_challenge_count < 2: + return self.user + else: + return self.password + + def login(self, user, password, *, initial_response_ok=True): + """Log in on an SMTP server that requires authentication. + + The arguments are: + - user: The user name to authenticate with. + - password: The password for the authentication. + + Keyword arguments: + - initial_response_ok: Allow sending the RFC 4954 initial-response + to the AUTH command, if the authentication methods supports it. + + If there has been no previous EHLO or HELO command this session, this + method tries ESMTP EHLO first. + + This method will return normally if the authentication was successful. + + This method may raise the following exceptions: + + SMTPHeloError The server didn't reply properly to + the helo greeting. + SMTPAuthenticationError The server didn't accept the username/ + password combination. + SMTPNotSupportedError The AUTH command is not supported by the + server. + SMTPException No suitable authentication method was + found. + """ + + self.ehlo_or_helo_if_needed() + if not self.has_extn("auth"): + raise SMTPNotSupportedError( + "SMTP AUTH extension not supported by server.") + + # Authentication methods the server claims to support + advertised_authlist = self.esmtp_features["auth"].split() + + # Authentication methods we can handle in our preferred order: + preferred_auths = ['CRAM-MD5', 'PLAIN', 'LOGIN'] + + # We try the supported authentications in our preferred order, if + # the server supports them. + authlist = [auth for auth in preferred_auths + if auth in advertised_authlist] + if not authlist: + raise SMTPException("No suitable authentication method found.") + + # Some servers advertise authentication methods they don't really + # support, so if authentication fails, we continue until we've tried + # all methods. + self.user, self.password = user, password + for authmethod in authlist: + method_name = 'auth_' + authmethod.lower().replace('-', '_') + try: + (code, resp) = self.auth( + authmethod, getattr(self, method_name), + initial_response_ok=initial_response_ok) + # 235 == 'Authentication successful' + # 503 == 'Error: already authenticated' + if code in (235, 503): + return (code, resp) + except SMTPAuthenticationError as e: + last_exception = e + + # We could not login successfully. Return result of last attempt. + raise last_exception + + def starttls(self, keyfile=None, certfile=None, context=None): + """Puts the connection to the SMTP server into TLS mode. + + If there has been no previous EHLO or HELO command this session, this + method tries ESMTP EHLO first. + + If the server supports TLS, this will encrypt the rest of the SMTP + session. If you provide the keyfile and certfile parameters, + the identity of the SMTP server and client can be checked. This, + however, depends on whether the socket module really checks the + certificates. + + This method may raise the following exceptions: + + SMTPHeloError The server didn't reply properly to + the helo greeting. + """ + self.ehlo_or_helo_if_needed() + if not self.has_extn("starttls"): + raise SMTPNotSupportedError( + "STARTTLS extension not supported by server.") + (resp, reply) = self.docmd("STARTTLS") + if resp == 220: + if not _have_ssl: + raise RuntimeError("No SSL support included in this Python") + if context is not None and keyfile is not None: + raise ValueError("context and keyfile arguments are mutually " + "exclusive") + if context is not None and certfile is not None: + raise ValueError("context and certfile arguments are mutually " + "exclusive") + if keyfile is not None or certfile is not None: + import warnings + warnings.warn("keyfile and certfile are deprecated, use a " + "custom context instead", DeprecationWarning, 2) + if context is None: + context = ssl._create_stdlib_context(certfile=certfile, + keyfile=keyfile) + self.sock = context.wrap_socket(self.sock, + server_hostname=self._host) + self.file = None + # RFC 3207: + # The client MUST discard any knowledge obtained from + # the server, such as the list of SMTP service extensions, + # which was not obtained from the TLS negotiation itself. + self.helo_resp = None + self.ehlo_resp = None + self.esmtp_features = {} + self.does_esmtp = 0 + else: + # RFC 3207: + # 501 Syntax error (no parameters allowed) + # 454 TLS not available due to temporary reason + raise SMTPResponseException(resp, reply) + return (resp, reply) + + def sendmail(self, from_addr, to_addrs, msg, mail_options=(), + rcpt_options=()): + """This command performs an entire mail transaction. + + The arguments are: + - from_addr : The address sending this mail. + - to_addrs : A list of addresses to send this mail to. A bare + string will be treated as a list with 1 address. + - msg : The message to send. + - mail_options : List of ESMTP options (such as 8bitmime) for the + mail command. + - rcpt_options : List of ESMTP options (such as DSN commands) for + all the rcpt commands. + + msg may be a string containing characters in the ASCII range, or a byte + string. A string is encoded to bytes using the ascii codec, and lone + \\r and \\n characters are converted to \\r\\n characters. + + If there has been no previous EHLO or HELO command this session, this + method tries ESMTP EHLO first. If the server does ESMTP, message size + and each of the specified options will be passed to it. If EHLO + fails, HELO will be tried and ESMTP options suppressed. + + This method will return normally if the mail is accepted for at least + one recipient. It returns a dictionary, with one entry for each + recipient that was refused. Each entry contains a tuple of the SMTP + error code and the accompanying error message sent by the server. + + This method may raise the following exceptions: + + SMTPHeloError The server didn't reply properly to + the helo greeting. + SMTPRecipientsRefused The server rejected ALL recipients + (no mail was sent). + SMTPSenderRefused The server didn't accept the from_addr. + SMTPDataError The server replied with an unexpected + error code (other than a refusal of + a recipient). + SMTPNotSupportedError The mail_options parameter includes 'SMTPUTF8' + but the SMTPUTF8 extension is not supported by + the server. + + Note: the connection will be open even after an exception is raised. + + Example: + + >>> import smtplib + >>> s=smtplib.SMTP("localhost") + >>> tolist=["one@one.org","two@two.org","three@three.org","four@four.org"] + >>> msg = '''\\ + ... From: Me@my.org + ... Subject: testin'... + ... + ... This is a test ''' + >>> s.sendmail("me@my.org",tolist,msg) + { "three@three.org" : ( 550 ,"User unknown" ) } + >>> s.quit() + + In the above example, the message was accepted for delivery to three + of the four addresses, and one was rejected, with the error code + 550. If all addresses are accepted, then the method will return an + empty dictionary. + + """ + self.ehlo_or_helo_if_needed() + esmtp_opts = [] + if isinstance(msg, str): + msg = _fix_eols(msg).encode('ascii') + if self.does_esmtp: + if self.has_extn('size'): + esmtp_opts.append("size=%d" % len(msg)) + for option in mail_options: + esmtp_opts.append(option) + (code, resp) = self.mail(from_addr, esmtp_opts) + if code != 250: + if code == 421: + self.close() + else: + self._rset() + raise SMTPSenderRefused(code, resp, from_addr) + senderrs = {} + if isinstance(to_addrs, str): + to_addrs = [to_addrs] + for each in to_addrs: + (code, resp) = self.rcpt(each, rcpt_options) + if (code != 250) and (code != 251): + senderrs[each] = (code, resp) + if code == 421: + self.close() + raise SMTPRecipientsRefused(senderrs) + if len(senderrs) == len(to_addrs): + # the server refused all our recipients + self._rset() + raise SMTPRecipientsRefused(senderrs) + (code, resp) = self.data(msg) + if code != 250: + if code == 421: + self.close() + else: + self._rset() + raise SMTPDataError(code, resp) + #if we got here then somebody got our mail + return senderrs + + def send_message(self, msg, from_addr=None, to_addrs=None, + mail_options=(), rcpt_options=()): + """Converts message to a bytestring and passes it to sendmail. + + The arguments are as for sendmail, except that msg is an + email.message.Message object. If from_addr is None or to_addrs is + None, these arguments are taken from the headers of the Message as + described in RFC 2822 (a ValueError is raised if there is more than + one set of 'Resent-' headers). Regardless of the values of from_addr and + to_addr, any Bcc field (or Resent-Bcc field, when the Message is a + resent) of the Message object won't be transmitted. The Message + object is then serialized using email.generator.BytesGenerator and + sendmail is called to transmit the message. If the sender or any of + the recipient addresses contain non-ASCII and the server advertises the + SMTPUTF8 capability, the policy is cloned with utf8 set to True for the + serialization, and SMTPUTF8 and BODY=8BITMIME are asserted on the send. + If the server does not support SMTPUTF8, an SMTPNotSupported error is + raised. Otherwise the generator is called without modifying the + policy. + + """ + # 'Resent-Date' is a mandatory field if the Message is resent (RFC 2822 + # Section 3.6.6). In such a case, we use the 'Resent-*' fields. However, + # if there is more than one 'Resent-' block there's no way to + # unambiguously determine which one is the most recent in all cases, + # so rather than guess we raise a ValueError in that case. + # + # TODO implement heuristics to guess the correct Resent-* block with an + # option allowing the user to enable the heuristics. (It should be + # possible to guess correctly almost all of the time.) + + self.ehlo_or_helo_if_needed() + resent = msg.get_all('Resent-Date') + if resent is None: + header_prefix = '' + elif len(resent) == 1: + header_prefix = 'Resent-' + else: + raise ValueError("message has more than one 'Resent-' header block") + if from_addr is None: + # Prefer the sender field per RFC 2822:3.6.2. + from_addr = (msg[header_prefix + 'Sender'] + if (header_prefix + 'Sender') in msg + else msg[header_prefix + 'From']) + from_addr = email.utils.getaddresses([from_addr])[0][1] + if to_addrs is None: + addr_fields = [f for f in (msg[header_prefix + 'To'], + msg[header_prefix + 'Bcc'], + msg[header_prefix + 'Cc']) + if f is not None] + to_addrs = [a[1] for a in email.utils.getaddresses(addr_fields)] + # Make a local copy so we can delete the bcc headers. + msg_copy = copy.copy(msg) + del msg_copy['Bcc'] + del msg_copy['Resent-Bcc'] + international = False + try: + ''.join([from_addr, *to_addrs]).encode('ascii') + except UnicodeEncodeError: + if not self.has_extn('smtputf8'): + raise SMTPNotSupportedError( + "One or more source or delivery addresses require" + " internationalized email support, but the server" + " does not advertise the required SMTPUTF8 capability") + international = True + with io.BytesIO() as bytesmsg: + if international: + g = email.generator.BytesGenerator( + bytesmsg, policy=msg.policy.clone(utf8=True)) + mail_options = (*mail_options, 'SMTPUTF8', 'BODY=8BITMIME') + else: + g = email.generator.BytesGenerator(bytesmsg) + g.flatten(msg_copy, linesep='\r\n') + flatmsg = bytesmsg.getvalue() + return self.sendmail(from_addr, to_addrs, flatmsg, mail_options, + rcpt_options) + + def close(self): + """Close the connection to the SMTP server.""" + try: + file = self.file + self.file = None + if file: + file.close() + finally: + sock = self.sock + self.sock = None + if sock: + sock.close() + + def quit(self): + """Terminate the SMTP session.""" + res = self.docmd("quit") + # A new EHLO is required after reconnecting with connect() + self.ehlo_resp = self.helo_resp = None + self.esmtp_features = {} + self.does_esmtp = False + self.close() + return res + +if _have_ssl: + + class SMTP_SSL(SMTP): + """ This is a subclass derived from SMTP that connects over an SSL + encrypted socket (to use this class you need a socket module that was + compiled with SSL support). If host is not specified, '' (the local + host) is used. If port is omitted, the standard SMTP-over-SSL port + (465) is used. local_hostname and source_address have the same meaning + as they do in the SMTP class. keyfile and certfile are also optional - + they can contain a PEM formatted private key and certificate chain file + for the SSL connection. context also optional, can contain a + SSLContext, and is an alternative to keyfile and certfile; If it is + specified both keyfile and certfile must be None. + + """ + + default_port = SMTP_SSL_PORT + + def __init__(self, host='', port=0, local_hostname=None, + keyfile=None, certfile=None, + timeout=socket._GLOBAL_DEFAULT_TIMEOUT, + source_address=None, context=None): + if context is not None and keyfile is not None: + raise ValueError("context and keyfile arguments are mutually " + "exclusive") + if context is not None and certfile is not None: + raise ValueError("context and certfile arguments are mutually " + "exclusive") + if keyfile is not None or certfile is not None: + import warnings + warnings.warn("keyfile and certfile are deprecated, use a " + "custom context instead", DeprecationWarning, 2) + self.keyfile = keyfile + self.certfile = certfile + if context is None: + context = ssl._create_stdlib_context(certfile=certfile, + keyfile=keyfile) + self.context = context + SMTP.__init__(self, host, port, local_hostname, timeout, + source_address) + + def _get_socket(self, host, port, timeout): + if self.debuglevel > 0: + self._print_debug('connect:', (host, port)) + new_socket = socket.create_connection((host, port), timeout, + self.source_address) + new_socket = self.context.wrap_socket(new_socket, + server_hostname=self._host) + return new_socket + + __all__.append("SMTP_SSL") + +# +# LMTP extension +# +LMTP_PORT = 2003 + +class LMTP(SMTP): + """LMTP - Local Mail Transfer Protocol + + The LMTP protocol, which is very similar to ESMTP, is heavily based + on the standard SMTP client. It's common to use Unix sockets for + LMTP, so our connect() method must support that as well as a regular + host:port server. local_hostname and source_address have the same + meaning as they do in the SMTP class. To specify a Unix socket, + you must use an absolute path as the host, starting with a '/'. + + Authentication is supported, using the regular SMTP mechanism. When + using a Unix socket, LMTP generally don't support or require any + authentication, but your mileage might vary.""" + + ehlo_msg = "lhlo" + + def __init__(self, host='', port=LMTP_PORT, local_hostname=None, + source_address=None): + """Initialize a new instance.""" + SMTP.__init__(self, host, port, local_hostname=local_hostname, + source_address=source_address) + + def connect(self, host='localhost', port=0, source_address=None): + """Connect to the LMTP daemon, on either a Unix or a TCP socket.""" + if host[0] != '/': + return SMTP.connect(self, host, port, source_address=source_address) + + # Handle Unix-domain sockets. + try: + self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + self.file = None + self.sock.connect(host) + except OSError: + if self.debuglevel > 0: + self._print_debug('connect fail:', host) + if self.sock: + self.sock.close() + self.sock = None + raise + (code, msg) = self.getreply() + if self.debuglevel > 0: + self._print_debug('connect:', msg) + return (code, msg) + + +# Test the sendmail method, which tests most of the others. +# Note: This always sends to localhost. +if __name__ == '__main__': + def prompt(prompt): + sys.stdout.write(prompt + ": ") + sys.stdout.flush() + return sys.stdin.readline().strip() + + fromaddr = prompt("From") + toaddrs = prompt("To").split(',') + print("Enter message, end with ^D:") + msg = '' + while 1: + line = sys.stdin.readline() + if not line: + break + msg = msg + line + print("Message length is %d" % len(msg)) + + server = SMTP('localhost') + server.set_debuglevel(1) + server.sendmail(fromaddr, toaddrs, msg) + server.quit() diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/sndhdr.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/sndhdr.py new file mode 100644 index 0000000000000000000000000000000000000000..96595c6974468213e0a93414af95f4981bb609c5 --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/sndhdr.py @@ -0,0 +1,257 @@ +"""Routines to help recognizing sound files. + +Function whathdr() recognizes various types of sound file headers. +It understands almost all headers that SOX can decode. + +The return tuple contains the following items, in this order: +- file type (as SOX understands it) +- sampling rate (0 if unknown or hard to decode) +- number of channels (0 if unknown or hard to decode) +- number of frames in the file (-1 if unknown or hard to decode) +- number of bits/sample, or 'U' for U-LAW, or 'A' for A-LAW + +If the file doesn't have a recognizable type, it returns None. +If the file can't be opened, OSError is raised. + +To compute the total time, divide the number of frames by the +sampling rate (a frame contains a sample for each channel). + +Function what() calls whathdr(). (It used to also use some +heuristics for raw data, but this doesn't work very well.) + +Finally, the function test() is a simple main program that calls +what() for all files mentioned on the argument list. For directory +arguments it calls what() for all files in that directory. Default +argument is "." (testing all files in the current directory). The +option -r tells it to recurse down directories found inside +explicitly given directories. +""" + +# The file structure is top-down except that the test program and its +# subroutine come last. + +__all__ = ['what', 'whathdr'] + +from collections import namedtuple + +SndHeaders = namedtuple('SndHeaders', + 'filetype framerate nchannels nframes sampwidth') + +SndHeaders.filetype.__doc__ = ("""The value for type indicates the data type +and will be one of the strings 'aifc', 'aiff', 'au','hcom', +'sndr', 'sndt', 'voc', 'wav', '8svx', 'sb', 'ub', or 'ul'.""") +SndHeaders.framerate.__doc__ = ("""The sampling_rate will be either the actual +value or 0 if unknown or difficult to decode.""") +SndHeaders.nchannels.__doc__ = ("""The number of channels or 0 if it cannot be +determined or if the value is difficult to decode.""") +SndHeaders.nframes.__doc__ = ("""The value for frames will be either the number +of frames or -1.""") +SndHeaders.sampwidth.__doc__ = ("""Either the sample size in bits or +'A' for A-LAW or 'U' for u-LAW.""") + +def what(filename): + """Guess the type of a sound file.""" + res = whathdr(filename) + return res + + +def whathdr(filename): + """Recognize sound headers.""" + with open(filename, 'rb') as f: + h = f.read(512) + for tf in tests: + res = tf(h, f) + if res: + return SndHeaders(*res) + return None + + +#-----------------------------------# +# Subroutines per sound header type # +#-----------------------------------# + +tests = [] + +def test_aifc(h, f): + import aifc + if not h.startswith(b'FORM'): + return None + if h[8:12] == b'AIFC': + fmt = 'aifc' + elif h[8:12] == b'AIFF': + fmt = 'aiff' + else: + return None + f.seek(0) + try: + a = aifc.open(f, 'r') + except (EOFError, aifc.Error): + return None + return (fmt, a.getframerate(), a.getnchannels(), + a.getnframes(), 8 * a.getsampwidth()) + +tests.append(test_aifc) + + +def test_au(h, f): + if h.startswith(b'.snd'): + func = get_long_be + elif h[:4] in (b'\0ds.', b'dns.'): + func = get_long_le + else: + return None + filetype = 'au' + hdr_size = func(h[4:8]) + data_size = func(h[8:12]) + encoding = func(h[12:16]) + rate = func(h[16:20]) + nchannels = func(h[20:24]) + sample_size = 1 # default + if encoding == 1: + sample_bits = 'U' + elif encoding == 2: + sample_bits = 8 + elif encoding == 3: + sample_bits = 16 + sample_size = 2 + else: + sample_bits = '?' + frame_size = sample_size * nchannels + if frame_size: + nframe = data_size / frame_size + else: + nframe = -1 + return filetype, rate, nchannels, nframe, sample_bits + +tests.append(test_au) + + +def test_hcom(h, f): + if h[65:69] != b'FSSD' or h[128:132] != b'HCOM': + return None + divisor = get_long_be(h[144:148]) + if divisor: + rate = 22050 / divisor + else: + rate = 0 + return 'hcom', rate, 1, -1, 8 + +tests.append(test_hcom) + + +def test_voc(h, f): + if not h.startswith(b'Creative Voice File\032'): + return None + sbseek = get_short_le(h[20:22]) + rate = 0 + if 0 <= sbseek < 500 and h[sbseek] == 1: + ratecode = 256 - h[sbseek+4] + if ratecode: + rate = int(1000000.0 / ratecode) + return 'voc', rate, 1, -1, 8 + +tests.append(test_voc) + + +def test_wav(h, f): + import wave + # 'RIFF' 'WAVE' 'fmt ' + if not h.startswith(b'RIFF') or h[8:12] != b'WAVE' or h[12:16] != b'fmt ': + return None + f.seek(0) + try: + w = wave.open(f, 'r') + except (EOFError, wave.Error): + return None + return ('wav', w.getframerate(), w.getnchannels(), + w.getnframes(), 8*w.getsampwidth()) + +tests.append(test_wav) + + +def test_8svx(h, f): + if not h.startswith(b'FORM') or h[8:12] != b'8SVX': + return None + # Should decode it to get #channels -- assume always 1 + return '8svx', 0, 1, 0, 8 + +tests.append(test_8svx) + + +def test_sndt(h, f): + if h.startswith(b'SOUND'): + nsamples = get_long_le(h[8:12]) + rate = get_short_le(h[20:22]) + return 'sndt', rate, 1, nsamples, 8 + +tests.append(test_sndt) + + +def test_sndr(h, f): + if h.startswith(b'\0\0'): + rate = get_short_le(h[2:4]) + if 4000 <= rate <= 25000: + return 'sndr', rate, 1, -1, 8 + +tests.append(test_sndr) + + +#-------------------------------------------# +# Subroutines to extract numbers from bytes # +#-------------------------------------------# + +def get_long_be(b): + return (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3] + +def get_long_le(b): + return (b[3] << 24) | (b[2] << 16) | (b[1] << 8) | b[0] + +def get_short_be(b): + return (b[0] << 8) | b[1] + +def get_short_le(b): + return (b[1] << 8) | b[0] + + +#--------------------# +# Small test program # +#--------------------# + +def test(): + import sys + recursive = 0 + if sys.argv[1:] and sys.argv[1] == '-r': + del sys.argv[1:2] + recursive = 1 + try: + if sys.argv[1:]: + testall(sys.argv[1:], recursive, 1) + else: + testall(['.'], recursive, 1) + except KeyboardInterrupt: + sys.stderr.write('\n[Interrupted]\n') + sys.exit(1) + +def testall(list, recursive, toplevel): + import sys + import os + for filename in list: + if os.path.isdir(filename): + print(filename + '/:', end=' ') + if recursive or toplevel: + print('recursing down:') + import glob + names = glob.glob(os.path.join(glob.escape(filename), '*')) + testall(names, recursive, 0) + else: + print('*** directory (use -r) ***') + else: + print(filename + ':', end=' ') + sys.stdout.flush() + try: + print(what(filename)) + except OSError: + print('*** not found ***') + +if __name__ == '__main__': + test() diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/socketserver.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/socketserver.py new file mode 100644 index 0000000000000000000000000000000000000000..9e94c76731e812626e2e66d43adeee6726d754c8 --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/socketserver.py @@ -0,0 +1,844 @@ +"""Generic socket server classes. + +This module tries to capture the various aspects of defining a server: + +For socket-based servers: + +- address family: + - AF_INET{,6}: IP (Internet Protocol) sockets (default) + - AF_UNIX: Unix domain sockets + - others, e.g. AF_DECNET are conceivable (see +- socket type: + - SOCK_STREAM (reliable stream, e.g. TCP) + - SOCK_DGRAM (datagrams, e.g. UDP) + +For request-based servers (including socket-based): + +- client address verification before further looking at the request + (This is actually a hook for any processing that needs to look + at the request before anything else, e.g. logging) +- how to handle multiple requests: + - synchronous (one request is handled at a time) + - forking (each request is handled by a new process) + - threading (each request is handled by a new thread) + +The classes in this module favor the server type that is simplest to +write: a synchronous TCP/IP server. This is bad class design, but +saves some typing. (There's also the issue that a deep class hierarchy +slows down method lookups.) + +There are five classes in an inheritance diagram, four of which represent +synchronous servers of four types: + + +------------+ + | BaseServer | + +------------+ + | + v + +-----------+ +------------------+ + | TCPServer |------->| UnixStreamServer | + +-----------+ +------------------+ + | + v + +-----------+ +--------------------+ + | UDPServer |------->| UnixDatagramServer | + +-----------+ +--------------------+ + +Note that UnixDatagramServer derives from UDPServer, not from +UnixStreamServer -- the only difference between an IP and a Unix +stream server is the address family, which is simply repeated in both +unix server classes. + +Forking and threading versions of each type of server can be created +using the ForkingMixIn and ThreadingMixIn mix-in classes. For +instance, a threading UDP server class is created as follows: + + class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass + +The Mix-in class must come first, since it overrides a method defined +in UDPServer! Setting the various member variables also changes +the behavior of the underlying server mechanism. + +To implement a service, you must derive a class from +BaseRequestHandler and redefine its handle() method. You can then run +various versions of the service by combining one of the server classes +with your request handler class. + +The request handler class must be different for datagram or stream +services. This can be hidden by using the request handler +subclasses StreamRequestHandler or DatagramRequestHandler. + +Of course, you still have to use your head! + +For instance, it makes no sense to use a forking server if the service +contains state in memory that can be modified by requests (since the +modifications in the child process would never reach the initial state +kept in the parent process and passed to each child). In this case, +you can use a threading server, but you will probably have to use +locks to avoid two requests that come in nearly simultaneous to apply +conflicting changes to the server state. + +On the other hand, if you are building e.g. an HTTP server, where all +data is stored externally (e.g. in the file system), a synchronous +class will essentially render the service "deaf" while one request is +being handled -- which may be for a very long time if a client is slow +to read all the data it has requested. Here a threading or forking +server is appropriate. + +In some cases, it may be appropriate to process part of a request +synchronously, but to finish processing in a forked child depending on +the request data. This can be implemented by using a synchronous +server and doing an explicit fork in the request handler class +handle() method. + +Another approach to handling multiple simultaneous requests in an +environment that supports neither threads nor fork (or where these are +too expensive or inappropriate for the service) is to maintain an +explicit table of partially finished requests and to use a selector to +decide which request to work on next (or whether to handle a new +incoming request). This is particularly important for stream services +where each client can potentially be connected for a long time (if +threads or subprocesses cannot be used). + +Future work: +- Standard classes for Sun RPC (which uses either UDP or TCP) +- Standard mix-in classes to implement various authentication + and encryption schemes + +XXX Open problems: +- What to do with out-of-band data? + +BaseServer: +- split generic "request" functionality out into BaseServer class. + Copyright (C) 2000 Luke Kenneth Casson Leighton + + example: read entries from a SQL database (requires overriding + get_request() to return a table entry from the database). + entry is processed by a RequestHandlerClass. + +""" + +# Author of the BaseServer patch: Luke Kenneth Casson Leighton + +__version__ = "0.4" + + +import socket +import selectors +import os +import sys +import threading +from io import BufferedIOBase +from time import monotonic as time + +__all__ = ["BaseServer", "TCPServer", "UDPServer", + "ThreadingUDPServer", "ThreadingTCPServer", + "BaseRequestHandler", "StreamRequestHandler", + "DatagramRequestHandler", "ThreadingMixIn"] +if hasattr(os, "fork"): + __all__.extend(["ForkingUDPServer","ForkingTCPServer", "ForkingMixIn"]) +if hasattr(socket, "AF_UNIX"): + __all__.extend(["UnixStreamServer","UnixDatagramServer", + "ThreadingUnixStreamServer", + "ThreadingUnixDatagramServer"]) + +# poll/select have the advantage of not requiring any extra file descriptor, +# contrarily to epoll/kqueue (also, they require a single syscall). +if hasattr(selectors, 'PollSelector'): + _ServerSelector = selectors.PollSelector +else: + _ServerSelector = selectors.SelectSelector + + +class BaseServer: + + """Base class for server classes. + + Methods for the caller: + + - __init__(server_address, RequestHandlerClass) + - serve_forever(poll_interval=0.5) + - shutdown() + - handle_request() # if you do not use serve_forever() + - fileno() -> int # for selector + + Methods that may be overridden: + + - server_bind() + - server_activate() + - get_request() -> request, client_address + - handle_timeout() + - verify_request(request, client_address) + - server_close() + - process_request(request, client_address) + - shutdown_request(request) + - close_request(request) + - service_actions() + - handle_error() + + Methods for derived classes: + + - finish_request(request, client_address) + + Class variables that may be overridden by derived classes or + instances: + + - timeout + - address_family + - socket_type + - allow_reuse_address + + Instance variables: + + - RequestHandlerClass + - socket + + """ + + timeout = None + + def __init__(self, server_address, RequestHandlerClass): + """Constructor. May be extended, do not override.""" + self.server_address = server_address + self.RequestHandlerClass = RequestHandlerClass + self.__is_shut_down = threading.Event() + self.__shutdown_request = False + + def server_activate(self): + """Called by constructor to activate the server. + + May be overridden. + + """ + pass + + def serve_forever(self, poll_interval=0.5): + """Handle one request at a time until shutdown. + + Polls for shutdown every poll_interval seconds. Ignores + self.timeout. If you need to do periodic tasks, do them in + another thread. + """ + self.__is_shut_down.clear() + try: + # XXX: Consider using another file descriptor or connecting to the + # socket to wake this up instead of polling. Polling reduces our + # responsiveness to a shutdown request and wastes cpu at all other + # times. + with _ServerSelector() as selector: + selector.register(self, selectors.EVENT_READ) + + while not self.__shutdown_request: + ready = selector.select(poll_interval) + # bpo-35017: shutdown() called during select(), exit immediately. + if self.__shutdown_request: + break + if ready: + self._handle_request_noblock() + + self.service_actions() + finally: + self.__shutdown_request = False + self.__is_shut_down.set() + + def shutdown(self): + """Stops the serve_forever loop. + + Blocks until the loop has finished. This must be called while + serve_forever() is running in another thread, or it will + deadlock. + """ + self.__shutdown_request = True + self.__is_shut_down.wait() + + def service_actions(self): + """Called by the serve_forever() loop. + + May be overridden by a subclass / Mixin to implement any code that + needs to be run during the loop. + """ + pass + + # The distinction between handling, getting, processing and finishing a + # request is fairly arbitrary. Remember: + # + # - handle_request() is the top-level call. It calls selector.select(), + # get_request(), verify_request() and process_request() + # - get_request() is different for stream or datagram sockets + # - process_request() is the place that may fork a new process or create a + # new thread to finish the request + # - finish_request() instantiates the request handler class; this + # constructor will handle the request all by itself + + def handle_request(self): + """Handle one request, possibly blocking. + + Respects self.timeout. + """ + # Support people who used socket.settimeout() to escape + # handle_request before self.timeout was available. + timeout = self.socket.gettimeout() + if timeout is None: + timeout = self.timeout + elif self.timeout is not None: + timeout = min(timeout, self.timeout) + if timeout is not None: + deadline = time() + timeout + + # Wait until a request arrives or the timeout expires - the loop is + # necessary to accommodate early wakeups due to EINTR. + with _ServerSelector() as selector: + selector.register(self, selectors.EVENT_READ) + + while True: + ready = selector.select(timeout) + if ready: + return self._handle_request_noblock() + else: + if timeout is not None: + timeout = deadline - time() + if timeout < 0: + return self.handle_timeout() + + def _handle_request_noblock(self): + """Handle one request, without blocking. + + I assume that selector.select() has returned that the socket is + readable before this function was called, so there should be no risk of + blocking in get_request(). + """ + try: + request, client_address = self.get_request() + except OSError: + return + if self.verify_request(request, client_address): + try: + self.process_request(request, client_address) + except Exception: + self.handle_error(request, client_address) + self.shutdown_request(request) + except: + self.shutdown_request(request) + raise + else: + self.shutdown_request(request) + + def handle_timeout(self): + """Called if no new request arrives within self.timeout. + + Overridden by ForkingMixIn. + """ + pass + + def verify_request(self, request, client_address): + """Verify the request. May be overridden. + + Return True if we should proceed with this request. + + """ + return True + + def process_request(self, request, client_address): + """Call finish_request. + + Overridden by ForkingMixIn and ThreadingMixIn. + + """ + self.finish_request(request, client_address) + self.shutdown_request(request) + + def server_close(self): + """Called to clean-up the server. + + May be overridden. + + """ + pass + + def finish_request(self, request, client_address): + """Finish one request by instantiating RequestHandlerClass.""" + self.RequestHandlerClass(request, client_address, self) + + def shutdown_request(self, request): + """Called to shutdown and close an individual request.""" + self.close_request(request) + + def close_request(self, request): + """Called to clean up an individual request.""" + pass + + def handle_error(self, request, client_address): + """Handle an error gracefully. May be overridden. + + The default is to print a traceback and continue. + + """ + print('-'*40, file=sys.stderr) + print('Exception happened during processing of request from', + client_address, file=sys.stderr) + import traceback + traceback.print_exc() + print('-'*40, file=sys.stderr) + + def __enter__(self): + return self + + def __exit__(self, *args): + self.server_close() + + +class TCPServer(BaseServer): + + """Base class for various socket-based server classes. + + Defaults to synchronous IP stream (i.e., TCP). + + Methods for the caller: + + - __init__(server_address, RequestHandlerClass, bind_and_activate=True) + - serve_forever(poll_interval=0.5) + - shutdown() + - handle_request() # if you don't use serve_forever() + - fileno() -> int # for selector + + Methods that may be overridden: + + - server_bind() + - server_activate() + - get_request() -> request, client_address + - handle_timeout() + - verify_request(request, client_address) + - process_request(request, client_address) + - shutdown_request(request) + - close_request(request) + - handle_error() + + Methods for derived classes: + + - finish_request(request, client_address) + + Class variables that may be overridden by derived classes or + instances: + + - timeout + - address_family + - socket_type + - request_queue_size (only for stream sockets) + - allow_reuse_address + + Instance variables: + + - server_address + - RequestHandlerClass + - socket + + """ + + address_family = socket.AF_INET + + socket_type = socket.SOCK_STREAM + + request_queue_size = 5 + + allow_reuse_address = False + + def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True): + """Constructor. May be extended, do not override.""" + BaseServer.__init__(self, server_address, RequestHandlerClass) + self.socket = socket.socket(self.address_family, + self.socket_type) + if bind_and_activate: + try: + self.server_bind() + self.server_activate() + except: + self.server_close() + raise + + def server_bind(self): + """Called by constructor to bind the socket. + + May be overridden. + + """ + if self.allow_reuse_address: + self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + self.socket.bind(self.server_address) + self.server_address = self.socket.getsockname() + + def server_activate(self): + """Called by constructor to activate the server. + + May be overridden. + + """ + self.socket.listen(self.request_queue_size) + + def server_close(self): + """Called to clean-up the server. + + May be overridden. + + """ + self.socket.close() + + def fileno(self): + """Return socket file number. + + Interface required by selector. + + """ + return self.socket.fileno() + + def get_request(self): + """Get the request and client address from the socket. + + May be overridden. + + """ + return self.socket.accept() + + def shutdown_request(self, request): + """Called to shutdown and close an individual request.""" + try: + #explicitly shutdown. socket.close() merely releases + #the socket and waits for GC to perform the actual close. + request.shutdown(socket.SHUT_WR) + except OSError: + pass #some platforms may raise ENOTCONN here + self.close_request(request) + + def close_request(self, request): + """Called to clean up an individual request.""" + request.close() + + +class UDPServer(TCPServer): + + """UDP server class.""" + + allow_reuse_address = False + + socket_type = socket.SOCK_DGRAM + + max_packet_size = 8192 + + def get_request(self): + data, client_addr = self.socket.recvfrom(self.max_packet_size) + return (data, self.socket), client_addr + + def server_activate(self): + # No need to call listen() for UDP. + pass + + def shutdown_request(self, request): + # No need to shutdown anything. + self.close_request(request) + + def close_request(self, request): + # No need to close anything. + pass + +if hasattr(os, "fork"): + class ForkingMixIn: + """Mix-in class to handle each request in a new process.""" + + timeout = 300 + active_children = None + max_children = 40 + # If true, server_close() waits until all child processes complete. + block_on_close = True + + def collect_children(self, *, blocking=False): + """Internal routine to wait for children that have exited.""" + if self.active_children is None: + return + + # If we're above the max number of children, wait and reap them until + # we go back below threshold. Note that we use waitpid(-1) below to be + # able to collect children in size() syscalls instead + # of size(): the downside is that this might reap children + # which we didn't spawn, which is why we only resort to this when we're + # above max_children. + while len(self.active_children) >= self.max_children: + try: + pid, _ = os.waitpid(-1, 0) + self.active_children.discard(pid) + except ChildProcessError: + # we don't have any children, we're done + self.active_children.clear() + except OSError: + break + + # Now reap all defunct children. + for pid in self.active_children.copy(): + try: + flags = 0 if blocking else os.WNOHANG + pid, _ = os.waitpid(pid, flags) + # if the child hasn't exited yet, pid will be 0 and ignored by + # discard() below + self.active_children.discard(pid) + except ChildProcessError: + # someone else reaped it + self.active_children.discard(pid) + except OSError: + pass + + def handle_timeout(self): + """Wait for zombies after self.timeout seconds of inactivity. + + May be extended, do not override. + """ + self.collect_children() + + def service_actions(self): + """Collect the zombie child processes regularly in the ForkingMixIn. + + service_actions is called in the BaseServer's serve_forever loop. + """ + self.collect_children() + + def process_request(self, request, client_address): + """Fork a new subprocess to process the request.""" + pid = os.fork() + if pid: + # Parent process + if self.active_children is None: + self.active_children = set() + self.active_children.add(pid) + self.close_request(request) + return + else: + # Child process. + # This must never return, hence os._exit()! + status = 1 + try: + self.finish_request(request, client_address) + status = 0 + except Exception: + self.handle_error(request, client_address) + finally: + try: + self.shutdown_request(request) + finally: + os._exit(status) + + def server_close(self): + super().server_close() + self.collect_children(blocking=self.block_on_close) + + +class _Threads(list): + """ + Joinable list of all non-daemon threads. + """ + def append(self, thread): + self.reap() + if thread.daemon: + return + super().append(thread) + + def pop_all(self): + self[:], result = [], self[:] + return result + + def join(self): + for thread in self.pop_all(): + thread.join() + + def reap(self): + self[:] = (thread for thread in self if thread.is_alive()) + + +class _NoThreads: + """ + Degenerate version of _Threads. + """ + def append(self, thread): + pass + + def join(self): + pass + + +class ThreadingMixIn: + """Mix-in class to handle each request in a new thread.""" + + # Decides how threads will act upon termination of the + # main process + daemon_threads = False + # If true, server_close() waits until all non-daemonic threads terminate. + block_on_close = True + # Threads object + # used by server_close() to wait for all threads completion. + _threads = _NoThreads() + + def process_request_thread(self, request, client_address): + """Same as in BaseServer but as a thread. + + In addition, exception handling is done here. + + """ + try: + self.finish_request(request, client_address) + except Exception: + self.handle_error(request, client_address) + finally: + self.shutdown_request(request) + + def process_request(self, request, client_address): + """Start a new thread to process the request.""" + if self.block_on_close: + vars(self).setdefault('_threads', _Threads()) + t = threading.Thread(target = self.process_request_thread, + args = (request, client_address)) + t.daemon = self.daemon_threads + self._threads.append(t) + t.start() + + def server_close(self): + super().server_close() + self._threads.join() + + +if hasattr(os, "fork"): + class ForkingUDPServer(ForkingMixIn, UDPServer): pass + class ForkingTCPServer(ForkingMixIn, TCPServer): pass + +class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass +class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass + +if hasattr(socket, 'AF_UNIX'): + + class UnixStreamServer(TCPServer): + address_family = socket.AF_UNIX + + class UnixDatagramServer(UDPServer): + address_family = socket.AF_UNIX + + class ThreadingUnixStreamServer(ThreadingMixIn, UnixStreamServer): pass + + class ThreadingUnixDatagramServer(ThreadingMixIn, UnixDatagramServer): pass + +class BaseRequestHandler: + + """Base class for request handler classes. + + This class is instantiated for each request to be handled. The + constructor sets the instance variables request, client_address + and server, and then calls the handle() method. To implement a + specific service, all you need to do is to derive a class which + defines a handle() method. + + The handle() method can find the request as self.request, the + client address as self.client_address, and the server (in case it + needs access to per-server information) as self.server. Since a + separate instance is created for each request, the handle() method + can define other arbitrary instance variables. + + """ + + def __init__(self, request, client_address, server): + self.request = request + self.client_address = client_address + self.server = server + self.setup() + try: + self.handle() + finally: + self.finish() + + def setup(self): + pass + + def handle(self): + pass + + def finish(self): + pass + + +# The following two classes make it possible to use the same service +# class for stream or datagram servers. +# Each class sets up these instance variables: +# - rfile: a file object from which receives the request is read +# - wfile: a file object to which the reply is written +# When the handle() method returns, wfile is flushed properly + + +class StreamRequestHandler(BaseRequestHandler): + + """Define self.rfile and self.wfile for stream sockets.""" + + # Default buffer sizes for rfile, wfile. + # We default rfile to buffered because otherwise it could be + # really slow for large data (a getc() call per byte); we make + # wfile unbuffered because (a) often after a write() we want to + # read and we need to flush the line; (b) big writes to unbuffered + # files are typically optimized by stdio even when big reads + # aren't. + rbufsize = -1 + wbufsize = 0 + + # A timeout to apply to the request socket, if not None. + timeout = None + + # Disable nagle algorithm for this socket, if True. + # Use only when wbufsize != 0, to avoid small packets. + disable_nagle_algorithm = False + + def setup(self): + self.connection = self.request + if self.timeout is not None: + self.connection.settimeout(self.timeout) + if self.disable_nagle_algorithm: + self.connection.setsockopt(socket.IPPROTO_TCP, + socket.TCP_NODELAY, True) + self.rfile = self.connection.makefile('rb', self.rbufsize) + if self.wbufsize == 0: + self.wfile = _SocketWriter(self.connection) + else: + self.wfile = self.connection.makefile('wb', self.wbufsize) + + def finish(self): + if not self.wfile.closed: + try: + self.wfile.flush() + except socket.error: + # A final socket error may have occurred here, such as + # the local error ECONNABORTED. + pass + self.wfile.close() + self.rfile.close() + +class _SocketWriter(BufferedIOBase): + """Simple writable BufferedIOBase implementation for a socket + + Does not hold data in a buffer, avoiding any need to call flush().""" + + def __init__(self, sock): + self._sock = sock + + def writable(self): + return True + + def write(self, b): + self._sock.sendall(b) + with memoryview(b) as view: + return view.nbytes + + def fileno(self): + return self._sock.fileno() + +class DatagramRequestHandler(BaseRequestHandler): + + """Define self.rfile and self.wfile for datagram sockets.""" + + def setup(self): + from io import BytesIO + self.packet, self.socket = self.request + self.rfile = BytesIO(self.packet) + self.wfile = BytesIO() + + def finish(self): + self.socket.sendto(self.wfile.getvalue(), self.client_address) diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/stat.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/stat.py new file mode 100644 index 0000000000000000000000000000000000000000..fc024db3f4fbeeb903272363ee2bad19de0e635b --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/stat.py @@ -0,0 +1,195 @@ +"""Constants/functions for interpreting results of os.stat() and os.lstat(). + +Suggested usage: from stat import * +""" + +# Indices for stat struct members in the tuple returned by os.stat() + +ST_MODE = 0 +ST_INO = 1 +ST_DEV = 2 +ST_NLINK = 3 +ST_UID = 4 +ST_GID = 5 +ST_SIZE = 6 +ST_ATIME = 7 +ST_MTIME = 8 +ST_CTIME = 9 + +# Extract bits from the mode + +def S_IMODE(mode): + """Return the portion of the file's mode that can be set by + os.chmod(). + """ + return mode & 0o7777 + +def S_IFMT(mode): + """Return the portion of the file's mode that describes the + file type. + """ + return mode & 0o170000 + +# Constants used as S_IFMT() for various file types +# (not all are implemented on all systems) + +S_IFDIR = 0o040000 # directory +S_IFCHR = 0o020000 # character device +S_IFBLK = 0o060000 # block device +S_IFREG = 0o100000 # regular file +S_IFIFO = 0o010000 # fifo (named pipe) +S_IFLNK = 0o120000 # symbolic link +S_IFSOCK = 0o140000 # socket file +# Fallbacks for uncommon platform-specific constants +S_IFDOOR = 0 +S_IFPORT = 0 +S_IFWHT = 0 + +# Functions to test for each file type + +def S_ISDIR(mode): + """Return True if mode is from a directory.""" + return S_IFMT(mode) == S_IFDIR + +def S_ISCHR(mode): + """Return True if mode is from a character special device file.""" + return S_IFMT(mode) == S_IFCHR + +def S_ISBLK(mode): + """Return True if mode is from a block special device file.""" + return S_IFMT(mode) == S_IFBLK + +def S_ISREG(mode): + """Return True if mode is from a regular file.""" + return S_IFMT(mode) == S_IFREG + +def S_ISFIFO(mode): + """Return True if mode is from a FIFO (named pipe).""" + return S_IFMT(mode) == S_IFIFO + +def S_ISLNK(mode): + """Return True if mode is from a symbolic link.""" + return S_IFMT(mode) == S_IFLNK + +def S_ISSOCK(mode): + """Return True if mode is from a socket.""" + return S_IFMT(mode) == S_IFSOCK + +def S_ISDOOR(mode): + """Return True if mode is from a door.""" + return False + +def S_ISPORT(mode): + """Return True if mode is from an event port.""" + return False + +def S_ISWHT(mode): + """Return True if mode is from a whiteout.""" + return False + +# Names for permission bits + +S_ISUID = 0o4000 # set UID bit +S_ISGID = 0o2000 # set GID bit +S_ENFMT = S_ISGID # file locking enforcement +S_ISVTX = 0o1000 # sticky bit +S_IREAD = 0o0400 # Unix V7 synonym for S_IRUSR +S_IWRITE = 0o0200 # Unix V7 synonym for S_IWUSR +S_IEXEC = 0o0100 # Unix V7 synonym for S_IXUSR +S_IRWXU = 0o0700 # mask for owner permissions +S_IRUSR = 0o0400 # read by owner +S_IWUSR = 0o0200 # write by owner +S_IXUSR = 0o0100 # execute by owner +S_IRWXG = 0o0070 # mask for group permissions +S_IRGRP = 0o0040 # read by group +S_IWGRP = 0o0020 # write by group +S_IXGRP = 0o0010 # execute by group +S_IRWXO = 0o0007 # mask for others (not in group) permissions +S_IROTH = 0o0004 # read by others +S_IWOTH = 0o0002 # write by others +S_IXOTH = 0o0001 # execute by others + +# Names for file flags + +UF_NODUMP = 0x00000001 # do not dump file +UF_IMMUTABLE = 0x00000002 # file may not be changed +UF_APPEND = 0x00000004 # file may only be appended to +UF_OPAQUE = 0x00000008 # directory is opaque when viewed through a union stack +UF_NOUNLINK = 0x00000010 # file may not be renamed or deleted +UF_COMPRESSED = 0x00000020 # OS X: file is hfs-compressed +UF_HIDDEN = 0x00008000 # OS X: file should not be displayed +SF_ARCHIVED = 0x00010000 # file may be archived +SF_IMMUTABLE = 0x00020000 # file may not be changed +SF_APPEND = 0x00040000 # file may only be appended to +SF_NOUNLINK = 0x00100000 # file may not be renamed or deleted +SF_SNAPSHOT = 0x00200000 # file is a snapshot file + + +_filemode_table = ( + ((S_IFLNK, "l"), + (S_IFSOCK, "s"), # Must appear before IFREG and IFDIR as IFSOCK == IFREG | IFDIR + (S_IFREG, "-"), + (S_IFBLK, "b"), + (S_IFDIR, "d"), + (S_IFCHR, "c"), + (S_IFIFO, "p")), + + ((S_IRUSR, "r"),), + ((S_IWUSR, "w"),), + ((S_IXUSR|S_ISUID, "s"), + (S_ISUID, "S"), + (S_IXUSR, "x")), + + ((S_IRGRP, "r"),), + ((S_IWGRP, "w"),), + ((S_IXGRP|S_ISGID, "s"), + (S_ISGID, "S"), + (S_IXGRP, "x")), + + ((S_IROTH, "r"),), + ((S_IWOTH, "w"),), + ((S_IXOTH|S_ISVTX, "t"), + (S_ISVTX, "T"), + (S_IXOTH, "x")) +) + +def filemode(mode): + """Convert a file's mode to a string of the form '-rwxrwxrwx'.""" + perm = [] + for table in _filemode_table: + for bit, char in table: + if mode & bit == bit: + perm.append(char) + break + else: + perm.append("-") + return "".join(perm) + + +# Windows FILE_ATTRIBUTE constants for interpreting os.stat()'s +# "st_file_attributes" member + +FILE_ATTRIBUTE_ARCHIVE = 32 +FILE_ATTRIBUTE_COMPRESSED = 2048 +FILE_ATTRIBUTE_DEVICE = 64 +FILE_ATTRIBUTE_DIRECTORY = 16 +FILE_ATTRIBUTE_ENCRYPTED = 16384 +FILE_ATTRIBUTE_HIDDEN = 2 +FILE_ATTRIBUTE_INTEGRITY_STREAM = 32768 +FILE_ATTRIBUTE_NORMAL = 128 +FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 8192 +FILE_ATTRIBUTE_NO_SCRUB_DATA = 131072 +FILE_ATTRIBUTE_OFFLINE = 4096 +FILE_ATTRIBUTE_READONLY = 1 +FILE_ATTRIBUTE_REPARSE_POINT = 1024 +FILE_ATTRIBUTE_SPARSE_FILE = 512 +FILE_ATTRIBUTE_SYSTEM = 4 +FILE_ATTRIBUTE_TEMPORARY = 256 +FILE_ATTRIBUTE_VIRTUAL = 65536 + + +# If available, use C implementation +try: + from _stat import * +except ImportError: + pass diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/string.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/string.py new file mode 100644 index 0000000000000000000000000000000000000000..b423ff5dc6f69f022fa153d2c1459639f7ae250a --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/string.py @@ -0,0 +1,282 @@ +"""A collection of string constants. + +Public module variables: + +whitespace -- a string containing all ASCII whitespace +ascii_lowercase -- a string containing all ASCII lowercase letters +ascii_uppercase -- a string containing all ASCII uppercase letters +ascii_letters -- a string containing all ASCII letters +digits -- a string containing all ASCII decimal digits +hexdigits -- a string containing all ASCII hexadecimal digits +octdigits -- a string containing all ASCII octal digits +punctuation -- a string containing all ASCII punctuation characters +printable -- a string containing all ASCII characters considered printable + +""" + +__all__ = ["ascii_letters", "ascii_lowercase", "ascii_uppercase", "capwords", + "digits", "hexdigits", "octdigits", "printable", "punctuation", + "whitespace", "Formatter", "Template"] + +import _string + +# Some strings for ctype-style character classification +whitespace = ' \t\n\r\v\f' +ascii_lowercase = 'abcdefghijklmnopqrstuvwxyz' +ascii_uppercase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +ascii_letters = ascii_lowercase + ascii_uppercase +digits = '0123456789' +hexdigits = digits + 'abcdef' + 'ABCDEF' +octdigits = '01234567' +punctuation = r"""!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~""" +printable = digits + ascii_letters + punctuation + whitespace + +# Functions which aren't available as string methods. + +# Capitalize the words in a string, e.g. " aBc dEf " -> "Abc Def". +def capwords(s, sep=None): + """capwords(s [,sep]) -> string + + Split the argument into words using split, capitalize each + word using capitalize, and join the capitalized words using + join. If the optional second argument sep is absent or None, + runs of whitespace characters are replaced by a single space + and leading and trailing whitespace are removed, otherwise + sep is used to split and join the words. + + """ + return (sep or ' ').join(x.capitalize() for x in s.split(sep)) + + +#################################################################### +import re as _re +from collections import ChainMap as _ChainMap + +_sentinel_dict = {} + +class _TemplateMetaclass(type): + pattern = r""" + %(delim)s(?: + (?P%(delim)s) | # Escape sequence of two delimiters + (?P%(id)s) | # delimiter and a Python identifier + {(?P%(bid)s)} | # delimiter and a braced identifier + (?P) # Other ill-formed delimiter exprs + ) + """ + + def __init__(cls, name, bases, dct): + super(_TemplateMetaclass, cls).__init__(name, bases, dct) + if 'pattern' in dct: + pattern = cls.pattern + else: + pattern = _TemplateMetaclass.pattern % { + 'delim' : _re.escape(cls.delimiter), + 'id' : cls.idpattern, + 'bid' : cls.braceidpattern or cls.idpattern, + } + cls.pattern = _re.compile(pattern, cls.flags | _re.VERBOSE) + + +class Template(metaclass=_TemplateMetaclass): + """A string class for supporting $-substitutions.""" + + delimiter = '$' + # r'[a-z]' matches to non-ASCII letters when used with IGNORECASE, but + # without the ASCII flag. We can't add re.ASCII to flags because of + # backward compatibility. So we use the ?a local flag and [a-z] pattern. + # See https://bugs.python.org/issue31672 + idpattern = r'(?a:[_a-z][_a-z0-9]*)' + braceidpattern = None + flags = _re.IGNORECASE + + def __init__(self, template): + self.template = template + + # Search for $$, $identifier, ${identifier}, and any bare $'s + + def _invalid(self, mo): + i = mo.start('invalid') + lines = self.template[:i].splitlines(keepends=True) + if not lines: + colno = 1 + lineno = 1 + else: + colno = i - len(''.join(lines[:-1])) + lineno = len(lines) + raise ValueError('Invalid placeholder in string: line %d, col %d' % + (lineno, colno)) + + def substitute(self, mapping=_sentinel_dict, /, **kws): + if mapping is _sentinel_dict: + mapping = kws + elif kws: + mapping = _ChainMap(kws, mapping) + # Helper function for .sub() + def convert(mo): + # Check the most common path first. + named = mo.group('named') or mo.group('braced') + if named is not None: + return str(mapping[named]) + if mo.group('escaped') is not None: + return self.delimiter + if mo.group('invalid') is not None: + self._invalid(mo) + raise ValueError('Unrecognized named group in pattern', + self.pattern) + return self.pattern.sub(convert, self.template) + + def safe_substitute(self, mapping=_sentinel_dict, /, **kws): + if mapping is _sentinel_dict: + mapping = kws + elif kws: + mapping = _ChainMap(kws, mapping) + # Helper function for .sub() + def convert(mo): + named = mo.group('named') or mo.group('braced') + if named is not None: + try: + return str(mapping[named]) + except KeyError: + return mo.group() + if mo.group('escaped') is not None: + return self.delimiter + if mo.group('invalid') is not None: + return mo.group() + raise ValueError('Unrecognized named group in pattern', + self.pattern) + return self.pattern.sub(convert, self.template) + + + +######################################################################## +# the Formatter class +# see PEP 3101 for details and purpose of this class + +# The hard parts are reused from the C implementation. They're exposed as "_" +# prefixed methods of str. + +# The overall parser is implemented in _string.formatter_parser. +# The field name parser is implemented in _string.formatter_field_name_split + +class Formatter: + def format(self, format_string, /, *args, **kwargs): + return self.vformat(format_string, args, kwargs) + + def vformat(self, format_string, args, kwargs): + used_args = set() + result, _ = self._vformat(format_string, args, kwargs, used_args, 2) + self.check_unused_args(used_args, args, kwargs) + return result + + def _vformat(self, format_string, args, kwargs, used_args, recursion_depth, + auto_arg_index=0): + if recursion_depth < 0: + raise ValueError('Max string recursion exceeded') + result = [] + for literal_text, field_name, format_spec, conversion in \ + self.parse(format_string): + + # output the literal text + if literal_text: + result.append(literal_text) + + # if there's a field, output it + if field_name is not None: + # this is some markup, find the object and do + # the formatting + + # handle arg indexing when empty field_names are given. + if field_name == '': + if auto_arg_index is False: + raise ValueError('cannot switch from manual field ' + 'specification to automatic field ' + 'numbering') + field_name = str(auto_arg_index) + auto_arg_index += 1 + elif field_name.isdigit(): + if auto_arg_index: + raise ValueError('cannot switch from manual field ' + 'specification to automatic field ' + 'numbering') + # disable auto arg incrementing, if it gets + # used later on, then an exception will be raised + auto_arg_index = False + + # given the field_name, find the object it references + # and the argument it came from + obj, arg_used = self.get_field(field_name, args, kwargs) + used_args.add(arg_used) + + # do any conversion on the resulting object + obj = self.convert_field(obj, conversion) + + # expand the format spec, if needed + format_spec, auto_arg_index = self._vformat( + format_spec, args, kwargs, + used_args, recursion_depth-1, + auto_arg_index=auto_arg_index) + + # format the object and append to the result + result.append(self.format_field(obj, format_spec)) + + return ''.join(result), auto_arg_index + + + def get_value(self, key, args, kwargs): + if isinstance(key, int): + return args[key] + else: + return kwargs[key] + + + def check_unused_args(self, used_args, args, kwargs): + pass + + + def format_field(self, value, format_spec): + return format(value, format_spec) + + + def convert_field(self, value, conversion): + # do any conversion on the resulting object + if conversion is None: + return value + elif conversion == 's': + return str(value) + elif conversion == 'r': + return repr(value) + elif conversion == 'a': + return ascii(value) + raise ValueError("Unknown conversion specifier {0!s}".format(conversion)) + + + # returns an iterable that contains tuples of the form: + # (literal_text, field_name, format_spec, conversion) + # literal_text can be zero length + # field_name can be None, in which case there's no + # object to format and output + # if field_name is not None, it is looked up, formatted + # with format_spec and conversion and then used + def parse(self, format_string): + return _string.formatter_parser(format_string) + + + # given a field_name, find the object it references. + # field_name: the field being looked up, e.g. "0.name" + # or "lookup[3]" + # used_args: a set of which args have been used + # args, kwargs: as passed in to vformat + def get_field(self, field_name, args, kwargs): + first, rest = _string.formatter_field_name_split(field_name) + + obj = self.get_value(first, args, kwargs) + + # loop through the rest of the field_name, doing + # getattr or getitem as needed + for is_attr, i in rest: + if is_attr: + obj = getattr(obj, i) + else: + obj = obj[i] + + return obj, first diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/subprocess.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/subprocess.py new file mode 100644 index 0000000000000000000000000000000000000000..7d363fa3153fa4b7cdaa892f5b2b595c532091c6 --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/subprocess.py @@ -0,0 +1,1959 @@ +# subprocess - Subprocesses with accessible I/O streams +# +# For more information about this module, see PEP 324. +# +# Copyright (c) 2003-2005 by Peter Astrand +# +# Licensed to PSF under a Contributor Agreement. +# See http://www.python.org/2.4/license for licensing details. + +r"""Subprocesses with accessible I/O streams + +This module allows you to spawn processes, connect to their +input/output/error pipes, and obtain their return codes. + +For a complete description of this module see the Python documentation. + +Main API +======== +run(...): Runs a command, waits for it to complete, then returns a + CompletedProcess instance. +Popen(...): A class for flexibly executing a command in a new process + +Constants +--------- +DEVNULL: Special value that indicates that os.devnull should be used +PIPE: Special value that indicates a pipe should be created +STDOUT: Special value that indicates that stderr should go to stdout + + +Older API +========= +call(...): Runs a command, waits for it to complete, then returns + the return code. +check_call(...): Same as call() but raises CalledProcessError() + if return code is not 0 +check_output(...): Same as check_call() but returns the contents of + stdout instead of a return code +getoutput(...): Runs a command in the shell, waits for it to complete, + then returns the output +getstatusoutput(...): Runs a command in the shell, waits for it to complete, + then returns a (exitcode, output) tuple +""" + +import builtins +import errno +import io +import os +import time +import signal +import sys +import threading +import warnings +import contextlib +from time import monotonic as _time + + +__all__ = ["Popen", "PIPE", "STDOUT", "call", "check_call", "getstatusoutput", + "getoutput", "check_output", "run", "CalledProcessError", "DEVNULL", + "SubprocessError", "TimeoutExpired", "CompletedProcess"] + # NOTE: We intentionally exclude list2cmdline as it is + # considered an internal implementation detail. issue10838. + +try: + import msvcrt + import _winapi + _mswindows = True +except ModuleNotFoundError: + _mswindows = False + import _posixsubprocess + import select + import selectors +else: + from _winapi import (CREATE_NEW_CONSOLE, CREATE_NEW_PROCESS_GROUP, + STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, + STD_ERROR_HANDLE, SW_HIDE, + STARTF_USESTDHANDLES, STARTF_USESHOWWINDOW, + ABOVE_NORMAL_PRIORITY_CLASS, BELOW_NORMAL_PRIORITY_CLASS, + HIGH_PRIORITY_CLASS, IDLE_PRIORITY_CLASS, + NORMAL_PRIORITY_CLASS, REALTIME_PRIORITY_CLASS, + CREATE_NO_WINDOW, DETACHED_PROCESS, + CREATE_DEFAULT_ERROR_MODE, CREATE_BREAKAWAY_FROM_JOB) + + __all__.extend(["CREATE_NEW_CONSOLE", "CREATE_NEW_PROCESS_GROUP", + "STD_INPUT_HANDLE", "STD_OUTPUT_HANDLE", + "STD_ERROR_HANDLE", "SW_HIDE", + "STARTF_USESTDHANDLES", "STARTF_USESHOWWINDOW", + "STARTUPINFO", + "ABOVE_NORMAL_PRIORITY_CLASS", "BELOW_NORMAL_PRIORITY_CLASS", + "HIGH_PRIORITY_CLASS", "IDLE_PRIORITY_CLASS", + "NORMAL_PRIORITY_CLASS", "REALTIME_PRIORITY_CLASS", + "CREATE_NO_WINDOW", "DETACHED_PROCESS", + "CREATE_DEFAULT_ERROR_MODE", "CREATE_BREAKAWAY_FROM_JOB"]) + + +# Exception classes used by this module. +class SubprocessError(Exception): pass + + +class CalledProcessError(SubprocessError): + """Raised when run() is called with check=True and the process + returns a non-zero exit status. + + Attributes: + cmd, returncode, stdout, stderr, output + """ + def __init__(self, returncode, cmd, output=None, stderr=None): + self.returncode = returncode + self.cmd = cmd + self.output = output + self.stderr = stderr + + def __str__(self): + if self.returncode and self.returncode < 0: + try: + return "Command '%s' died with %r." % ( + self.cmd, signal.Signals(-self.returncode)) + except ValueError: + return "Command '%s' died with unknown signal %d." % ( + self.cmd, -self.returncode) + else: + return "Command '%s' returned non-zero exit status %d." % ( + self.cmd, self.returncode) + + @property + def stdout(self): + """Alias for output attribute, to match stderr""" + return self.output + + @stdout.setter + def stdout(self, value): + # There's no obvious reason to set this, but allow it anyway so + # .stdout is a transparent alias for .output + self.output = value + + +class TimeoutExpired(SubprocessError): + """This exception is raised when the timeout expires while waiting for a + child process. + + Attributes: + cmd, output, stdout, stderr, timeout + """ + def __init__(self, cmd, timeout, output=None, stderr=None): + self.cmd = cmd + self.timeout = timeout + self.output = output + self.stderr = stderr + + def __str__(self): + return ("Command '%s' timed out after %s seconds" % + (self.cmd, self.timeout)) + + @property + def stdout(self): + return self.output + + @stdout.setter + def stdout(self, value): + # There's no obvious reason to set this, but allow it anyway so + # .stdout is a transparent alias for .output + self.output = value + + +if _mswindows: + class STARTUPINFO: + def __init__(self, *, dwFlags=0, hStdInput=None, hStdOutput=None, + hStdError=None, wShowWindow=0, lpAttributeList=None): + self.dwFlags = dwFlags + self.hStdInput = hStdInput + self.hStdOutput = hStdOutput + self.hStdError = hStdError + self.wShowWindow = wShowWindow + self.lpAttributeList = lpAttributeList or {"handle_list": []} + + def copy(self): + attr_list = self.lpAttributeList.copy() + if 'handle_list' in attr_list: + attr_list['handle_list'] = list(attr_list['handle_list']) + + return STARTUPINFO(dwFlags=self.dwFlags, + hStdInput=self.hStdInput, + hStdOutput=self.hStdOutput, + hStdError=self.hStdError, + wShowWindow=self.wShowWindow, + lpAttributeList=attr_list) + + + class Handle(int): + closed = False + + def Close(self, CloseHandle=_winapi.CloseHandle): + if not self.closed: + self.closed = True + CloseHandle(self) + + def Detach(self): + if not self.closed: + self.closed = True + return int(self) + raise ValueError("already closed") + + def __repr__(self): + return "%s(%d)" % (self.__class__.__name__, int(self)) + + __del__ = Close +else: + # When select or poll has indicated that the file is writable, + # we can write up to _PIPE_BUF bytes without risk of blocking. + # POSIX defines PIPE_BUF as >= 512. + _PIPE_BUF = getattr(select, 'PIPE_BUF', 512) + + # poll/select have the advantage of not requiring any extra file + # descriptor, contrarily to epoll/kqueue (also, they require a single + # syscall). + if hasattr(selectors, 'PollSelector'): + _PopenSelector = selectors.PollSelector + else: + _PopenSelector = selectors.SelectSelector + + +if _mswindows: + # On Windows we just need to close `Popen._handle` when we no longer need + # it, so that the kernel can free it. `Popen._handle` gets closed + # implicitly when the `Popen` instance is finalized (see `Handle.__del__`, + # which is calling `CloseHandle` as requested in [1]), so there is nothing + # for `_cleanup` to do. + # + # [1] https://docs.microsoft.com/en-us/windows/desktop/ProcThread/ + # creating-processes + _active = None + + def _cleanup(): + pass +else: + # This lists holds Popen instances for which the underlying process had not + # exited at the time its __del__ method got called: those processes are + # wait()ed for synchronously from _cleanup() when a new Popen object is + # created, to avoid zombie processes. + _active = [] + + def _cleanup(): + if _active is None: + return + for inst in _active[:]: + res = inst._internal_poll(_deadstate=sys.maxsize) + if res is not None: + try: + _active.remove(inst) + except ValueError: + # This can happen if two threads create a new Popen instance. + # It's harmless that it was already removed, so ignore. + pass + +PIPE = -1 +STDOUT = -2 +DEVNULL = -3 + + +# XXX This function is only used by multiprocessing and the test suite, +# but it's here so that it can be imported when Python is compiled without +# threads. + +def _optim_args_from_interpreter_flags(): + """Return a list of command-line arguments reproducing the current + optimization settings in sys.flags.""" + args = [] + value = sys.flags.optimize + if value > 0: + args.append('-' + 'O' * value) + return args + + +def _args_from_interpreter_flags(): + """Return a list of command-line arguments reproducing the current + settings in sys.flags, sys.warnoptions and sys._xoptions.""" + flag_opt_map = { + 'debug': 'd', + # 'inspect': 'i', + # 'interactive': 'i', + 'dont_write_bytecode': 'B', + 'no_site': 'S', + 'verbose': 'v', + 'bytes_warning': 'b', + 'quiet': 'q', + # -O is handled in _optim_args_from_interpreter_flags() + } + args = _optim_args_from_interpreter_flags() + for flag, opt in flag_opt_map.items(): + v = getattr(sys.flags, flag) + if v > 0: + args.append('-' + opt * v) + + if sys.flags.isolated: + args.append('-I') + else: + if sys.flags.ignore_environment: + args.append('-E') + if sys.flags.no_user_site: + args.append('-s') + + # -W options + warnopts = sys.warnoptions[:] + bytes_warning = sys.flags.bytes_warning + xoptions = getattr(sys, '_xoptions', {}) + dev_mode = ('dev' in xoptions) + + if bytes_warning > 1: + warnopts.remove("error::BytesWarning") + elif bytes_warning: + warnopts.remove("default::BytesWarning") + if dev_mode: + warnopts.remove('default') + for opt in warnopts: + args.append('-W' + opt) + + # -X options + if dev_mode: + args.extend(('-X', 'dev')) + for opt in ('faulthandler', 'tracemalloc', 'importtime', + 'showalloccount', 'showrefcount', 'utf8'): + if opt in xoptions: + value = xoptions[opt] + if value is True: + arg = opt + else: + arg = '%s=%s' % (opt, value) + args.extend(('-X', arg)) + + return args + + +def call(*popenargs, timeout=None, **kwargs): + """Run command with arguments. Wait for command to complete or + timeout, then return the returncode attribute. + + The arguments are the same as for the Popen constructor. Example: + + retcode = call(["ls", "-l"]) + """ + with Popen(*popenargs, **kwargs) as p: + try: + return p.wait(timeout=timeout) + except: # Including KeyboardInterrupt, wait handled that. + p.kill() + # We don't call p.wait() again as p.__exit__ does that for us. + raise + + +def check_call(*popenargs, **kwargs): + """Run command with arguments. Wait for command to complete. If + the exit code was zero then return, otherwise raise + CalledProcessError. The CalledProcessError object will have the + return code in the returncode attribute. + + The arguments are the same as for the call function. Example: + + check_call(["ls", "-l"]) + """ + retcode = call(*popenargs, **kwargs) + if retcode: + cmd = kwargs.get("args") + if cmd is None: + cmd = popenargs[0] + raise CalledProcessError(retcode, cmd) + return 0 + + +def check_output(*popenargs, timeout=None, **kwargs): + r"""Run command with arguments and return its output. + + If the exit code was non-zero it raises a CalledProcessError. The + CalledProcessError object will have the return code in the returncode + attribute and output in the output attribute. + + The arguments are the same as for the Popen constructor. Example: + + >>> check_output(["ls", "-l", "/dev/null"]) + b'crw-rw-rw- 1 root root 1, 3 Oct 18 2007 /dev/null\n' + + The stdout argument is not allowed as it is used internally. + To capture standard error in the result, use stderr=STDOUT. + + >>> check_output(["/bin/sh", "-c", + ... "ls -l non_existent_file ; exit 0"], + ... stderr=STDOUT) + b'ls: non_existent_file: No such file or directory\n' + + There is an additional optional argument, "input", allowing you to + pass a string to the subprocess's stdin. If you use this argument + you may not also use the Popen constructor's "stdin" argument, as + it too will be used internally. Example: + + >>> check_output(["sed", "-e", "s/foo/bar/"], + ... input=b"when in the course of fooman events\n") + b'when in the course of barman events\n' + + By default, all communication is in bytes, and therefore any "input" + should be bytes, and the return value will be bytes. If in text mode, + any "input" should be a string, and the return value will be a string + decoded according to locale encoding, or by "encoding" if set. Text mode + is triggered by setting any of text, encoding, errors or universal_newlines. + """ + if 'stdout' in kwargs: + raise ValueError('stdout argument not allowed, it will be overridden.') + + if 'input' in kwargs and kwargs['input'] is None: + # Explicitly passing input=None was previously equivalent to passing an + # empty string. That is maintained here for backwards compatibility. + if kwargs.get('universal_newlines') or kwargs.get('text'): + empty = '' + else: + empty = b'' + kwargs['input'] = empty + + return run(*popenargs, stdout=PIPE, timeout=timeout, check=True, + **kwargs).stdout + + +class CompletedProcess(object): + """A process that has finished running. + + This is returned by run(). + + Attributes: + args: The list or str args passed to run(). + returncode: The exit code of the process, negative for signals. + stdout: The standard output (None if not captured). + stderr: The standard error (None if not captured). + """ + def __init__(self, args, returncode, stdout=None, stderr=None): + self.args = args + self.returncode = returncode + self.stdout = stdout + self.stderr = stderr + + def __repr__(self): + args = ['args={!r}'.format(self.args), + 'returncode={!r}'.format(self.returncode)] + if self.stdout is not None: + args.append('stdout={!r}'.format(self.stdout)) + if self.stderr is not None: + args.append('stderr={!r}'.format(self.stderr)) + return "{}({})".format(type(self).__name__, ', '.join(args)) + + def check_returncode(self): + """Raise CalledProcessError if the exit code is non-zero.""" + if self.returncode: + raise CalledProcessError(self.returncode, self.args, self.stdout, + self.stderr) + + +def run(*popenargs, + input=None, capture_output=False, timeout=None, check=False, **kwargs): + """Run command with arguments and return a CompletedProcess instance. + + The returned instance will have attributes args, returncode, stdout and + stderr. By default, stdout and stderr are not captured, and those attributes + will be None. Pass stdout=PIPE and/or stderr=PIPE in order to capture them. + + If check is True and the exit code was non-zero, it raises a + CalledProcessError. The CalledProcessError object will have the return code + in the returncode attribute, and output & stderr attributes if those streams + were captured. + + If timeout is given, and the process takes too long, a TimeoutExpired + exception will be raised. + + There is an optional argument "input", allowing you to + pass bytes or a string to the subprocess's stdin. If you use this argument + you may not also use the Popen constructor's "stdin" argument, as + it will be used internally. + + By default, all communication is in bytes, and therefore any "input" should + be bytes, and the stdout and stderr will be bytes. If in text mode, any + "input" should be a string, and stdout and stderr will be strings decoded + according to locale encoding, or by "encoding" if set. Text mode is + triggered by setting any of text, encoding, errors or universal_newlines. + + The other arguments are the same as for the Popen constructor. + """ + if input is not None: + if kwargs.get('stdin') is not None: + raise ValueError('stdin and input arguments may not both be used.') + kwargs['stdin'] = PIPE + + if capture_output: + if kwargs.get('stdout') is not None or kwargs.get('stderr') is not None: + raise ValueError('stdout and stderr arguments may not be used ' + 'with capture_output.') + kwargs['stdout'] = PIPE + kwargs['stderr'] = PIPE + + with Popen(*popenargs, **kwargs) as process: + try: + stdout, stderr = process.communicate(input, timeout=timeout) + except TimeoutExpired as exc: + process.kill() + if _mswindows: + # Windows accumulates the output in a single blocking + # read() call run on child threads, with the timeout + # being done in a join() on those threads. communicate() + # _after_ kill() is required to collect that and add it + # to the exception. + exc.stdout, exc.stderr = process.communicate() + else: + # POSIX _communicate already populated the output so + # far into the TimeoutExpired exception. + process.wait() + raise + except: # Including KeyboardInterrupt, communicate handled that. + process.kill() + # We don't call process.wait() as .__exit__ does that for us. + raise + retcode = process.poll() + if check and retcode: + raise CalledProcessError(retcode, process.args, + output=stdout, stderr=stderr) + return CompletedProcess(process.args, retcode, stdout, stderr) + + +def list2cmdline(seq): + """ + Translate a sequence of arguments into a command line + string, using the same rules as the MS C runtime: + + 1) Arguments are delimited by white space, which is either a + space or a tab. + + 2) A string surrounded by double quotation marks is + interpreted as a single argument, regardless of white space + contained within. A quoted string can be embedded in an + argument. + + 3) A double quotation mark preceded by a backslash is + interpreted as a literal double quotation mark. + + 4) Backslashes are interpreted literally, unless they + immediately precede a double quotation mark. + + 5) If backslashes immediately precede a double quotation mark, + every pair of backslashes is interpreted as a literal + backslash. If the number of backslashes is odd, the last + backslash escapes the next double quotation mark as + described in rule 3. + """ + + # See + # http://msdn.microsoft.com/en-us/library/17w5ykft.aspx + # or search http://msdn.microsoft.com for + # "Parsing C++ Command-Line Arguments" + result = [] + needquote = False + for arg in map(os.fsdecode, seq): + bs_buf = [] + + # Add a space to separate this argument from the others + if result: + result.append(' ') + + needquote = (" " in arg) or ("\t" in arg) or not arg + if needquote: + result.append('"') + + for c in arg: + if c == '\\': + # Don't know if we need to double yet. + bs_buf.append(c) + elif c == '"': + # Double backslashes. + result.append('\\' * len(bs_buf)*2) + bs_buf = [] + result.append('\\"') + else: + # Normal char + if bs_buf: + result.extend(bs_buf) + bs_buf = [] + result.append(c) + + # Add remaining backslashes, if any. + if bs_buf: + result.extend(bs_buf) + + if needquote: + result.extend(bs_buf) + result.append('"') + + return ''.join(result) + + +# Various tools for executing commands and looking at their output and status. +# + +def getstatusoutput(cmd): + """Return (exitcode, output) of executing cmd in a shell. + + Execute the string 'cmd' in a shell with 'check_output' and + return a 2-tuple (status, output). The locale encoding is used + to decode the output and process newlines. + + A trailing newline is stripped from the output. + The exit status for the command can be interpreted + according to the rules for the function 'wait'. Example: + + >>> import subprocess + >>> subprocess.getstatusoutput('ls /bin/ls') + (0, '/bin/ls') + >>> subprocess.getstatusoutput('cat /bin/junk') + (1, 'cat: /bin/junk: No such file or directory') + >>> subprocess.getstatusoutput('/bin/junk') + (127, 'sh: /bin/junk: not found') + >>> subprocess.getstatusoutput('/bin/kill $$') + (-15, '') + """ + try: + data = check_output(cmd, shell=True, text=True, stderr=STDOUT) + exitcode = 0 + except CalledProcessError as ex: + data = ex.output + exitcode = ex.returncode + if data[-1:] == '\n': + data = data[:-1] + return exitcode, data + +def getoutput(cmd): + """Return output (stdout or stderr) of executing cmd in a shell. + + Like getstatusoutput(), except the exit status is ignored and the return + value is a string containing the command's output. Example: + + >>> import subprocess + >>> subprocess.getoutput('ls /bin/ls') + '/bin/ls' + """ + return getstatusoutput(cmd)[1] + + +def _use_posix_spawn(): + """Check if posix_spawn() can be used for subprocess. + + subprocess requires a posix_spawn() implementation that properly reports + errors to the parent process, & sets errno on the following failures: + + * Process attribute actions failed. + * File actions failed. + * exec() failed. + + Prefer an implementation which can use vfork() in some cases for best + performance. + """ + if _mswindows or not hasattr(os, 'posix_spawn'): + # os.posix_spawn() is not available + return False + + if sys.platform == 'darwin': + # posix_spawn() is a syscall on macOS and properly reports errors + return True + + # Check libc name and runtime libc version + try: + ver = os.confstr('CS_GNU_LIBC_VERSION') + # parse 'glibc 2.28' as ('glibc', (2, 28)) + parts = ver.split(maxsplit=1) + if len(parts) != 2: + # reject unknown format + raise ValueError + libc = parts[0] + version = tuple(map(int, parts[1].split('.'))) + + if sys.platform == 'linux' and libc == 'glibc' and version >= (2, 24): + # glibc 2.24 has a new Linux posix_spawn implementation using vfork + # which properly reports errors to the parent process. + return True + # Note: Don't use the implementation in earlier glibc because it doesn't + # use vfork (even if glibc 2.26 added a pipe to properly report errors + # to the parent process). + except (AttributeError, ValueError, OSError): + # os.confstr() or CS_GNU_LIBC_VERSION value not available + pass + + # By default, assume that posix_spawn() does not properly report errors. + return False + + +_USE_POSIX_SPAWN = _use_posix_spawn() + + +class Popen(object): + """ Execute a child program in a new process. + + For a complete description of the arguments see the Python documentation. + + Arguments: + args: A string, or a sequence of program arguments. + + bufsize: supplied as the buffering argument to the open() function when + creating the stdin/stdout/stderr pipe file objects + + executable: A replacement program to execute. + + stdin, stdout and stderr: These specify the executed programs' standard + input, standard output and standard error file handles, respectively. + + preexec_fn: (POSIX only) An object to be called in the child process + just before the child is executed. + + close_fds: Controls closing or inheriting of file descriptors. + + shell: If true, the command will be executed through the shell. + + cwd: Sets the current directory before the child is executed. + + env: Defines the environment variables for the new process. + + text: If true, decode stdin, stdout and stderr using the given encoding + (if set) or the system default otherwise. + + universal_newlines: Alias of text, provided for backwards compatibility. + + startupinfo and creationflags (Windows only) + + restore_signals (POSIX only) + + start_new_session (POSIX only) + + pass_fds (POSIX only) + + encoding and errors: Text mode encoding and error handling to use for + file objects stdin, stdout and stderr. + + Attributes: + stdin, stdout, stderr, pid, returncode + """ + _child_created = False # Set here since __del__ checks it + + def __init__(self, args, bufsize=-1, executable=None, + stdin=None, stdout=None, stderr=None, + preexec_fn=None, close_fds=True, + shell=False, cwd=None, env=None, universal_newlines=None, + startupinfo=None, creationflags=0, + restore_signals=True, start_new_session=False, + pass_fds=(), *, encoding=None, errors=None, text=None): + """Create new Popen instance.""" + _cleanup() + # Held while anything is calling waitpid before returncode has been + # updated to prevent clobbering returncode if wait() or poll() are + # called from multiple threads at once. After acquiring the lock, + # code must re-check self.returncode to see if another thread just + # finished a waitpid() call. + self._waitpid_lock = threading.Lock() + + self._input = None + self._communication_started = False + if bufsize is None: + bufsize = -1 # Restore default + if not isinstance(bufsize, int): + raise TypeError("bufsize must be an integer") + + if _mswindows: + if preexec_fn is not None: + raise ValueError("preexec_fn is not supported on Windows " + "platforms") + else: + # POSIX + if pass_fds and not close_fds: + warnings.warn("pass_fds overriding close_fds.", RuntimeWarning) + close_fds = True + if startupinfo is not None: + raise ValueError("startupinfo is only supported on Windows " + "platforms") + if creationflags != 0: + raise ValueError("creationflags is only supported on Windows " + "platforms") + + self.args = args + self.stdin = None + self.stdout = None + self.stderr = None + self.pid = None + self.returncode = None + self.encoding = encoding + self.errors = errors + + # Validate the combinations of text and universal_newlines + if (text is not None and universal_newlines is not None + and bool(universal_newlines) != bool(text)): + raise SubprocessError('Cannot disambiguate when both text ' + 'and universal_newlines are supplied but ' + 'different. Pass one or the other.') + + # Input and output objects. The general principle is like + # this: + # + # Parent Child + # ------ ----- + # p2cwrite ---stdin---> p2cread + # c2pread <--stdout--- c2pwrite + # errread <--stderr--- errwrite + # + # On POSIX, the child objects are file descriptors. On + # Windows, these are Windows file handles. The parent objects + # are file descriptors on both platforms. The parent objects + # are -1 when not using PIPEs. The child objects are -1 + # when not redirecting. + + (p2cread, p2cwrite, + c2pread, c2pwrite, + errread, errwrite) = self._get_handles(stdin, stdout, stderr) + + # We wrap OS handles *before* launching the child, otherwise a + # quickly terminating child could make our fds unwrappable + # (see #8458). + + if _mswindows: + if p2cwrite != -1: + p2cwrite = msvcrt.open_osfhandle(p2cwrite.Detach(), 0) + if c2pread != -1: + c2pread = msvcrt.open_osfhandle(c2pread.Detach(), 0) + if errread != -1: + errread = msvcrt.open_osfhandle(errread.Detach(), 0) + + self.text_mode = encoding or errors or text or universal_newlines + + # How long to resume waiting on a child after the first ^C. + # There is no right value for this. The purpose is to be polite + # yet remain good for interactive users trying to exit a tool. + self._sigint_wait_secs = 0.25 # 1/xkcd221.getRandomNumber() + + self._closed_child_pipe_fds = False + + if self.text_mode: + if bufsize == 1: + line_buffering = True + # Use the default buffer size for the underlying binary streams + # since they don't support line buffering. + bufsize = -1 + else: + line_buffering = False + + try: + if p2cwrite != -1: + self.stdin = io.open(p2cwrite, 'wb', bufsize) + if self.text_mode: + self.stdin = io.TextIOWrapper(self.stdin, write_through=True, + line_buffering=line_buffering, + encoding=encoding, errors=errors) + if c2pread != -1: + self.stdout = io.open(c2pread, 'rb', bufsize) + if self.text_mode: + self.stdout = io.TextIOWrapper(self.stdout, + encoding=encoding, errors=errors) + if errread != -1: + self.stderr = io.open(errread, 'rb', bufsize) + if self.text_mode: + self.stderr = io.TextIOWrapper(self.stderr, + encoding=encoding, errors=errors) + + self._execute_child(args, executable, preexec_fn, close_fds, + pass_fds, cwd, env, + startupinfo, creationflags, shell, + p2cread, p2cwrite, + c2pread, c2pwrite, + errread, errwrite, + restore_signals, start_new_session) + except: + # Cleanup if the child failed starting. + for f in filter(None, (self.stdin, self.stdout, self.stderr)): + try: + f.close() + except OSError: + pass # Ignore EBADF or other errors. + + if not self._closed_child_pipe_fds: + to_close = [] + if stdin == PIPE: + to_close.append(p2cread) + if stdout == PIPE: + to_close.append(c2pwrite) + if stderr == PIPE: + to_close.append(errwrite) + if hasattr(self, '_devnull'): + to_close.append(self._devnull) + for fd in to_close: + try: + if _mswindows and isinstance(fd, Handle): + fd.Close() + else: + os.close(fd) + except OSError: + pass + + raise + + @property + def universal_newlines(self): + # universal_newlines as retained as an alias of text_mode for API + # compatibility. bpo-31756 + return self.text_mode + + @universal_newlines.setter + def universal_newlines(self, universal_newlines): + self.text_mode = bool(universal_newlines) + + def _translate_newlines(self, data, encoding, errors): + data = data.decode(encoding, errors) + return data.replace("\r\n", "\n").replace("\r", "\n") + + def __enter__(self): + return self + + def __exit__(self, exc_type, value, traceback): + if self.stdout: + self.stdout.close() + if self.stderr: + self.stderr.close() + try: # Flushing a BufferedWriter may raise an error + if self.stdin: + self.stdin.close() + finally: + if exc_type == KeyboardInterrupt: + # https://bugs.python.org/issue25942 + # In the case of a KeyboardInterrupt we assume the SIGINT + # was also already sent to our child processes. We can't + # block indefinitely as that is not user friendly. + # If we have not already waited a brief amount of time in + # an interrupted .wait() or .communicate() call, do so here + # for consistency. + if self._sigint_wait_secs > 0: + try: + self._wait(timeout=self._sigint_wait_secs) + except TimeoutExpired: + pass + self._sigint_wait_secs = 0 # Note that this has been done. + return # resume the KeyboardInterrupt + + # Wait for the process to terminate, to avoid zombies. + self.wait() + + def __del__(self, _maxsize=sys.maxsize, _warn=warnings.warn): + if not self._child_created: + # We didn't get to successfully create a child process. + return + if self.returncode is None: + # Not reading subprocess exit status creates a zombie process which + # is only destroyed at the parent python process exit + _warn("subprocess %s is still running" % self.pid, + ResourceWarning, source=self) + # In case the child hasn't been waited on, check if it's done. + self._internal_poll(_deadstate=_maxsize) + if self.returncode is None and _active is not None: + # Child is still running, keep us alive until we can wait on it. + _active.append(self) + + def _get_devnull(self): + if not hasattr(self, '_devnull'): + self._devnull = os.open(os.devnull, os.O_RDWR) + return self._devnull + + def _stdin_write(self, input): + if input: + try: + self.stdin.write(input) + except BrokenPipeError: + pass # communicate() must ignore broken pipe errors. + except OSError as exc: + if exc.errno == errno.EINVAL: + # bpo-19612, bpo-30418: On Windows, stdin.write() fails + # with EINVAL if the child process exited or if the child + # process is still running but closed the pipe. + pass + else: + raise + + try: + self.stdin.close() + except BrokenPipeError: + pass # communicate() must ignore broken pipe errors. + except OSError as exc: + if exc.errno == errno.EINVAL: + pass + else: + raise + + def communicate(self, input=None, timeout=None): + """Interact with process: Send data to stdin and close it. + Read data from stdout and stderr, until end-of-file is + reached. Wait for process to terminate. + + The optional "input" argument should be data to be sent to the + child process, or None, if no data should be sent to the child. + communicate() returns a tuple (stdout, stderr). + + By default, all communication is in bytes, and therefore any + "input" should be bytes, and the (stdout, stderr) will be bytes. + If in text mode (indicated by self.text_mode), any "input" should + be a string, and (stdout, stderr) will be strings decoded + according to locale encoding, or by "encoding" if set. Text mode + is triggered by setting any of text, encoding, errors or + universal_newlines. + """ + + if self._communication_started and input: + raise ValueError("Cannot send input after starting communication") + + # Optimization: If we are not worried about timeouts, we haven't + # started communicating, and we have one or zero pipes, using select() + # or threads is unnecessary. + if (timeout is None and not self._communication_started and + [self.stdin, self.stdout, self.stderr].count(None) >= 2): + stdout = None + stderr = None + if self.stdin: + self._stdin_write(input) + elif self.stdout: + stdout = self.stdout.read() + self.stdout.close() + elif self.stderr: + stderr = self.stderr.read() + self.stderr.close() + self.wait() + else: + if timeout is not None: + endtime = _time() + timeout + else: + endtime = None + + try: + stdout, stderr = self._communicate(input, endtime, timeout) + except KeyboardInterrupt: + # https://bugs.python.org/issue25942 + # See the detailed comment in .wait(). + if timeout is not None: + sigint_timeout = min(self._sigint_wait_secs, + self._remaining_time(endtime)) + else: + sigint_timeout = self._sigint_wait_secs + self._sigint_wait_secs = 0 # nothing else should wait. + try: + self._wait(timeout=sigint_timeout) + except TimeoutExpired: + pass + raise # resume the KeyboardInterrupt + + finally: + self._communication_started = True + + sts = self.wait(timeout=self._remaining_time(endtime)) + + return (stdout, stderr) + + + def poll(self): + """Check if child process has terminated. Set and return returncode + attribute.""" + return self._internal_poll() + + + def _remaining_time(self, endtime): + """Convenience for _communicate when computing timeouts.""" + if endtime is None: + return None + else: + return endtime - _time() + + + def _check_timeout(self, endtime, orig_timeout, stdout_seq, stderr_seq, + skip_check_and_raise=False): + """Convenience for checking if a timeout has expired.""" + if endtime is None: + return + if skip_check_and_raise or _time() > endtime: + raise TimeoutExpired( + self.args, orig_timeout, + output=b''.join(stdout_seq) if stdout_seq else None, + stderr=b''.join(stderr_seq) if stderr_seq else None) + + + def wait(self, timeout=None): + """Wait for child process to terminate; returns self.returncode.""" + if timeout is not None: + endtime = _time() + timeout + try: + return self._wait(timeout=timeout) + except KeyboardInterrupt: + # https://bugs.python.org/issue25942 + # The first keyboard interrupt waits briefly for the child to + # exit under the common assumption that it also received the ^C + # generated SIGINT and will exit rapidly. + if timeout is not None: + sigint_timeout = min(self._sigint_wait_secs, + self._remaining_time(endtime)) + else: + sigint_timeout = self._sigint_wait_secs + self._sigint_wait_secs = 0 # nothing else should wait. + try: + self._wait(timeout=sigint_timeout) + except TimeoutExpired: + pass + raise # resume the KeyboardInterrupt + + def _close_pipe_fds(self, + p2cread, p2cwrite, + c2pread, c2pwrite, + errread, errwrite): + # self._devnull is not always defined. + devnull_fd = getattr(self, '_devnull', None) + + with contextlib.ExitStack() as stack: + if _mswindows: + if p2cread != -1: + stack.callback(p2cread.Close) + if c2pwrite != -1: + stack.callback(c2pwrite.Close) + if errwrite != -1: + stack.callback(errwrite.Close) + else: + if p2cread != -1 and p2cwrite != -1 and p2cread != devnull_fd: + stack.callback(os.close, p2cread) + if c2pwrite != -1 and c2pread != -1 and c2pwrite != devnull_fd: + stack.callback(os.close, c2pwrite) + if errwrite != -1 and errread != -1 and errwrite != devnull_fd: + stack.callback(os.close, errwrite) + + if devnull_fd is not None: + stack.callback(os.close, devnull_fd) + + # Prevent a double close of these handles/fds from __init__ on error. + self._closed_child_pipe_fds = True + + if _mswindows: + # + # Windows methods + # + def _get_handles(self, stdin, stdout, stderr): + """Construct and return tuple with IO objects: + p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite + """ + if stdin is None and stdout is None and stderr is None: + return (-1, -1, -1, -1, -1, -1) + + p2cread, p2cwrite = -1, -1 + c2pread, c2pwrite = -1, -1 + errread, errwrite = -1, -1 + + if stdin is None: + p2cread = _winapi.GetStdHandle(_winapi.STD_INPUT_HANDLE) + if p2cread is None: + p2cread, _ = _winapi.CreatePipe(None, 0) + p2cread = Handle(p2cread) + _winapi.CloseHandle(_) + elif stdin == PIPE: + p2cread, p2cwrite = _winapi.CreatePipe(None, 0) + p2cread, p2cwrite = Handle(p2cread), Handle(p2cwrite) + elif stdin == DEVNULL: + p2cread = msvcrt.get_osfhandle(self._get_devnull()) + elif isinstance(stdin, int): + p2cread = msvcrt.get_osfhandle(stdin) + else: + # Assuming file-like object + p2cread = msvcrt.get_osfhandle(stdin.fileno()) + p2cread = self._make_inheritable(p2cread) + + if stdout is None: + c2pwrite = _winapi.GetStdHandle(_winapi.STD_OUTPUT_HANDLE) + if c2pwrite is None: + _, c2pwrite = _winapi.CreatePipe(None, 0) + c2pwrite = Handle(c2pwrite) + _winapi.CloseHandle(_) + elif stdout == PIPE: + c2pread, c2pwrite = _winapi.CreatePipe(None, 0) + c2pread, c2pwrite = Handle(c2pread), Handle(c2pwrite) + elif stdout == DEVNULL: + c2pwrite = msvcrt.get_osfhandle(self._get_devnull()) + elif isinstance(stdout, int): + c2pwrite = msvcrt.get_osfhandle(stdout) + else: + # Assuming file-like object + c2pwrite = msvcrt.get_osfhandle(stdout.fileno()) + c2pwrite = self._make_inheritable(c2pwrite) + + if stderr is None: + errwrite = _winapi.GetStdHandle(_winapi.STD_ERROR_HANDLE) + if errwrite is None: + _, errwrite = _winapi.CreatePipe(None, 0) + errwrite = Handle(errwrite) + _winapi.CloseHandle(_) + elif stderr == PIPE: + errread, errwrite = _winapi.CreatePipe(None, 0) + errread, errwrite = Handle(errread), Handle(errwrite) + elif stderr == STDOUT: + errwrite = c2pwrite + elif stderr == DEVNULL: + errwrite = msvcrt.get_osfhandle(self._get_devnull()) + elif isinstance(stderr, int): + errwrite = msvcrt.get_osfhandle(stderr) + else: + # Assuming file-like object + errwrite = msvcrt.get_osfhandle(stderr.fileno()) + errwrite = self._make_inheritable(errwrite) + + return (p2cread, p2cwrite, + c2pread, c2pwrite, + errread, errwrite) + + + def _make_inheritable(self, handle): + """Return a duplicate of handle, which is inheritable""" + h = _winapi.DuplicateHandle( + _winapi.GetCurrentProcess(), handle, + _winapi.GetCurrentProcess(), 0, 1, + _winapi.DUPLICATE_SAME_ACCESS) + return Handle(h) + + + def _filter_handle_list(self, handle_list): + """Filter out console handles that can't be used + in lpAttributeList["handle_list"] and make sure the list + isn't empty. This also removes duplicate handles.""" + # An handle with it's lowest two bits set might be a special console + # handle that if passed in lpAttributeList["handle_list"], will + # cause it to fail. + return list({handle for handle in handle_list + if handle & 0x3 != 0x3 + or _winapi.GetFileType(handle) != + _winapi.FILE_TYPE_CHAR}) + + + def _execute_child(self, args, executable, preexec_fn, close_fds, + pass_fds, cwd, env, + startupinfo, creationflags, shell, + p2cread, p2cwrite, + c2pread, c2pwrite, + errread, errwrite, + unused_restore_signals, unused_start_new_session): + """Execute program (MS Windows version)""" + + assert not pass_fds, "pass_fds not supported on Windows." + + if isinstance(args, str): + pass + elif isinstance(args, bytes): + if shell: + raise TypeError('bytes args is not allowed on Windows') + args = list2cmdline([args]) + elif isinstance(args, os.PathLike): + if shell: + raise TypeError('path-like args is not allowed when ' + 'shell is true') + args = list2cmdline([args]) + else: + args = list2cmdline(args) + + if executable is not None: + executable = os.fsdecode(executable) + + # Process startup details + if startupinfo is None: + startupinfo = STARTUPINFO() + else: + # bpo-34044: Copy STARTUPINFO since it is modified above, + # so the caller can reuse it multiple times. + startupinfo = startupinfo.copy() + + use_std_handles = -1 not in (p2cread, c2pwrite, errwrite) + if use_std_handles: + startupinfo.dwFlags |= _winapi.STARTF_USESTDHANDLES + startupinfo.hStdInput = p2cread + startupinfo.hStdOutput = c2pwrite + startupinfo.hStdError = errwrite + + attribute_list = startupinfo.lpAttributeList + have_handle_list = bool(attribute_list and + "handle_list" in attribute_list and + attribute_list["handle_list"]) + + # If we were given an handle_list or need to create one + if have_handle_list or (use_std_handles and close_fds): + if attribute_list is None: + attribute_list = startupinfo.lpAttributeList = {} + handle_list = attribute_list["handle_list"] = \ + list(attribute_list.get("handle_list", [])) + + if use_std_handles: + handle_list += [int(p2cread), int(c2pwrite), int(errwrite)] + + handle_list[:] = self._filter_handle_list(handle_list) + + if handle_list: + if not close_fds: + warnings.warn("startupinfo.lpAttributeList['handle_list'] " + "overriding close_fds", RuntimeWarning) + + # When using the handle_list we always request to inherit + # handles but the only handles that will be inherited are + # the ones in the handle_list + close_fds = False + + if shell: + startupinfo.dwFlags |= _winapi.STARTF_USESHOWWINDOW + startupinfo.wShowWindow = _winapi.SW_HIDE + if not executable: + # gh-101283: without a fully-qualified path, before Windows + # checks the system directories, it first looks in the + # application directory, and also the current directory if + # NeedCurrentDirectoryForExePathW(ExeName) is true, so try + # to avoid executing unqualified "cmd.exe". + comspec = os.environ.get('ComSpec') + if not comspec: + system_root = os.environ.get('SystemRoot', '') + comspec = os.path.join(system_root, 'System32', 'cmd.exe') + if not os.path.isabs(comspec): + raise FileNotFoundError('shell not found: neither %ComSpec% nor %SystemRoot% is set') + if os.path.isabs(comspec): + executable = comspec + else: + comspec = executable + + args = '{} /c "{}"'.format (comspec, args) + + if cwd is not None: + cwd = os.fsdecode(cwd) + + sys.audit("subprocess.Popen", executable, args, cwd, env) + + # Start the process + try: + hp, ht, pid, tid = _winapi.CreateProcess(executable, args, + # no special security + None, None, + int(not close_fds), + creationflags, + env, + cwd, + startupinfo) + finally: + # Child is launched. Close the parent's copy of those pipe + # handles that only the child should have open. You need + # to make sure that no handles to the write end of the + # output pipe are maintained in this process or else the + # pipe will not close when the child process exits and the + # ReadFile will hang. + self._close_pipe_fds(p2cread, p2cwrite, + c2pread, c2pwrite, + errread, errwrite) + + # Retain the process handle, but close the thread handle + self._child_created = True + self._handle = Handle(hp) + self.pid = pid + _winapi.CloseHandle(ht) + + def _internal_poll(self, _deadstate=None, + _WaitForSingleObject=_winapi.WaitForSingleObject, + _WAIT_OBJECT_0=_winapi.WAIT_OBJECT_0, + _GetExitCodeProcess=_winapi.GetExitCodeProcess): + """Check if child process has terminated. Returns returncode + attribute. + + This method is called by __del__, so it can only refer to objects + in its local scope. + + """ + if self.returncode is None: + if _WaitForSingleObject(self._handle, 0) == _WAIT_OBJECT_0: + self.returncode = _GetExitCodeProcess(self._handle) + return self.returncode + + + def _wait(self, timeout): + """Internal implementation of wait() on Windows.""" + if timeout is None: + timeout_millis = _winapi.INFINITE + else: + timeout_millis = int(timeout * 1000) + if self.returncode is None: + # API note: Returns immediately if timeout_millis == 0. + result = _winapi.WaitForSingleObject(self._handle, + timeout_millis) + if result == _winapi.WAIT_TIMEOUT: + raise TimeoutExpired(self.args, timeout) + self.returncode = _winapi.GetExitCodeProcess(self._handle) + return self.returncode + + + def _readerthread(self, fh, buffer): + buffer.append(fh.read()) + fh.close() + + + def _communicate(self, input, endtime, orig_timeout): + # Start reader threads feeding into a list hanging off of this + # object, unless they've already been started. + if self.stdout and not hasattr(self, "_stdout_buff"): + self._stdout_buff = [] + self.stdout_thread = \ + threading.Thread(target=self._readerthread, + args=(self.stdout, self._stdout_buff)) + self.stdout_thread.daemon = True + self.stdout_thread.start() + if self.stderr and not hasattr(self, "_stderr_buff"): + self._stderr_buff = [] + self.stderr_thread = \ + threading.Thread(target=self._readerthread, + args=(self.stderr, self._stderr_buff)) + self.stderr_thread.daemon = True + self.stderr_thread.start() + + if self.stdin: + self._stdin_write(input) + + # Wait for the reader threads, or time out. If we time out, the + # threads remain reading and the fds left open in case the user + # calls communicate again. + if self.stdout is not None: + self.stdout_thread.join(self._remaining_time(endtime)) + if self.stdout_thread.is_alive(): + raise TimeoutExpired(self.args, orig_timeout) + if self.stderr is not None: + self.stderr_thread.join(self._remaining_time(endtime)) + if self.stderr_thread.is_alive(): + raise TimeoutExpired(self.args, orig_timeout) + + # Collect the output from and close both pipes, now that we know + # both have been read successfully. + stdout = None + stderr = None + if self.stdout: + stdout = self._stdout_buff + self.stdout.close() + if self.stderr: + stderr = self._stderr_buff + self.stderr.close() + + # All data exchanged. Translate lists into strings. + stdout = stdout[0] if stdout else None + stderr = stderr[0] if stderr else None + + return (stdout, stderr) + + def send_signal(self, sig): + """Send a signal to the process.""" + # Don't signal a process that we know has already died. + if self.returncode is not None: + return + if sig == signal.SIGTERM: + self.terminate() + elif sig == signal.CTRL_C_EVENT: + os.kill(self.pid, signal.CTRL_C_EVENT) + elif sig == signal.CTRL_BREAK_EVENT: + os.kill(self.pid, signal.CTRL_BREAK_EVENT) + else: + raise ValueError("Unsupported signal: {}".format(sig)) + + def terminate(self): + """Terminates the process.""" + # Don't terminate a process that we know has already died. + if self.returncode is not None: + return + try: + _winapi.TerminateProcess(self._handle, 1) + except PermissionError: + # ERROR_ACCESS_DENIED (winerror 5) is received when the + # process already died. + rc = _winapi.GetExitCodeProcess(self._handle) + if rc == _winapi.STILL_ACTIVE: + raise + self.returncode = rc + + kill = terminate + + else: + # + # POSIX methods + # + def _get_handles(self, stdin, stdout, stderr): + """Construct and return tuple with IO objects: + p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite + """ + p2cread, p2cwrite = -1, -1 + c2pread, c2pwrite = -1, -1 + errread, errwrite = -1, -1 + + if stdin is None: + pass + elif stdin == PIPE: + p2cread, p2cwrite = os.pipe() + elif stdin == DEVNULL: + p2cread = self._get_devnull() + elif isinstance(stdin, int): + p2cread = stdin + else: + # Assuming file-like object + p2cread = stdin.fileno() + + if stdout is None: + pass + elif stdout == PIPE: + c2pread, c2pwrite = os.pipe() + elif stdout == DEVNULL: + c2pwrite = self._get_devnull() + elif isinstance(stdout, int): + c2pwrite = stdout + else: + # Assuming file-like object + c2pwrite = stdout.fileno() + + if stderr is None: + pass + elif stderr == PIPE: + errread, errwrite = os.pipe() + elif stderr == STDOUT: + if c2pwrite != -1: + errwrite = c2pwrite + else: # child's stdout is not set, use parent's stdout + errwrite = sys.__stdout__.fileno() + elif stderr == DEVNULL: + errwrite = self._get_devnull() + elif isinstance(stderr, int): + errwrite = stderr + else: + # Assuming file-like object + errwrite = stderr.fileno() + + return (p2cread, p2cwrite, + c2pread, c2pwrite, + errread, errwrite) + + + def _posix_spawn(self, args, executable, env, restore_signals, + p2cread, p2cwrite, + c2pread, c2pwrite, + errread, errwrite): + """Execute program using os.posix_spawn().""" + if env is None: + env = os.environ + + kwargs = {} + if restore_signals: + # See _Py_RestoreSignals() in Python/pylifecycle.c + sigset = [] + for signame in ('SIGPIPE', 'SIGXFZ', 'SIGXFSZ'): + signum = getattr(signal, signame, None) + if signum is not None: + sigset.append(signum) + kwargs['setsigdef'] = sigset + + file_actions = [] + for fd in (p2cwrite, c2pread, errread): + if fd != -1: + file_actions.append((os.POSIX_SPAWN_CLOSE, fd)) + for fd, fd2 in ( + (p2cread, 0), + (c2pwrite, 1), + (errwrite, 2), + ): + if fd != -1: + file_actions.append((os.POSIX_SPAWN_DUP2, fd, fd2)) + if file_actions: + kwargs['file_actions'] = file_actions + + self.pid = os.posix_spawn(executable, args, env, **kwargs) + self._child_created = True + + self._close_pipe_fds(p2cread, p2cwrite, + c2pread, c2pwrite, + errread, errwrite) + + def _execute_child(self, args, executable, preexec_fn, close_fds, + pass_fds, cwd, env, + startupinfo, creationflags, shell, + p2cread, p2cwrite, + c2pread, c2pwrite, + errread, errwrite, + restore_signals, start_new_session): + """Execute program (POSIX version)""" + + if isinstance(args, (str, bytes)): + args = [args] + elif isinstance(args, os.PathLike): + if shell: + raise TypeError('path-like args is not allowed when ' + 'shell is true') + args = [args] + else: + args = list(args) + + if shell: + # On Android the default shell is at '/system/bin/sh'. + unix_shell = ('/system/bin/sh' if + hasattr(sys, 'getandroidapilevel') else '/bin/sh') + args = [unix_shell, "-c"] + args + if executable: + args[0] = executable + + if executable is None: + executable = args[0] + + sys.audit("subprocess.Popen", executable, args, cwd, env) + + if (_USE_POSIX_SPAWN + and os.path.dirname(executable) + and preexec_fn is None + and not close_fds + and not pass_fds + and cwd is None + and (p2cread == -1 or p2cread > 2) + and (c2pwrite == -1 or c2pwrite > 2) + and (errwrite == -1 or errwrite > 2) + and not start_new_session): + self._posix_spawn(args, executable, env, restore_signals, + p2cread, p2cwrite, + c2pread, c2pwrite, + errread, errwrite) + return + + orig_executable = executable + + # For transferring possible exec failure from child to parent. + # Data format: "exception name:hex errno:description" + # Pickle is not used; it is complex and involves memory allocation. + errpipe_read, errpipe_write = os.pipe() + # errpipe_write must not be in the standard io 0, 1, or 2 fd range. + low_fds_to_close = [] + while errpipe_write < 3: + low_fds_to_close.append(errpipe_write) + errpipe_write = os.dup(errpipe_write) + for low_fd in low_fds_to_close: + os.close(low_fd) + try: + try: + # We must avoid complex work that could involve + # malloc or free in the child process to avoid + # potential deadlocks, thus we do all this here. + # and pass it to fork_exec() + + if env is not None: + env_list = [] + for k, v in env.items(): + k = os.fsencode(k) + if b'=' in k: + raise ValueError("illegal environment variable name") + env_list.append(k + b'=' + os.fsencode(v)) + else: + env_list = None # Use execv instead of execve. + executable = os.fsencode(executable) + if os.path.dirname(executable): + executable_list = (executable,) + else: + # This matches the behavior of os._execvpe(). + executable_list = tuple( + os.path.join(os.fsencode(dir), executable) + for dir in os.get_exec_path(env)) + fds_to_keep = set(pass_fds) + fds_to_keep.add(errpipe_write) + self.pid = _posixsubprocess.fork_exec( + args, executable_list, + close_fds, tuple(sorted(map(int, fds_to_keep))), + cwd, env_list, + p2cread, p2cwrite, c2pread, c2pwrite, + errread, errwrite, + errpipe_read, errpipe_write, + restore_signals, start_new_session, preexec_fn) + self._child_created = True + finally: + # be sure the FD is closed no matter what + os.close(errpipe_write) + + self._close_pipe_fds(p2cread, p2cwrite, + c2pread, c2pwrite, + errread, errwrite) + + # Wait for exec to fail or succeed; possibly raising an + # exception (limited in size) + errpipe_data = bytearray() + while True: + part = os.read(errpipe_read, 50000) + errpipe_data += part + if not part or len(errpipe_data) > 50000: + break + finally: + # be sure the FD is closed no matter what + os.close(errpipe_read) + + if errpipe_data: + try: + pid, sts = os.waitpid(self.pid, 0) + if pid == self.pid: + self._handle_exitstatus(sts) + else: + self.returncode = sys.maxsize + except ChildProcessError: + pass + + try: + exception_name, hex_errno, err_msg = ( + errpipe_data.split(b':', 2)) + # The encoding here should match the encoding + # written in by the subprocess implementations + # like _posixsubprocess + err_msg = err_msg.decode() + except ValueError: + exception_name = b'SubprocessError' + hex_errno = b'0' + err_msg = 'Bad exception data from child: {!r}'.format( + bytes(errpipe_data)) + child_exception_type = getattr( + builtins, exception_name.decode('ascii'), + SubprocessError) + if issubclass(child_exception_type, OSError) and hex_errno: + errno_num = int(hex_errno, 16) + child_exec_never_called = (err_msg == "noexec") + if child_exec_never_called: + err_msg = "" + # The error must be from chdir(cwd). + err_filename = cwd + else: + err_filename = orig_executable + if errno_num != 0: + err_msg = os.strerror(errno_num) + raise child_exception_type(errno_num, err_msg, err_filename) + raise child_exception_type(err_msg) + + + def _handle_exitstatus(self, sts, _WIFSIGNALED=os.WIFSIGNALED, + _WTERMSIG=os.WTERMSIG, _WIFEXITED=os.WIFEXITED, + _WEXITSTATUS=os.WEXITSTATUS, _WIFSTOPPED=os.WIFSTOPPED, + _WSTOPSIG=os.WSTOPSIG): + """All callers to this function MUST hold self._waitpid_lock.""" + # This method is called (indirectly) by __del__, so it cannot + # refer to anything outside of its local scope. + if _WIFSIGNALED(sts): + self.returncode = -_WTERMSIG(sts) + elif _WIFEXITED(sts): + self.returncode = _WEXITSTATUS(sts) + elif _WIFSTOPPED(sts): + self.returncode = -_WSTOPSIG(sts) + else: + # Should never happen + raise SubprocessError("Unknown child exit status!") + + + def _internal_poll(self, _deadstate=None, _waitpid=os.waitpid, + _WNOHANG=os.WNOHANG, _ECHILD=errno.ECHILD): + """Check if child process has terminated. Returns returncode + attribute. + + This method is called by __del__, so it cannot reference anything + outside of the local scope (nor can any methods it calls). + + """ + if self.returncode is None: + if not self._waitpid_lock.acquire(False): + # Something else is busy calling waitpid. Don't allow two + # at once. We know nothing yet. + return None + try: + if self.returncode is not None: + return self.returncode # Another thread waited. + pid, sts = _waitpid(self.pid, _WNOHANG) + if pid == self.pid: + self._handle_exitstatus(sts) + except OSError as e: + if _deadstate is not None: + self.returncode = _deadstate + elif e.errno == _ECHILD: + # This happens if SIGCLD is set to be ignored or + # waiting for child processes has otherwise been + # disabled for our process. This child is dead, we + # can't get the status. + # http://bugs.python.org/issue15756 + self.returncode = 0 + finally: + self._waitpid_lock.release() + return self.returncode + + + def _try_wait(self, wait_flags): + """All callers to this function MUST hold self._waitpid_lock.""" + try: + (pid, sts) = os.waitpid(self.pid, wait_flags) + except ChildProcessError: + # This happens if SIGCLD is set to be ignored or waiting + # for child processes has otherwise been disabled for our + # process. This child is dead, we can't get the status. + pid = self.pid + sts = 0 + return (pid, sts) + + + def _wait(self, timeout): + """Internal implementation of wait() on POSIX.""" + if self.returncode is not None: + return self.returncode + + if timeout is not None: + endtime = _time() + timeout + # Enter a busy loop if we have a timeout. This busy loop was + # cribbed from Lib/threading.py in Thread.wait() at r71065. + delay = 0.0005 # 500 us -> initial delay of 1 ms + while True: + if self._waitpid_lock.acquire(False): + try: + if self.returncode is not None: + break # Another thread waited. + (pid, sts) = self._try_wait(os.WNOHANG) + assert pid == self.pid or pid == 0 + if pid == self.pid: + self._handle_exitstatus(sts) + break + finally: + self._waitpid_lock.release() + remaining = self._remaining_time(endtime) + if remaining <= 0: + raise TimeoutExpired(self.args, timeout) + delay = min(delay * 2, remaining, .05) + time.sleep(delay) + else: + while self.returncode is None: + with self._waitpid_lock: + if self.returncode is not None: + break # Another thread waited. + (pid, sts) = self._try_wait(0) + # Check the pid and loop as waitpid has been known to + # return 0 even without WNOHANG in odd situations. + # http://bugs.python.org/issue14396. + if pid == self.pid: + self._handle_exitstatus(sts) + return self.returncode + + + def _communicate(self, input, endtime, orig_timeout): + if self.stdin and not self._communication_started: + # Flush stdio buffer. This might block, if the user has + # been writing to .stdin in an uncontrolled fashion. + try: + self.stdin.flush() + except BrokenPipeError: + pass # communicate() must ignore BrokenPipeError. + if not input: + try: + self.stdin.close() + except BrokenPipeError: + pass # communicate() must ignore BrokenPipeError. + + stdout = None + stderr = None + + # Only create this mapping if we haven't already. + if not self._communication_started: + self._fileobj2output = {} + if self.stdout: + self._fileobj2output[self.stdout] = [] + if self.stderr: + self._fileobj2output[self.stderr] = [] + + if self.stdout: + stdout = self._fileobj2output[self.stdout] + if self.stderr: + stderr = self._fileobj2output[self.stderr] + + self._save_input(input) + + if self._input: + input_view = memoryview(self._input) + + with _PopenSelector() as selector: + if self.stdin and input: + selector.register(self.stdin, selectors.EVENT_WRITE) + if self.stdout and not self.stdout.closed: + selector.register(self.stdout, selectors.EVENT_READ) + if self.stderr and not self.stderr.closed: + selector.register(self.stderr, selectors.EVENT_READ) + + while selector.get_map(): + timeout = self._remaining_time(endtime) + if timeout is not None and timeout < 0: + self._check_timeout(endtime, orig_timeout, + stdout, stderr, + skip_check_and_raise=True) + raise RuntimeError( # Impossible :) + '_check_timeout(..., skip_check_and_raise=True) ' + 'failed to raise TimeoutExpired.') + + ready = selector.select(timeout) + self._check_timeout(endtime, orig_timeout, stdout, stderr) + + # XXX Rewrite these to use non-blocking I/O on the file + # objects; they are no longer using C stdio! + + for key, events in ready: + if key.fileobj is self.stdin: + chunk = input_view[self._input_offset : + self._input_offset + _PIPE_BUF] + try: + self._input_offset += os.write(key.fd, chunk) + except BrokenPipeError: + selector.unregister(key.fileobj) + key.fileobj.close() + else: + if self._input_offset >= len(self._input): + selector.unregister(key.fileobj) + key.fileobj.close() + elif key.fileobj in (self.stdout, self.stderr): + data = os.read(key.fd, 32768) + if not data: + selector.unregister(key.fileobj) + key.fileobj.close() + self._fileobj2output[key.fileobj].append(data) + + self.wait(timeout=self._remaining_time(endtime)) + + # All data exchanged. Translate lists into strings. + if stdout is not None: + stdout = b''.join(stdout) + if stderr is not None: + stderr = b''.join(stderr) + + # Translate newlines, if requested. + # This also turns bytes into strings. + if self.text_mode: + if stdout is not None: + stdout = self._translate_newlines(stdout, + self.stdout.encoding, + self.stdout.errors) + if stderr is not None: + stderr = self._translate_newlines(stderr, + self.stderr.encoding, + self.stderr.errors) + + return (stdout, stderr) + + + def _save_input(self, input): + # This method is called from the _communicate_with_*() methods + # so that if we time out while communicating, we can continue + # sending input if we retry. + if self.stdin and self._input is None: + self._input_offset = 0 + self._input = input + if input is not None and self.text_mode: + self._input = self._input.encode(self.stdin.encoding, + self.stdin.errors) + + + def send_signal(self, sig): + """Send a signal to the process.""" + # Skip signalling a process that we know has already died. + if self.returncode is None: + os.kill(self.pid, sig) + + def terminate(self): + """Terminate the process with SIGTERM + """ + self.send_signal(signal.SIGTERM) + + def kill(self): + """Kill the process with SIGKILL + """ + self.send_signal(signal.SIGKILL) diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/sysconfig.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/sysconfig.py new file mode 100644 index 0000000000000000000000000000000000000000..b4401c83aac7f564e7f125ecaf7337251d1e6532 --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/sysconfig.py @@ -0,0 +1,732 @@ +"""Access to Python's configuration information.""" + +import os +import sys +from os.path import pardir, realpath + +__all__ = [ + 'get_config_h_filename', + 'get_config_var', + 'get_config_vars', + 'get_makefile_filename', + 'get_path', + 'get_path_names', + 'get_paths', + 'get_platform', + 'get_python_version', + 'get_scheme_names', + 'parse_config_h', +] + +# Keys for get_config_var() that are never converted to Python integers. +_ALWAYS_STR = { + 'MACOSX_DEPLOYMENT_TARGET', +} + +_INSTALL_SCHEMES = { + 'posix_prefix': { + 'stdlib': '{installed_base}/lib/python{py_version_short}', + 'platstdlib': '{platbase}/lib/python{py_version_short}', + 'purelib': '{base}/lib/python{py_version_short}/site-packages', + 'platlib': '{platbase}/lib/python{py_version_short}/site-packages', + 'include': + '{installed_base}/include/python{py_version_short}{abiflags}', + 'platinclude': + '{installed_platbase}/include/python{py_version_short}{abiflags}', + 'scripts': '{base}/bin', + 'data': '{base}', + }, + 'posix_home': { + 'stdlib': '{installed_base}/lib/python', + 'platstdlib': '{base}/lib/python', + 'purelib': '{base}/lib/python', + 'platlib': '{base}/lib/python', + 'include': '{installed_base}/include/python', + 'platinclude': '{installed_base}/include/python', + 'scripts': '{base}/bin', + 'data': '{base}', + }, + 'nt': { + 'stdlib': '{installed_base}/Lib', + 'platstdlib': '{base}/Lib', + 'purelib': '{base}/Lib/site-packages', + 'platlib': '{base}/Lib/site-packages', + 'include': '{installed_base}/Include', + 'platinclude': '{installed_base}/Include', + 'scripts': '{base}/Scripts', + 'data': '{base}', + }, + # NOTE: When modifying "purelib" scheme, update site._get_path() too. + 'nt_user': { + 'stdlib': '{userbase}/Python{py_version_nodot}', + 'platstdlib': '{userbase}/Python{py_version_nodot}', + 'purelib': '{userbase}/Python{py_version_nodot}/site-packages', + 'platlib': '{userbase}/Python{py_version_nodot}/site-packages', + 'include': '{userbase}/Python{py_version_nodot}/Include', + 'scripts': '{userbase}/Python{py_version_nodot}/Scripts', + 'data': '{userbase}', + }, + 'posix_user': { + 'stdlib': '{userbase}/lib/python{py_version_short}', + 'platstdlib': '{userbase}/lib/python{py_version_short}', + 'purelib': '{userbase}/lib/python{py_version_short}/site-packages', + 'platlib': '{userbase}/lib/python{py_version_short}/site-packages', + 'include': '{userbase}/include/python{py_version_short}', + 'scripts': '{userbase}/bin', + 'data': '{userbase}', + }, + 'osx_framework_user': { + 'stdlib': '{userbase}/lib/python', + 'platstdlib': '{userbase}/lib/python', + 'purelib': '{userbase}/lib/python/site-packages', + 'platlib': '{userbase}/lib/python/site-packages', + 'include': '{userbase}/include', + 'scripts': '{userbase}/bin', + 'data': '{userbase}', + }, + } + +_SCHEME_KEYS = ('stdlib', 'platstdlib', 'purelib', 'platlib', 'include', + 'scripts', 'data') + + # FIXME don't rely on sys.version here, its format is an implementation detail + # of CPython, use sys.version_info or sys.hexversion +_PY_VERSION = sys.version.split()[0] +_PY_VERSION_SHORT = '%d.%d' % sys.version_info[:2] +_PY_VERSION_SHORT_NO_DOT = '%d%d' % sys.version_info[:2] +_PREFIX = os.path.normpath(sys.prefix) +_BASE_PREFIX = os.path.normpath(sys.base_prefix) +_EXEC_PREFIX = os.path.normpath(sys.exec_prefix) +_BASE_EXEC_PREFIX = os.path.normpath(sys.base_exec_prefix) +_CONFIG_VARS = None +_USER_BASE = None + + +def _safe_realpath(path): + try: + return realpath(path) + except OSError: + return path + +if sys.executable: + _PROJECT_BASE = os.path.dirname(_safe_realpath(sys.executable)) +else: + # sys.executable can be empty if argv[0] has been changed and Python is + # unable to retrieve the real program name + _PROJECT_BASE = _safe_realpath(os.getcwd()) + +if (os.name == 'nt' and + _PROJECT_BASE.lower().endswith(('\\pcbuild\\win32', '\\pcbuild\\amd64'))): + _PROJECT_BASE = _safe_realpath(os.path.join(_PROJECT_BASE, pardir, pardir)) + +# set for cross builds +if "_PYTHON_PROJECT_BASE" in os.environ: + _PROJECT_BASE = _safe_realpath(os.environ["_PYTHON_PROJECT_BASE"]) + +def _is_python_source_dir(d): + for fn in ("Setup", "Setup.local"): + if os.path.isfile(os.path.join(d, "Modules", fn)): + return True + return False + +_sys_home = getattr(sys, '_home', None) + +if os.name == 'nt': + def _fix_pcbuild(d): + if d and os.path.normcase(d).startswith( + os.path.normcase(os.path.join(_PREFIX, "PCbuild"))): + return _PREFIX + return d + _PROJECT_BASE = _fix_pcbuild(_PROJECT_BASE) + _sys_home = _fix_pcbuild(_sys_home) + +def is_python_build(check_home=False): + if check_home and _sys_home: + return _is_python_source_dir(_sys_home) + return _is_python_source_dir(_PROJECT_BASE) + +_PYTHON_BUILD = is_python_build(True) + +if _PYTHON_BUILD: + for scheme in ('posix_prefix', 'posix_home'): + _INSTALL_SCHEMES[scheme]['include'] = '{srcdir}/Include' + _INSTALL_SCHEMES[scheme]['platinclude'] = '{projectbase}/.' + + +def _subst_vars(s, local_vars): + try: + return s.format(**local_vars) + except KeyError: + try: + return s.format(**os.environ) + except KeyError as var: + raise AttributeError('{%s}' % var) from None + +def _extend_dict(target_dict, other_dict): + target_keys = target_dict.keys() + for key, value in other_dict.items(): + if key in target_keys: + continue + target_dict[key] = value + + +def _expand_vars(scheme, vars): + res = {} + if vars is None: + vars = {} + _extend_dict(vars, get_config_vars()) + + for key, value in _INSTALL_SCHEMES[scheme].items(): + if os.name in ('posix', 'nt'): + value = os.path.expanduser(value) + res[key] = os.path.normpath(_subst_vars(value, vars)) + return res + + +def _get_default_scheme(): + if os.name == 'posix': + # the default scheme for posix is posix_prefix + return 'posix_prefix' + return os.name + + +# NOTE: site.py has copy of this function. +# Sync it when modify this function. +def _getuserbase(): + env_base = os.environ.get("PYTHONUSERBASE", None) + if env_base: + return env_base + + def joinuser(*args): + return os.path.expanduser(os.path.join(*args)) + + if os.name == "nt": + base = os.environ.get("APPDATA") or "~" + return joinuser(base, "Python") + + if sys.platform == "darwin" and sys._framework: + return joinuser("~", "Library", sys._framework, + "%d.%d" % sys.version_info[:2]) + + return joinuser("~", ".local") + + +def _parse_makefile(filename, vars=None): + """Parse a Makefile-style file. + + A dictionary containing name/value pairs is returned. If an + optional dictionary is passed in as the second argument, it is + used instead of a new dictionary. + """ + # Regexes needed for parsing Makefile (and similar syntaxes, + # like old-style Setup files). + import re + _variable_rx = re.compile(r"([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)") + _findvar1_rx = re.compile(r"\$\(([A-Za-z][A-Za-z0-9_]*)\)") + _findvar2_rx = re.compile(r"\${([A-Za-z][A-Za-z0-9_]*)}") + + if vars is None: + vars = {} + done = {} + notdone = {} + + with open(filename, errors="surrogateescape") as f: + lines = f.readlines() + + for line in lines: + if line.startswith('#') or line.strip() == '': + continue + m = _variable_rx.match(line) + if m: + n, v = m.group(1, 2) + v = v.strip() + # `$$' is a literal `$' in make + tmpv = v.replace('$$', '') + + if "$" in tmpv: + notdone[n] = v + else: + try: + if n in _ALWAYS_STR: + raise ValueError + + v = int(v) + except ValueError: + # insert literal `$' + done[n] = v.replace('$$', '$') + else: + done[n] = v + + # do variable interpolation here + variables = list(notdone.keys()) + + # Variables with a 'PY_' prefix in the makefile. These need to + # be made available without that prefix through sysconfig. + # Special care is needed to ensure that variable expansion works, even + # if the expansion uses the name without a prefix. + renamed_variables = ('CFLAGS', 'LDFLAGS', 'CPPFLAGS') + + while len(variables) > 0: + for name in tuple(variables): + value = notdone[name] + m1 = _findvar1_rx.search(value) + m2 = _findvar2_rx.search(value) + if m1 and m2: + m = m1 if m1.start() < m2.start() else m2 + else: + m = m1 if m1 else m2 + if m is not None: + n = m.group(1) + found = True + if n in done: + item = str(done[n]) + elif n in notdone: + # get it on a subsequent round + found = False + elif n in os.environ: + # do it like make: fall back to environment + item = os.environ[n] + + elif n in renamed_variables: + if (name.startswith('PY_') and + name[3:] in renamed_variables): + item = "" + + elif 'PY_' + n in notdone: + found = False + + else: + item = str(done['PY_' + n]) + + else: + done[n] = item = "" + + if found: + after = value[m.end():] + value = value[:m.start()] + item + after + if "$" in after: + notdone[name] = value + else: + try: + if name in _ALWAYS_STR: + raise ValueError + value = int(value) + except ValueError: + done[name] = value.strip() + else: + done[name] = value + variables.remove(name) + + if name.startswith('PY_') \ + and name[3:] in renamed_variables: + + name = name[3:] + if name not in done: + done[name] = value + + else: + # bogus variable reference (e.g. "prefix=$/opt/python"); + # just drop it since we can't deal + done[name] = value + variables.remove(name) + + # strip spurious spaces + for k, v in done.items(): + if isinstance(v, str): + done[k] = v.strip() + + # save the results in the global dictionary + vars.update(done) + return vars + + +def get_makefile_filename(): + """Return the path of the Makefile.""" + if _PYTHON_BUILD: + return os.path.join(_sys_home or _PROJECT_BASE, "Makefile") + if hasattr(sys, 'abiflags'): + config_dir_name = 'config-%s%s' % (_PY_VERSION_SHORT, sys.abiflags) + else: + config_dir_name = 'config' + if hasattr(sys.implementation, '_multiarch'): + config_dir_name += '-%s' % sys.implementation._multiarch + return os.path.join(get_path('stdlib'), config_dir_name, 'Makefile') + + +def _get_sysconfigdata_name(check_exists=False): + for envvar in ('_PYTHON_SYSCONFIGDATA_NAME', '_CONDA_PYTHON_SYSCONFIGDATA_NAME'): + res = os.environ.get(envvar, None) + if res and check_exists: + try: + import importlib.util + loader = importlib.util.find_spec(res) + except: + res = None + if res: + return res + return '_sysconfigdata_{abi}_{platform}_{multiarch}'.format( + abi=sys.abiflags, + platform=sys.platform, + multiarch=getattr(sys.implementation, '_multiarch', '')) + + +def _generate_posix_vars(): + """Generate the Python module containing build-time variables.""" + import pprint + vars = {} + # load the installed Makefile: + makefile = get_makefile_filename() + try: + _parse_makefile(makefile, vars) + except OSError as e: + msg = "invalid Python installation: unable to open %s" % makefile + if hasattr(e, "strerror"): + msg = msg + " (%s)" % e.strerror + raise OSError(msg) + # load the installed pyconfig.h: + config_h = get_config_h_filename() + try: + with open(config_h) as f: + parse_config_h(f, vars) + except OSError as e: + msg = "invalid Python installation: unable to open %s" % config_h + if hasattr(e, "strerror"): + msg = msg + " (%s)" % e.strerror + raise OSError(msg) + # On AIX, there are wrong paths to the linker scripts in the Makefile + # -- these paths are relative to the Python source, but when installed + # the scripts are in another directory. + if _PYTHON_BUILD: + vars['BLDSHARED'] = vars['LDSHARED'] + + # There's a chicken-and-egg situation on OS X with regards to the + # _sysconfigdata module after the changes introduced by #15298: + # get_config_vars() is called by get_platform() as part of the + # `make pybuilddir.txt` target -- which is a precursor to the + # _sysconfigdata.py module being constructed. Unfortunately, + # get_config_vars() eventually calls _init_posix(), which attempts + # to import _sysconfigdata, which we won't have built yet. In order + # for _init_posix() to work, if we're on Darwin, just mock up the + # _sysconfigdata module manually and populate it with the build vars. + # This is more than sufficient for ensuring the subsequent call to + # get_platform() succeeds. + name = _get_sysconfigdata_name() + if 'darwin' in sys.platform: + import types + module = types.ModuleType(name) + module.build_time_vars = vars + sys.modules[name] = module + + pybuilddir = 'build/lib.%s-%s' % (get_platform(), _PY_VERSION_SHORT) + if hasattr(sys, "gettotalrefcount"): + pybuilddir += '-pydebug' + os.makedirs(pybuilddir, exist_ok=True) + destfile = os.path.join(pybuilddir, name + '.py') + + with open(destfile, 'w', encoding='utf8') as f: + f.write('# system configuration generated and used by' + ' the sysconfig module\n') + f.write('build_time_vars = ') + pprint.pprint(vars, stream=f) + + # Create file used for sys.path fixup -- see Modules/getpath.c + with open('pybuilddir.txt', 'w', encoding='utf8') as f: + f.write(pybuilddir) + +def _init_posix(vars): + """Initialize the module as appropriate for POSIX systems.""" + # _sysconfigdata is generated at build time, see _generate_posix_vars() + name = _get_sysconfigdata_name(True) + _temp = __import__(name, globals(), locals(), ['build_time_vars'], 0) + build_time_vars = _temp.build_time_vars + vars.update(build_time_vars) + +def _init_non_posix(vars): + """Initialize the module as appropriate for NT""" + # set basic install directories + import _imp + vars['LIBDEST'] = get_path('stdlib') + vars['BINLIBDEST'] = get_path('platstdlib') + vars['INCLUDEPY'] = get_path('include') + vars['EXT_SUFFIX'] = _imp.extension_suffixes()[0] + vars['EXE'] = '.exe' + vars['VERSION'] = _PY_VERSION_SHORT_NO_DOT + vars['BINDIR'] = os.path.dirname(_safe_realpath(sys.executable)) + +# +# public APIs +# + + +def parse_config_h(fp, vars=None): + """Parse a config.h-style file. + + A dictionary containing name/value pairs is returned. If an + optional dictionary is passed in as the second argument, it is + used instead of a new dictionary. + """ + if vars is None: + vars = {} + import re + define_rx = re.compile("#define ([A-Z][A-Za-z0-9_]+) (.*)\n") + undef_rx = re.compile("/[*] #undef ([A-Z][A-Za-z0-9_]+) [*]/\n") + + while True: + line = fp.readline() + if not line: + break + m = define_rx.match(line) + if m: + n, v = m.group(1, 2) + try: + if n in _ALWAYS_STR: + raise ValueError + v = int(v) + except ValueError: + pass + vars[n] = v + else: + m = undef_rx.match(line) + if m: + vars[m.group(1)] = 0 + return vars + + +def get_config_h_filename(): + """Return the path of pyconfig.h.""" + if _PYTHON_BUILD: + if os.name == "nt": + inc_dir = os.path.join(_sys_home or _PROJECT_BASE, "PC") + else: + inc_dir = _sys_home or _PROJECT_BASE + else: + inc_dir = get_path('platinclude') + return os.path.join(inc_dir, 'pyconfig.h') + + +def get_scheme_names(): + """Return a tuple containing the schemes names.""" + return tuple(sorted(_INSTALL_SCHEMES)) + + +def get_path_names(): + """Return a tuple containing the paths names.""" + return _SCHEME_KEYS + + +def get_paths(scheme=_get_default_scheme(), vars=None, expand=True): + """Return a mapping containing an install scheme. + + ``scheme`` is the install scheme name. If not provided, it will + return the default scheme for the current platform. + """ + if expand: + return _expand_vars(scheme, vars) + else: + return _INSTALL_SCHEMES[scheme] + + +def get_path(name, scheme=_get_default_scheme(), vars=None, expand=True): + """Return a path corresponding to the scheme. + + ``scheme`` is the install scheme name. + """ + return get_paths(scheme, vars, expand)[name] + + +def get_config_vars(*args): + """With no arguments, return a dictionary of all configuration + variables relevant for the current platform. + + On Unix, this means every variable defined in Python's installed Makefile; + On Windows it's a much smaller set. + + With arguments, return a list of values that result from looking up + each argument in the configuration variable dictionary. + """ + global _CONFIG_VARS + if _CONFIG_VARS is None: + _CONFIG_VARS = {} + # Normalized versions of prefix and exec_prefix are handy to have; + # in fact, these are the standard versions used most places in the + # Distutils. + _CONFIG_VARS['prefix'] = _PREFIX + _CONFIG_VARS['exec_prefix'] = _EXEC_PREFIX + _CONFIG_VARS['py_version'] = _PY_VERSION + _CONFIG_VARS['py_version_short'] = _PY_VERSION_SHORT + _CONFIG_VARS['py_version_nodot'] = _PY_VERSION_SHORT_NO_DOT + _CONFIG_VARS['installed_base'] = _BASE_PREFIX + _CONFIG_VARS['base'] = _PREFIX + _CONFIG_VARS['installed_platbase'] = _BASE_EXEC_PREFIX + _CONFIG_VARS['platbase'] = _EXEC_PREFIX + _CONFIG_VARS['projectbase'] = _PROJECT_BASE + try: + _CONFIG_VARS['abiflags'] = sys.abiflags + except AttributeError: + # sys.abiflags may not be defined on all platforms. + _CONFIG_VARS['abiflags'] = '' + + if os.name == 'nt': + _init_non_posix(_CONFIG_VARS) + if os.name == 'posix': + _init_posix(_CONFIG_VARS) + # For backward compatibility, see issue19555 + SO = _CONFIG_VARS.get('EXT_SUFFIX') + if SO is not None: + _CONFIG_VARS['SO'] = SO + # Setting 'userbase' is done below the call to the + # init function to enable using 'get_config_var' in + # the init-function. + _CONFIG_VARS['userbase'] = _getuserbase() + + # Always convert srcdir to an absolute path + srcdir = _CONFIG_VARS.get('srcdir', _PROJECT_BASE) + if os.name == 'posix': + if _PYTHON_BUILD: + # If srcdir is a relative path (typically '.' or '..') + # then it should be interpreted relative to the directory + # containing Makefile. + base = os.path.dirname(get_makefile_filename()) + srcdir = os.path.join(base, srcdir) + else: + # srcdir is not meaningful since the installation is + # spread about the filesystem. We choose the + # directory containing the Makefile since we know it + # exists. + srcdir = os.path.dirname(get_makefile_filename()) + _CONFIG_VARS['srcdir'] = _safe_realpath(srcdir) + + # OS X platforms require special customization to handle + # multi-architecture, multi-os-version installers + if sys.platform == 'darwin': + import _osx_support + _osx_support.customize_config_vars(_CONFIG_VARS) + + if args: + vals = [] + for name in args: + vals.append(_CONFIG_VARS.get(name)) + return vals + else: + return _CONFIG_VARS + + +def get_config_var(name): + """Return the value of a single variable using the dictionary returned by + 'get_config_vars()'. + + Equivalent to get_config_vars().get(name) + """ + if name == 'SO': + import warnings + warnings.warn('SO is deprecated, use EXT_SUFFIX', DeprecationWarning, 2) + return get_config_vars().get(name) + + +def get_platform(): + """Return a string that identifies the current platform. + + This is used mainly to distinguish platform-specific build directories and + platform-specific built distributions. Typically includes the OS name and + version and the architecture (as supplied by 'os.uname()'), although the + exact information included depends on the OS; on Linux, the kernel version + isn't particularly important. + + Examples of returned values: + linux-i586 + linux-alpha (?) + solaris-2.6-sun4u + + Windows will return one of: + win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc) + win32 (all others - specifically, sys.platform is returned) + + For other non-POSIX platforms, currently just returns 'sys.platform'. + + """ + if os.name == 'nt': + if 'amd64' in sys.version.lower(): + return 'win-amd64' + if '(arm)' in sys.version.lower(): + return 'win-arm32' + if '(arm64)' in sys.version.lower(): + return 'win-arm64' + return sys.platform + + if os.name != "posix" or not hasattr(os, 'uname'): + # XXX what about the architecture? NT is Intel or Alpha + return sys.platform + + # Set for cross builds explicitly + if "_PYTHON_HOST_PLATFORM" in os.environ: + return os.environ["_PYTHON_HOST_PLATFORM"] + + # Try to distinguish various flavours of Unix + osname, host, release, version, machine = os.uname() + + # Convert the OS name to lowercase, remove '/' characters, and translate + # spaces (for "Power Macintosh") + osname = osname.lower().replace('/', '') + machine = machine.replace(' ', '_') + machine = machine.replace('/', '-') + + if osname[:5] == "linux": + # At least on Linux/Intel, 'machine' is the processor -- + # i386, etc. + # XXX what about Alpha, SPARC, etc? + return "%s-%s" % (osname, machine) + elif osname[:5] == "sunos": + if release[0] >= "5": # SunOS 5 == Solaris 2 + osname = "solaris" + release = "%d.%s" % (int(release[0]) - 3, release[2:]) + # We can't use "platform.architecture()[0]" because a + # bootstrap problem. We use a dict to get an error + # if some suspicious happens. + bitness = {2147483647:"32bit", 9223372036854775807:"64bit"} + machine += ".%s" % bitness[sys.maxsize] + # fall through to standard osname-release-machine representation + elif osname[:3] == "aix": + return "%s-%s.%s" % (osname, version, release) + elif osname[:6] == "cygwin": + osname = "cygwin" + import re + rel_re = re.compile(r'[\d.]+') + m = rel_re.match(release) + if m: + release = m.group() + elif osname[:6] == "darwin": + import _osx_support + osname, release, machine = _osx_support.get_platform_osx( + get_config_vars(), + osname, release, machine) + + return "%s-%s-%s" % (osname, release, machine) + + +def get_python_version(): + return _PY_VERSION_SHORT + + +def _print_dict(title, data): + for index, (key, value) in enumerate(sorted(data.items())): + if index == 0: + print('%s: ' % (title)) + print('\t%s = "%s"' % (key, value)) + + +def _main(): + """Display all information sysconfig detains.""" + if '--generate-posix-vars' in sys.argv: + _generate_posix_vars() + return + print('Platform: "%s"' % get_platform()) + print('Python version: "%s"' % get_python_version()) + print('Current installation scheme: "%s"' % _get_default_scheme()) + print() + _print_dict('Paths', get_paths()) + print() + _print_dict('Variables', get_config_vars()) + + +if __name__ == '__main__': + _main() diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/tempfile.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/tempfile.py new file mode 100644 index 0000000000000000000000000000000000000000..8f9cb6c3ca350041765821969d025c45424b7144 --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/tempfile.py @@ -0,0 +1,831 @@ +"""Temporary files. + +This module provides generic, low- and high-level interfaces for +creating temporary files and directories. All of the interfaces +provided by this module can be used without fear of race conditions +except for 'mktemp'. 'mktemp' is subject to race conditions and +should not be used; it is provided for backward compatibility only. + +The default path names are returned as str. If you supply bytes as +input, all return values will be in bytes. Ex: + + >>> tempfile.mkstemp() + (4, '/tmp/tmptpu9nin8') + >>> tempfile.mkdtemp(suffix=b'') + b'/tmp/tmppbi8f0hy' + +This module also provides some data items to the user: + + TMP_MAX - maximum number of names that will be tried before + giving up. + tempdir - If this is set to a string before the first use of + any routine from this module, it will be considered as + another candidate location to store temporary files. +""" + +__all__ = [ + "NamedTemporaryFile", "TemporaryFile", # high level safe interfaces + "SpooledTemporaryFile", "TemporaryDirectory", + "mkstemp", "mkdtemp", # low level safe interfaces + "mktemp", # deprecated unsafe interface + "TMP_MAX", "gettempprefix", # constants + "tempdir", "gettempdir", + "gettempprefixb", "gettempdirb", + ] + + +# Imports. + +import functools as _functools +import warnings as _warnings +import io as _io +import os as _os +import shutil as _shutil +import errno as _errno +from random import Random as _Random +import sys as _sys +import weakref as _weakref +import _thread +_allocate_lock = _thread.allocate_lock + +_text_openflags = _os.O_RDWR | _os.O_CREAT | _os.O_EXCL +if hasattr(_os, 'O_NOFOLLOW'): + _text_openflags |= _os.O_NOFOLLOW + +_bin_openflags = _text_openflags +if hasattr(_os, 'O_BINARY'): + _bin_openflags |= _os.O_BINARY + +if hasattr(_os, 'TMP_MAX'): + TMP_MAX = _os.TMP_MAX +else: + TMP_MAX = 10000 + +# This variable _was_ unused for legacy reasons, see issue 10354. +# But as of 3.5 we actually use it at runtime so changing it would +# have a possibly desirable side effect... But we do not want to support +# that as an API. It is undocumented on purpose. Do not depend on this. +template = "tmp" + +# Internal routines. + +_once_lock = _allocate_lock() + + +def _exists(fn): + try: + _os.lstat(fn) + except OSError: + return False + else: + return True + + +def _infer_return_type(*args): + """Look at the type of all args and divine their implied return type.""" + return_type = None + for arg in args: + if arg is None: + continue + if isinstance(arg, bytes): + if return_type is str: + raise TypeError("Can't mix bytes and non-bytes in " + "path components.") + return_type = bytes + else: + if return_type is bytes: + raise TypeError("Can't mix bytes and non-bytes in " + "path components.") + return_type = str + if return_type is None: + return str # tempfile APIs return a str by default. + return return_type + + +def _sanitize_params(prefix, suffix, dir): + """Common parameter processing for most APIs in this module.""" + output_type = _infer_return_type(prefix, suffix, dir) + if suffix is None: + suffix = output_type() + if prefix is None: + if output_type is str: + prefix = template + else: + prefix = _os.fsencode(template) + if dir is None: + if output_type is str: + dir = gettempdir() + else: + dir = gettempdirb() + return prefix, suffix, dir, output_type + + +class _RandomNameSequence: + """An instance of _RandomNameSequence generates an endless + sequence of unpredictable strings which can safely be incorporated + into file names. Each string is eight characters long. Multiple + threads can safely use the same instance at the same time. + + _RandomNameSequence is an iterator.""" + + characters = "abcdefghijklmnopqrstuvwxyz0123456789_" + + @property + def rng(self): + cur_pid = _os.getpid() + if cur_pid != getattr(self, '_rng_pid', None): + self._rng = _Random() + self._rng_pid = cur_pid + return self._rng + + def __iter__(self): + return self + + def __next__(self): + c = self.characters + choose = self.rng.choice + letters = [choose(c) for dummy in range(8)] + return ''.join(letters) + +def _candidate_tempdir_list(): + """Generate a list of candidate temporary directories which + _get_default_tempdir will try.""" + + dirlist = [] + + # First, try the environment. + for envname in 'TMPDIR', 'TEMP', 'TMP': + dirname = _os.getenv(envname) + if dirname: dirlist.append(dirname) + + # Failing that, try OS-specific locations. + if _os.name == 'nt': + dirlist.extend([ _os.path.expanduser(r'~\AppData\Local\Temp'), + _os.path.expandvars(r'%SYSTEMROOT%\Temp'), + r'c:\temp', r'c:\tmp', r'\temp', r'\tmp' ]) + else: + dirlist.extend([ '/tmp', '/var/tmp', '/usr/tmp' ]) + + # As a last resort, the current directory. + try: + dirlist.append(_os.getcwd()) + except (AttributeError, OSError): + dirlist.append(_os.curdir) + + return dirlist + +def _get_default_tempdir(): + """Calculate the default directory to use for temporary files. + This routine should be called exactly once. + + We determine whether or not a candidate temp dir is usable by + trying to create and write to a file in that directory. If this + is successful, the test file is deleted. To prevent denial of + service, the name of the test file must be randomized.""" + + namer = _RandomNameSequence() + dirlist = _candidate_tempdir_list() + + for dir in dirlist: + if dir != _os.curdir: + dir = _os.path.abspath(dir) + # Try only a few names per directory. + for seq in range(100): + name = next(namer) + filename = _os.path.join(dir, name) + try: + fd = _os.open(filename, _bin_openflags, 0o600) + try: + try: + with _io.open(fd, 'wb', closefd=False) as fp: + fp.write(b'blat') + finally: + _os.close(fd) + finally: + _os.unlink(filename) + return dir + except FileExistsError: + pass + except PermissionError: + # This exception is thrown when a directory with the chosen name + # already exists on windows. + if (_os.name == 'nt' and _os.path.isdir(dir) and + _os.access(dir, _os.W_OK)): + continue + break # no point trying more names in this directory + except OSError: + break # no point trying more names in this directory + raise FileNotFoundError(_errno.ENOENT, + "No usable temporary directory found in %s" % + dirlist) + +_name_sequence = None + +def _get_candidate_names(): + """Common setup sequence for all user-callable interfaces.""" + + global _name_sequence + if _name_sequence is None: + _once_lock.acquire() + try: + if _name_sequence is None: + _name_sequence = _RandomNameSequence() + finally: + _once_lock.release() + return _name_sequence + + +def _mkstemp_inner(dir, pre, suf, flags, output_type): + """Code common to mkstemp, TemporaryFile, and NamedTemporaryFile.""" + + names = _get_candidate_names() + if output_type is bytes: + names = map(_os.fsencode, names) + + for seq in range(TMP_MAX): + name = next(names) + file = _os.path.join(dir, pre + name + suf) + _sys.audit("tempfile.mkstemp", file) + try: + fd = _os.open(file, flags, 0o600) + except FileExistsError: + continue # try again + except PermissionError: + # This exception is thrown when a directory with the chosen name + # already exists on windows. + if (_os.name == 'nt' and _os.path.isdir(dir) and + _os.access(dir, _os.W_OK)): + continue + else: + raise + return (fd, _os.path.abspath(file)) + + raise FileExistsError(_errno.EEXIST, + "No usable temporary file name found") + + +# User visible interfaces. + +def gettempprefix(): + """The default prefix for temporary directories.""" + return template + +def gettempprefixb(): + """The default prefix for temporary directories as bytes.""" + return _os.fsencode(gettempprefix()) + +tempdir = None + +def gettempdir(): + """Accessor for tempfile.tempdir.""" + global tempdir + if tempdir is None: + _once_lock.acquire() + try: + if tempdir is None: + tempdir = _get_default_tempdir() + finally: + _once_lock.release() + return tempdir + +def gettempdirb(): + """A bytes version of tempfile.gettempdir().""" + return _os.fsencode(gettempdir()) + +def mkstemp(suffix=None, prefix=None, dir=None, text=False): + """User-callable function to create and return a unique temporary + file. The return value is a pair (fd, name) where fd is the + file descriptor returned by os.open, and name is the filename. + + If 'suffix' is not None, the file name will end with that suffix, + otherwise there will be no suffix. + + If 'prefix' is not None, the file name will begin with that prefix, + otherwise a default prefix is used. + + If 'dir' is not None, the file will be created in that directory, + otherwise a default directory is used. + + If 'text' is specified and true, the file is opened in text + mode. Else (the default) the file is opened in binary mode. + + If any of 'suffix', 'prefix' and 'dir' are not None, they must be the + same type. If they are bytes, the returned name will be bytes; str + otherwise. + + The file is readable and writable only by the creating user ID. + If the operating system uses permission bits to indicate whether a + file is executable, the file is executable by no one. The file + descriptor is not inherited by children of this process. + + Caller is responsible for deleting the file when done with it. + """ + + prefix, suffix, dir, output_type = _sanitize_params(prefix, suffix, dir) + + if text: + flags = _text_openflags + else: + flags = _bin_openflags + + return _mkstemp_inner(dir, prefix, suffix, flags, output_type) + + +def mkdtemp(suffix=None, prefix=None, dir=None): + """User-callable function to create and return a unique temporary + directory. The return value is the pathname of the directory. + + Arguments are as for mkstemp, except that the 'text' argument is + not accepted. + + The directory is readable, writable, and searchable only by the + creating user. + + Caller is responsible for deleting the directory when done with it. + """ + + prefix, suffix, dir, output_type = _sanitize_params(prefix, suffix, dir) + + names = _get_candidate_names() + if output_type is bytes: + names = map(_os.fsencode, names) + + for seq in range(TMP_MAX): + name = next(names) + file = _os.path.join(dir, prefix + name + suffix) + _sys.audit("tempfile.mkdtemp", file) + try: + _os.mkdir(file, 0o700) + except FileExistsError: + continue # try again + except PermissionError: + # This exception is thrown when a directory with the chosen name + # already exists on windows. + if (_os.name == 'nt' and _os.path.isdir(dir) and + _os.access(dir, _os.W_OK)): + continue + else: + raise + return file + + raise FileExistsError(_errno.EEXIST, + "No usable temporary directory name found") + +def mktemp(suffix="", prefix=template, dir=None): + """User-callable function to return a unique temporary file name. The + file is not created. + + Arguments are similar to mkstemp, except that the 'text' argument is + not accepted, and suffix=None, prefix=None and bytes file names are not + supported. + + THIS FUNCTION IS UNSAFE AND SHOULD NOT BE USED. The file name may + refer to a file that did not exist at some point, but by the time + you get around to creating it, someone else may have beaten you to + the punch. + """ + +## from warnings import warn as _warn +## _warn("mktemp is a potential security risk to your program", +## RuntimeWarning, stacklevel=2) + + if dir is None: + dir = gettempdir() + + names = _get_candidate_names() + for seq in range(TMP_MAX): + name = next(names) + file = _os.path.join(dir, prefix + name + suffix) + if not _exists(file): + return file + + raise FileExistsError(_errno.EEXIST, + "No usable temporary filename found") + + +class _TemporaryFileCloser: + """A separate object allowing proper closing of a temporary file's + underlying file object, without adding a __del__ method to the + temporary file.""" + + file = None # Set here since __del__ checks it + close_called = False + + def __init__(self, file, name, delete=True): + self.file = file + self.name = name + self.delete = delete + + # NT provides delete-on-close as a primitive, so we don't need + # the wrapper to do anything special. We still use it so that + # file.name is useful (i.e. not "(fdopen)") with NamedTemporaryFile. + if _os.name != 'nt': + # Cache the unlinker so we don't get spurious errors at + # shutdown when the module-level "os" is None'd out. Note + # that this must be referenced as self.unlink, because the + # name TemporaryFileWrapper may also get None'd out before + # __del__ is called. + + def close(self, unlink=_os.unlink): + if not self.close_called and self.file is not None: + self.close_called = True + try: + self.file.close() + finally: + if self.delete: + unlink(self.name) + + # Need to ensure the file is deleted on __del__ + def __del__(self): + self.close() + + else: + def close(self): + if not self.close_called: + self.close_called = True + self.file.close() + + +class _TemporaryFileWrapper: + """Temporary file wrapper + + This class provides a wrapper around files opened for + temporary use. In particular, it seeks to automatically + remove the file when it is no longer needed. + """ + + def __init__(self, file, name, delete=True): + self.file = file + self.name = name + self.delete = delete + self._closer = _TemporaryFileCloser(file, name, delete) + + def __getattr__(self, name): + # Attribute lookups are delegated to the underlying file + # and cached for non-numeric results + # (i.e. methods are cached, closed and friends are not) + file = self.__dict__['file'] + a = getattr(file, name) + if hasattr(a, '__call__'): + func = a + @_functools.wraps(func) + def func_wrapper(*args, **kwargs): + return func(*args, **kwargs) + # Avoid closing the file as long as the wrapper is alive, + # see issue #18879. + func_wrapper._closer = self._closer + a = func_wrapper + if not isinstance(a, int): + setattr(self, name, a) + return a + + # The underlying __enter__ method returns the wrong object + # (self.file) so override it to return the wrapper + def __enter__(self): + self.file.__enter__() + return self + + # Need to trap __exit__ as well to ensure the file gets + # deleted when used in a with statement + def __exit__(self, exc, value, tb): + result = self.file.__exit__(exc, value, tb) + self.close() + return result + + def close(self): + """ + Close the temporary file, possibly deleting it. + """ + self._closer.close() + + # iter() doesn't use __getattr__ to find the __iter__ method + def __iter__(self): + # Don't return iter(self.file), but yield from it to avoid closing + # file as long as it's being used as iterator (see issue #23700). We + # can't use 'yield from' here because iter(file) returns the file + # object itself, which has a close method, and thus the file would get + # closed when the generator is finalized, due to PEP380 semantics. + for line in self.file: + yield line + + +def NamedTemporaryFile(mode='w+b', buffering=-1, encoding=None, + newline=None, suffix=None, prefix=None, + dir=None, delete=True, *, errors=None): + """Create and return a temporary file. + Arguments: + 'prefix', 'suffix', 'dir' -- as for mkstemp. + 'mode' -- the mode argument to io.open (default "w+b"). + 'buffering' -- the buffer size argument to io.open (default -1). + 'encoding' -- the encoding argument to io.open (default None) + 'newline' -- the newline argument to io.open (default None) + 'delete' -- whether the file is deleted on close (default True). + 'errors' -- the errors argument to io.open (default None) + The file is created as mkstemp() would do it. + + Returns an object with a file-like interface; the name of the file + is accessible as its 'name' attribute. The file will be automatically + deleted when it is closed unless the 'delete' argument is set to False. + """ + + prefix, suffix, dir, output_type = _sanitize_params(prefix, suffix, dir) + + flags = _bin_openflags + + # Setting O_TEMPORARY in the flags causes the OS to delete + # the file when it is closed. This is only supported by Windows. + if _os.name == 'nt' and delete: + flags |= _os.O_TEMPORARY + + (fd, name) = _mkstemp_inner(dir, prefix, suffix, flags, output_type) + try: + file = _io.open(fd, mode, buffering=buffering, + newline=newline, encoding=encoding, errors=errors) + + return _TemporaryFileWrapper(file, name, delete) + except BaseException: + _os.unlink(name) + _os.close(fd) + raise + +if _os.name != 'posix' or _sys.platform == 'cygwin': + # On non-POSIX and Cygwin systems, assume that we cannot unlink a file + # while it is open. + TemporaryFile = NamedTemporaryFile + +else: + # Is the O_TMPFILE flag available and does it work? + # The flag is set to False if os.open(dir, os.O_TMPFILE) raises an + # IsADirectoryError exception + _O_TMPFILE_WORKS = hasattr(_os, 'O_TMPFILE') + + def TemporaryFile(mode='w+b', buffering=-1, encoding=None, + newline=None, suffix=None, prefix=None, + dir=None, *, errors=None): + """Create and return a temporary file. + Arguments: + 'prefix', 'suffix', 'dir' -- as for mkstemp. + 'mode' -- the mode argument to io.open (default "w+b"). + 'buffering' -- the buffer size argument to io.open (default -1). + 'encoding' -- the encoding argument to io.open (default None) + 'newline' -- the newline argument to io.open (default None) + 'errors' -- the errors argument to io.open (default None) + The file is created as mkstemp() would do it. + + Returns an object with a file-like interface. The file has no + name, and will cease to exist when it is closed. + """ + global _O_TMPFILE_WORKS + + prefix, suffix, dir, output_type = _sanitize_params(prefix, suffix, dir) + + flags = _bin_openflags + if _O_TMPFILE_WORKS: + try: + flags2 = (flags | _os.O_TMPFILE) & ~_os.O_CREAT + fd = _os.open(dir, flags2, 0o600) + except IsADirectoryError: + # Linux kernel older than 3.11 ignores the O_TMPFILE flag: + # O_TMPFILE is read as O_DIRECTORY. Trying to open a directory + # with O_RDWR|O_DIRECTORY fails with IsADirectoryError, a + # directory cannot be open to write. Set flag to False to not + # try again. + _O_TMPFILE_WORKS = False + except OSError: + # The filesystem of the directory does not support O_TMPFILE. + # For example, OSError(95, 'Operation not supported'). + # + # On Linux kernel older than 3.11, trying to open a regular + # file (or a symbolic link to a regular file) with O_TMPFILE + # fails with NotADirectoryError, because O_TMPFILE is read as + # O_DIRECTORY. + pass + else: + try: + return _io.open(fd, mode, buffering=buffering, + newline=newline, encoding=encoding, + errors=errors) + except: + _os.close(fd) + raise + # Fallback to _mkstemp_inner(). + + (fd, name) = _mkstemp_inner(dir, prefix, suffix, flags, output_type) + try: + _os.unlink(name) + return _io.open(fd, mode, buffering=buffering, + newline=newline, encoding=encoding, errors=errors) + except: + _os.close(fd) + raise + +class SpooledTemporaryFile: + """Temporary file wrapper, specialized to switch from BytesIO + or StringIO to a real file when it exceeds a certain size or + when a fileno is needed. + """ + _rolled = False + + def __init__(self, max_size=0, mode='w+b', buffering=-1, + encoding=None, newline=None, + suffix=None, prefix=None, dir=None, *, errors=None): + if 'b' in mode: + self._file = _io.BytesIO() + else: + self._file = _io.TextIOWrapper(_io.BytesIO(), + encoding=encoding, errors=errors, + newline=newline) + self._max_size = max_size + self._rolled = False + self._TemporaryFileArgs = {'mode': mode, 'buffering': buffering, + 'suffix': suffix, 'prefix': prefix, + 'encoding': encoding, 'newline': newline, + 'dir': dir, 'errors': errors} + + def _check(self, file): + if self._rolled: return + max_size = self._max_size + if max_size and file.tell() > max_size: + self.rollover() + + def rollover(self): + if self._rolled: return + file = self._file + newfile = self._file = TemporaryFile(**self._TemporaryFileArgs) + del self._TemporaryFileArgs + + pos = file.tell() + if hasattr(newfile, 'buffer'): + newfile.buffer.write(file.detach().getvalue()) + else: + newfile.write(file.getvalue()) + newfile.seek(pos, 0) + + self._rolled = True + + # The method caching trick from NamedTemporaryFile + # won't work here, because _file may change from a + # BytesIO/StringIO instance to a real file. So we list + # all the methods directly. + + # Context management protocol + def __enter__(self): + if self._file.closed: + raise ValueError("Cannot enter context with closed file") + return self + + def __exit__(self, exc, value, tb): + self._file.close() + + # file protocol + def __iter__(self): + return self._file.__iter__() + + def close(self): + self._file.close() + + @property + def closed(self): + return self._file.closed + + @property + def encoding(self): + return self._file.encoding + + @property + def errors(self): + return self._file.errors + + def fileno(self): + self.rollover() + return self._file.fileno() + + def flush(self): + self._file.flush() + + def isatty(self): + return self._file.isatty() + + @property + def mode(self): + try: + return self._file.mode + except AttributeError: + return self._TemporaryFileArgs['mode'] + + @property + def name(self): + try: + return self._file.name + except AttributeError: + return None + + @property + def newlines(self): + return self._file.newlines + + def read(self, *args): + return self._file.read(*args) + + def readline(self, *args): + return self._file.readline(*args) + + def readlines(self, *args): + return self._file.readlines(*args) + + def seek(self, *args): + return self._file.seek(*args) + + @property + def softspace(self): + return self._file.softspace + + def tell(self): + return self._file.tell() + + def truncate(self, size=None): + if size is None: + self._file.truncate() + else: + if size > self._max_size: + self.rollover() + self._file.truncate(size) + + def write(self, s): + file = self._file + rv = file.write(s) + self._check(file) + return rv + + def writelines(self, iterable): + file = self._file + rv = file.writelines(iterable) + self._check(file) + return rv + + +class TemporaryDirectory(object): + """Create and return a temporary directory. This has the same + behavior as mkdtemp but can be used as a context manager. For + example: + + with TemporaryDirectory() as tmpdir: + ... + + Upon exiting the context, the directory and everything contained + in it are removed. + """ + + def __init__(self, suffix=None, prefix=None, dir=None): + self.name = mkdtemp(suffix, prefix, dir) + self._finalizer = _weakref.finalize( + self, self._cleanup, self.name, + warn_message="Implicitly cleaning up {!r}".format(self)) + + @classmethod + def _rmtree(cls, name): + def onerror(func, path, exc_info): + if issubclass(exc_info[0], PermissionError): + def resetperms(path): + try: + _os.chflags(path, 0) + except AttributeError: + pass + _os.chmod(path, 0o700) + + try: + if path != name: + resetperms(_os.path.dirname(path)) + resetperms(path) + + try: + _os.unlink(path) + # PermissionError is raised on FreeBSD for directories + except (IsADirectoryError, PermissionError): + cls._rmtree(path) + except FileNotFoundError: + pass + elif issubclass(exc_info[0], FileNotFoundError): + pass + else: + raise + + _shutil.rmtree(name, onerror=onerror) + + @classmethod + def _cleanup(cls, name, warn_message): + cls._rmtree(name) + _warnings.warn(warn_message, ResourceWarning) + + def __repr__(self): + return "<{} {!r}>".format(self.__class__.__name__, self.name) + + def __enter__(self): + return self.name + + def __exit__(self, exc, value, tb): + self.cleanup() + + def cleanup(self): + if self._finalizer.detach(): + self._rmtree(self.name) diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/this.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/this.py new file mode 100644 index 0000000000000000000000000000000000000000..e68dd3ff39b04ff857420b98889bee590b344024 --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/this.py @@ -0,0 +1,28 @@ +s = """Gur Mra bs Clguba, ol Gvz Crgref + +Ornhgvshy vf orggre guna htyl. +Rkcyvpvg vf orggre guna vzcyvpvg. +Fvzcyr vf orggre guna pbzcyrk. +Pbzcyrk vf orggre guna pbzcyvpngrq. +Syng vf orggre guna arfgrq. +Fcnefr vf orggre guna qrafr. +Ernqnovyvgl pbhagf. +Fcrpvny pnfrf nera'g fcrpvny rabhtu gb oernx gur ehyrf. +Nygubhtu cenpgvpnyvgl orngf chevgl. +Reebef fubhyq arire cnff fvyragyl. +Hayrff rkcyvpvgyl fvyraprq. +Va gur snpr bs nzovthvgl, ershfr gur grzcgngvba gb thrff. +Gurer fubhyq or bar-- naq cersrenoyl bayl bar --boivbhf jnl gb qb vg. +Nygubhtu gung jnl znl abg or boivbhf ng svefg hayrff lbh'er Qhgpu. +Abj vf orggre guna arire. +Nygubhtu arire vf bsgra orggre guna *evtug* abj. +Vs gur vzcyrzragngvba vf uneq gb rkcynva, vg'f n onq vqrn. +Vs gur vzcyrzragngvba vf rnfl gb rkcynva, vg znl or n tbbq vqrn. +Anzrfcnprf ner bar ubaxvat terng vqrn -- yrg'f qb zber bs gubfr!""" + +d = {} +for c in (65, 97): + for i in range(26): + d[chr(i+c)] = chr((i+13) % 26 + c) + +print("".join([d.get(c, c) for c in s])) diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/threading.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/threading.py new file mode 100644 index 0000000000000000000000000000000000000000..813dae2aa9f8e5d3b565482f033394ba3cbb9bc7 --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/threading.py @@ -0,0 +1,1466 @@ +"""Thread module emulating a subset of Java's threading model.""" + +import os as _os +import sys as _sys +import _thread + +from time import monotonic as _time +from _weakrefset import WeakSet +from itertools import islice as _islice, count as _count +try: + from _collections import deque as _deque +except ImportError: + from collections import deque as _deque + +# Note regarding PEP 8 compliant names +# This threading model was originally inspired by Java, and inherited +# the convention of camelCase function and method names from that +# language. Those original names are not in any imminent danger of +# being deprecated (even for Py3k),so this module provides them as an +# alias for the PEP 8 compliant names +# Note that using the new PEP 8 compliant names facilitates substitution +# with the multiprocessing module, which doesn't provide the old +# Java inspired names. + +__all__ = ['get_ident', 'active_count', 'Condition', 'current_thread', + 'enumerate', 'main_thread', 'TIMEOUT_MAX', + 'Event', 'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore', 'Thread', + 'Barrier', 'BrokenBarrierError', 'Timer', 'ThreadError', + 'setprofile', 'settrace', 'local', 'stack_size', + 'excepthook', 'ExceptHookArgs'] + +# Rename some stuff so "from threading import *" is safe +_start_new_thread = _thread.start_new_thread +_allocate_lock = _thread.allocate_lock +_set_sentinel = _thread._set_sentinel +get_ident = _thread.get_ident +try: + get_native_id = _thread.get_native_id + _HAVE_THREAD_NATIVE_ID = True + __all__.append('get_native_id') +except AttributeError: + _HAVE_THREAD_NATIVE_ID = False +ThreadError = _thread.error +try: + _CRLock = _thread.RLock +except AttributeError: + _CRLock = None +TIMEOUT_MAX = _thread.TIMEOUT_MAX +del _thread + + +# Support for profile and trace hooks + +_profile_hook = None +_trace_hook = None + +def setprofile(func): + """Set a profile function for all threads started from the threading module. + + The func will be passed to sys.setprofile() for each thread, before its + run() method is called. + + """ + global _profile_hook + _profile_hook = func + +def settrace(func): + """Set a trace function for all threads started from the threading module. + + The func will be passed to sys.settrace() for each thread, before its run() + method is called. + + """ + global _trace_hook + _trace_hook = func + +# Synchronization classes + +Lock = _allocate_lock + +def RLock(*args, **kwargs): + """Factory function that returns a new reentrant lock. + + A reentrant lock must be released by the thread that acquired it. Once a + thread has acquired a reentrant lock, the same thread may acquire it again + without blocking; the thread must release it once for each time it has + acquired it. + + """ + if _CRLock is None: + return _PyRLock(*args, **kwargs) + return _CRLock(*args, **kwargs) + +class _RLock: + """This class implements reentrant lock objects. + + A reentrant lock must be released by the thread that acquired it. Once a + thread has acquired a reentrant lock, the same thread may acquire it + again without blocking; the thread must release it once for each time it + has acquired it. + + """ + + def __init__(self): + self._block = _allocate_lock() + self._owner = None + self._count = 0 + + def __repr__(self): + owner = self._owner + try: + owner = _active[owner].name + except KeyError: + pass + return "<%s %s.%s object owner=%r count=%d at %s>" % ( + "locked" if self._block.locked() else "unlocked", + self.__class__.__module__, + self.__class__.__qualname__, + owner, + self._count, + hex(id(self)) + ) + + def acquire(self, blocking=True, timeout=-1): + """Acquire a lock, blocking or non-blocking. + + When invoked without arguments: if this thread already owns the lock, + increment the recursion level by one, and return immediately. Otherwise, + if another thread owns the lock, block until the lock is unlocked. Once + the lock is unlocked (not owned by any thread), then grab ownership, set + the recursion level to one, and return. If more than one thread is + blocked waiting until the lock is unlocked, only one at a time will be + able to grab ownership of the lock. There is no return value in this + case. + + When invoked with the blocking argument set to true, do the same thing + as when called without arguments, and return true. + + When invoked with the blocking argument set to false, do not block. If a + call without an argument would block, return false immediately; + otherwise, do the same thing as when called without arguments, and + return true. + + When invoked with the floating-point timeout argument set to a positive + value, block for at most the number of seconds specified by timeout + and as long as the lock cannot be acquired. Return true if the lock has + been acquired, false if the timeout has elapsed. + + """ + me = get_ident() + if self._owner == me: + self._count += 1 + return 1 + rc = self._block.acquire(blocking, timeout) + if rc: + self._owner = me + self._count = 1 + return rc + + __enter__ = acquire + + def release(self): + """Release a lock, decrementing the recursion level. + + If after the decrement it is zero, reset the lock to unlocked (not owned + by any thread), and if any other threads are blocked waiting for the + lock to become unlocked, allow exactly one of them to proceed. If after + the decrement the recursion level is still nonzero, the lock remains + locked and owned by the calling thread. + + Only call this method when the calling thread owns the lock. A + RuntimeError is raised if this method is called when the lock is + unlocked. + + There is no return value. + + """ + if self._owner != get_ident(): + raise RuntimeError("cannot release un-acquired lock") + self._count = count = self._count - 1 + if not count: + self._owner = None + self._block.release() + + def __exit__(self, t, v, tb): + self.release() + + # Internal methods used by condition variables + + def _acquire_restore(self, state): + self._block.acquire() + self._count, self._owner = state + + def _release_save(self): + if self._count == 0: + raise RuntimeError("cannot release un-acquired lock") + count = self._count + self._count = 0 + owner = self._owner + self._owner = None + self._block.release() + return (count, owner) + + def _is_owned(self): + return self._owner == get_ident() + +_PyRLock = _RLock + + +class Condition: + """Class that implements a condition variable. + + A condition variable allows one or more threads to wait until they are + notified by another thread. + + If the lock argument is given and not None, it must be a Lock or RLock + object, and it is used as the underlying lock. Otherwise, a new RLock object + is created and used as the underlying lock. + + """ + + def __init__(self, lock=None): + if lock is None: + lock = RLock() + self._lock = lock + # Export the lock's acquire() and release() methods + self.acquire = lock.acquire + self.release = lock.release + # If the lock defines _release_save() and/or _acquire_restore(), + # these override the default implementations (which just call + # release() and acquire() on the lock). Ditto for _is_owned(). + try: + self._release_save = lock._release_save + except AttributeError: + pass + try: + self._acquire_restore = lock._acquire_restore + except AttributeError: + pass + try: + self._is_owned = lock._is_owned + except AttributeError: + pass + self._waiters = _deque() + + def __enter__(self): + return self._lock.__enter__() + + def __exit__(self, *args): + return self._lock.__exit__(*args) + + def __repr__(self): + return "" % (self._lock, len(self._waiters)) + + def _release_save(self): + self._lock.release() # No state to save + + def _acquire_restore(self, x): + self._lock.acquire() # Ignore saved state + + def _is_owned(self): + # Return True if lock is owned by current_thread. + # This method is called only if _lock doesn't have _is_owned(). + if self._lock.acquire(0): + self._lock.release() + return False + else: + return True + + def wait(self, timeout=None): + """Wait until notified or until a timeout occurs. + + If the calling thread has not acquired the lock when this method is + called, a RuntimeError is raised. + + This method releases the underlying lock, and then blocks until it is + awakened by a notify() or notify_all() call for the same condition + variable in another thread, or until the optional timeout occurs. Once + awakened or timed out, it re-acquires the lock and returns. + + When the timeout argument is present and not None, it should be a + floating point number specifying a timeout for the operation in seconds + (or fractions thereof). + + When the underlying lock is an RLock, it is not released using its + release() method, since this may not actually unlock the lock when it + was acquired multiple times recursively. Instead, an internal interface + of the RLock class is used, which really unlocks it even when it has + been recursively acquired several times. Another internal interface is + then used to restore the recursion level when the lock is reacquired. + + """ + if not self._is_owned(): + raise RuntimeError("cannot wait on un-acquired lock") + waiter = _allocate_lock() + waiter.acquire() + self._waiters.append(waiter) + saved_state = self._release_save() + gotit = False + try: # restore state no matter what (e.g., KeyboardInterrupt) + if timeout is None: + waiter.acquire() + gotit = True + else: + if timeout > 0: + gotit = waiter.acquire(True, timeout) + else: + gotit = waiter.acquire(False) + return gotit + finally: + self._acquire_restore(saved_state) + if not gotit: + try: + self._waiters.remove(waiter) + except ValueError: + pass + + def wait_for(self, predicate, timeout=None): + """Wait until a condition evaluates to True. + + predicate should be a callable which result will be interpreted as a + boolean value. A timeout may be provided giving the maximum time to + wait. + + """ + endtime = None + waittime = timeout + result = predicate() + while not result: + if waittime is not None: + if endtime is None: + endtime = _time() + waittime + else: + waittime = endtime - _time() + if waittime <= 0: + break + self.wait(waittime) + result = predicate() + return result + + def notify(self, n=1): + """Wake up one or more threads waiting on this condition, if any. + + If the calling thread has not acquired the lock when this method is + called, a RuntimeError is raised. + + This method wakes up at most n of the threads waiting for the condition + variable; it is a no-op if no threads are waiting. + + """ + if not self._is_owned(): + raise RuntimeError("cannot notify on un-acquired lock") + all_waiters = self._waiters + waiters_to_notify = _deque(_islice(all_waiters, n)) + if not waiters_to_notify: + return + for waiter in waiters_to_notify: + waiter.release() + try: + all_waiters.remove(waiter) + except ValueError: + pass + + def notify_all(self): + """Wake up all threads waiting on this condition. + + If the calling thread has not acquired the lock when this method + is called, a RuntimeError is raised. + + """ + self.notify(len(self._waiters)) + + notifyAll = notify_all + + +class Semaphore: + """This class implements semaphore objects. + + Semaphores manage a counter representing the number of release() calls minus + the number of acquire() calls, plus an initial value. The acquire() method + blocks if necessary until it can return without making the counter + negative. If not given, value defaults to 1. + + """ + + # After Tim Peters' semaphore class, but not quite the same (no maximum) + + def __init__(self, value=1): + if value < 0: + raise ValueError("semaphore initial value must be >= 0") + self._cond = Condition(Lock()) + self._value = value + + def acquire(self, blocking=True, timeout=None): + """Acquire a semaphore, decrementing the internal counter by one. + + When invoked without arguments: if the internal counter is larger than + zero on entry, decrement it by one and return immediately. If it is zero + on entry, block, waiting until some other thread has called release() to + make it larger than zero. This is done with proper interlocking so that + if multiple acquire() calls are blocked, release() will wake exactly one + of them up. The implementation may pick one at random, so the order in + which blocked threads are awakened should not be relied on. There is no + return value in this case. + + When invoked with blocking set to true, do the same thing as when called + without arguments, and return true. + + When invoked with blocking set to false, do not block. If a call without + an argument would block, return false immediately; otherwise, do the + same thing as when called without arguments, and return true. + + When invoked with a timeout other than None, it will block for at + most timeout seconds. If acquire does not complete successfully in + that interval, return false. Return true otherwise. + + """ + if not blocking and timeout is not None: + raise ValueError("can't specify timeout for non-blocking acquire") + rc = False + endtime = None + with self._cond: + while self._value == 0: + if not blocking: + break + if timeout is not None: + if endtime is None: + endtime = _time() + timeout + else: + timeout = endtime - _time() + if timeout <= 0: + break + self._cond.wait(timeout) + else: + self._value -= 1 + rc = True + return rc + + __enter__ = acquire + + def release(self): + """Release a semaphore, incrementing the internal counter by one. + + When the counter is zero on entry and another thread is waiting for it + to become larger than zero again, wake up that thread. + + """ + with self._cond: + self._value += 1 + self._cond.notify() + + def __exit__(self, t, v, tb): + self.release() + + +class BoundedSemaphore(Semaphore): + """Implements a bounded semaphore. + + A bounded semaphore checks to make sure its current value doesn't exceed its + initial value. If it does, ValueError is raised. In most situations + semaphores are used to guard resources with limited capacity. + + If the semaphore is released too many times it's a sign of a bug. If not + given, value defaults to 1. + + Like regular semaphores, bounded semaphores manage a counter representing + the number of release() calls minus the number of acquire() calls, plus an + initial value. The acquire() method blocks if necessary until it can return + without making the counter negative. If not given, value defaults to 1. + + """ + + def __init__(self, value=1): + Semaphore.__init__(self, value) + self._initial_value = value + + def release(self): + """Release a semaphore, incrementing the internal counter by one. + + When the counter is zero on entry and another thread is waiting for it + to become larger than zero again, wake up that thread. + + If the number of releases exceeds the number of acquires, + raise a ValueError. + + """ + with self._cond: + if self._value >= self._initial_value: + raise ValueError("Semaphore released too many times") + self._value += 1 + self._cond.notify() + + +class Event: + """Class implementing event objects. + + Events manage a flag that can be set to true with the set() method and reset + to false with the clear() method. The wait() method blocks until the flag is + true. The flag is initially false. + + """ + + # After Tim Peters' event class (without is_posted()) + + def __init__(self): + self._cond = Condition(Lock()) + self._flag = False + + def _reset_internal_locks(self): + # private! called by Thread._reset_internal_locks by _after_fork() + self._cond.__init__(Lock()) + + def is_set(self): + """Return true if and only if the internal flag is true.""" + return self._flag + + isSet = is_set + + def set(self): + """Set the internal flag to true. + + All threads waiting for it to become true are awakened. Threads + that call wait() once the flag is true will not block at all. + + """ + with self._cond: + self._flag = True + self._cond.notify_all() + + def clear(self): + """Reset the internal flag to false. + + Subsequently, threads calling wait() will block until set() is called to + set the internal flag to true again. + + """ + with self._cond: + self._flag = False + + def wait(self, timeout=None): + """Block until the internal flag is true. + + If the internal flag is true on entry, return immediately. Otherwise, + block until another thread calls set() to set the flag to true, or until + the optional timeout occurs. + + When the timeout argument is present and not None, it should be a + floating point number specifying a timeout for the operation in seconds + (or fractions thereof). + + This method returns the internal flag on exit, so it will always return + True except if a timeout is given and the operation times out. + + """ + with self._cond: + signaled = self._flag + if not signaled: + signaled = self._cond.wait(timeout) + return signaled + + +# A barrier class. Inspired in part by the pthread_barrier_* api and +# the CyclicBarrier class from Java. See +# http://sourceware.org/pthreads-win32/manual/pthread_barrier_init.html and +# http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/ +# CyclicBarrier.html +# for information. +# We maintain two main states, 'filling' and 'draining' enabling the barrier +# to be cyclic. Threads are not allowed into it until it has fully drained +# since the previous cycle. In addition, a 'resetting' state exists which is +# similar to 'draining' except that threads leave with a BrokenBarrierError, +# and a 'broken' state in which all threads get the exception. +class Barrier: + """Implements a Barrier. + + Useful for synchronizing a fixed number of threads at known synchronization + points. Threads block on 'wait()' and are simultaneously awoken once they + have all made that call. + + """ + + def __init__(self, parties, action=None, timeout=None): + """Create a barrier, initialised to 'parties' threads. + + 'action' is a callable which, when supplied, will be called by one of + the threads after they have all entered the barrier and just prior to + releasing them all. If a 'timeout' is provided, it is used as the + default for all subsequent 'wait()' calls. + + """ + self._cond = Condition(Lock()) + self._action = action + self._timeout = timeout + self._parties = parties + self._state = 0 #0 filling, 1, draining, -1 resetting, -2 broken + self._count = 0 + + def wait(self, timeout=None): + """Wait for the barrier. + + When the specified number of threads have started waiting, they are all + simultaneously awoken. If an 'action' was provided for the barrier, one + of the threads will have executed that callback prior to returning. + Returns an individual index number from 0 to 'parties-1'. + + """ + if timeout is None: + timeout = self._timeout + with self._cond: + self._enter() # Block while the barrier drains. + index = self._count + self._count += 1 + try: + if index + 1 == self._parties: + # We release the barrier + self._release() + else: + # We wait until someone releases us + self._wait(timeout) + return index + finally: + self._count -= 1 + # Wake up any threads waiting for barrier to drain. + self._exit() + + # Block until the barrier is ready for us, or raise an exception + # if it is broken. + def _enter(self): + while self._state in (-1, 1): + # It is draining or resetting, wait until done + self._cond.wait() + #see if the barrier is in a broken state + if self._state < 0: + raise BrokenBarrierError + assert self._state == 0 + + # Optionally run the 'action' and release the threads waiting + # in the barrier. + def _release(self): + try: + if self._action: + self._action() + # enter draining state + self._state = 1 + self._cond.notify_all() + except: + #an exception during the _action handler. Break and reraise + self._break() + raise + + # Wait in the barrier until we are released. Raise an exception + # if the barrier is reset or broken. + def _wait(self, timeout): + if not self._cond.wait_for(lambda : self._state != 0, timeout): + #timed out. Break the barrier + self._break() + raise BrokenBarrierError + if self._state < 0: + raise BrokenBarrierError + assert self._state == 1 + + # If we are the last thread to exit the barrier, signal any threads + # waiting for the barrier to drain. + def _exit(self): + if self._count == 0: + if self._state in (-1, 1): + #resetting or draining + self._state = 0 + self._cond.notify_all() + + def reset(self): + """Reset the barrier to the initial state. + + Any threads currently waiting will get the BrokenBarrier exception + raised. + + """ + with self._cond: + if self._count > 0: + if self._state == 0: + #reset the barrier, waking up threads + self._state = -1 + elif self._state == -2: + #was broken, set it to reset state + #which clears when the last thread exits + self._state = -1 + else: + self._state = 0 + self._cond.notify_all() + + def abort(self): + """Place the barrier into a 'broken' state. + + Useful in case of error. Any currently waiting threads and threads + attempting to 'wait()' will have BrokenBarrierError raised. + + """ + with self._cond: + self._break() + + def _break(self): + # An internal error was detected. The barrier is set to + # a broken state all parties awakened. + self._state = -2 + self._cond.notify_all() + + @property + def parties(self): + """Return the number of threads required to trip the barrier.""" + return self._parties + + @property + def n_waiting(self): + """Return the number of threads currently waiting at the barrier.""" + # We don't need synchronization here since this is an ephemeral result + # anyway. It returns the correct value in the steady state. + if self._state == 0: + return self._count + return 0 + + @property + def broken(self): + """Return True if the barrier is in a broken state.""" + return self._state == -2 + +# exception raised by the Barrier class +class BrokenBarrierError(RuntimeError): + pass + + +# Helper to generate new thread names +_counter = _count().__next__ +_counter() # Consume 0 so first non-main thread has id 1. +def _newname(template="Thread-%d"): + return template % _counter() + +# Active thread administration +_active_limbo_lock = _allocate_lock() +_active = {} # maps thread id to Thread object +_limbo = {} +_dangling = WeakSet() +# Set of Thread._tstate_lock locks of non-daemon threads used by _shutdown() +# to wait until all Python thread states get deleted: +# see Thread._set_tstate_lock(). +_shutdown_locks_lock = _allocate_lock() +_shutdown_locks = set() + +# Main class for threads + +class Thread: + """A class that represents a thread of control. + + This class can be safely subclassed in a limited fashion. There are two ways + to specify the activity: by passing a callable object to the constructor, or + by overriding the run() method in a subclass. + + """ + + _initialized = False + + def __init__(self, group=None, target=None, name=None, + args=(), kwargs=None, *, daemon=None): + """This constructor should always be called with keyword arguments. Arguments are: + + *group* should be None; reserved for future extension when a ThreadGroup + class is implemented. + + *target* is the callable object to be invoked by the run() + method. Defaults to None, meaning nothing is called. + + *name* is the thread name. By default, a unique name is constructed of + the form "Thread-N" where N is a small decimal number. + + *args* is the argument tuple for the target invocation. Defaults to (). + + *kwargs* is a dictionary of keyword arguments for the target + invocation. Defaults to {}. + + If a subclass overrides the constructor, it must make sure to invoke + the base class constructor (Thread.__init__()) before doing anything + else to the thread. + + """ + assert group is None, "group argument must be None for now" + if kwargs is None: + kwargs = {} + self._target = target + self._name = str(name or _newname()) + self._args = args + self._kwargs = kwargs + if daemon is not None: + self._daemonic = daemon + else: + self._daemonic = current_thread().daemon + self._ident = None + if _HAVE_THREAD_NATIVE_ID: + self._native_id = None + self._tstate_lock = None + self._started = Event() + self._is_stopped = False + self._initialized = True + # Copy of sys.stderr used by self._invoke_excepthook() + self._stderr = _sys.stderr + self._invoke_excepthook = _make_invoke_excepthook() + # For debugging and _after_fork() + _dangling.add(self) + + def _reset_internal_locks(self, is_alive): + # private! Called by _after_fork() to reset our internal locks as + # they may be in an invalid state leading to a deadlock or crash. + self._started._reset_internal_locks() + if is_alive: + self._set_tstate_lock() + else: + # The thread isn't alive after fork: it doesn't have a tstate + # anymore. + self._is_stopped = True + self._tstate_lock = None + + def __repr__(self): + assert self._initialized, "Thread.__init__() was not called" + status = "initial" + if self._started.is_set(): + status = "started" + self.is_alive() # easy way to get ._is_stopped set when appropriate + if self._is_stopped: + status = "stopped" + if self._daemonic: + status += " daemon" + if self._ident is not None: + status += " %s" % self._ident + return "<%s(%s, %s)>" % (self.__class__.__name__, self._name, status) + + def start(self): + """Start the thread's activity. + + It must be called at most once per thread object. It arranges for the + object's run() method to be invoked in a separate thread of control. + + This method will raise a RuntimeError if called more than once on the + same thread object. + + """ + if not self._initialized: + raise RuntimeError("thread.__init__() not called") + + if self._started.is_set(): + raise RuntimeError("threads can only be started once") + with _active_limbo_lock: + _limbo[self] = self + try: + _start_new_thread(self._bootstrap, ()) + except Exception: + with _active_limbo_lock: + del _limbo[self] + raise + self._started.wait() + + def run(self): + """Method representing the thread's activity. + + You may override this method in a subclass. The standard run() method + invokes the callable object passed to the object's constructor as the + target argument, if any, with sequential and keyword arguments taken + from the args and kwargs arguments, respectively. + + """ + try: + if self._target: + self._target(*self._args, **self._kwargs) + finally: + # Avoid a refcycle if the thread is running a function with + # an argument that has a member that points to the thread. + del self._target, self._args, self._kwargs + + def _bootstrap(self): + # Wrapper around the real bootstrap code that ignores + # exceptions during interpreter cleanup. Those typically + # happen when a daemon thread wakes up at an unfortunate + # moment, finds the world around it destroyed, and raises some + # random exception *** while trying to report the exception in + # _bootstrap_inner() below ***. Those random exceptions + # don't help anybody, and they confuse users, so we suppress + # them. We suppress them only when it appears that the world + # indeed has already been destroyed, so that exceptions in + # _bootstrap_inner() during normal business hours are properly + # reported. Also, we only suppress them for daemonic threads; + # if a non-daemonic encounters this, something else is wrong. + try: + self._bootstrap_inner() + except: + if self._daemonic and _sys is None: + return + raise + + def _set_ident(self): + self._ident = get_ident() + + if _HAVE_THREAD_NATIVE_ID: + def _set_native_id(self): + self._native_id = get_native_id() + + def _set_tstate_lock(self): + """ + Set a lock object which will be released by the interpreter when + the underlying thread state (see pystate.h) gets deleted. + """ + self._tstate_lock = _set_sentinel() + self._tstate_lock.acquire() + + if not self.daemon: + with _shutdown_locks_lock: + _shutdown_locks.add(self._tstate_lock) + + def _bootstrap_inner(self): + try: + self._set_ident() + self._set_tstate_lock() + if _HAVE_THREAD_NATIVE_ID: + self._set_native_id() + self._started.set() + with _active_limbo_lock: + _active[self._ident] = self + del _limbo[self] + + if _trace_hook: + _sys.settrace(_trace_hook) + if _profile_hook: + _sys.setprofile(_profile_hook) + + try: + self.run() + except: + self._invoke_excepthook(self) + finally: + with _active_limbo_lock: + try: + # We don't call self._delete() because it also + # grabs _active_limbo_lock. + del _active[get_ident()] + except: + pass + + def _stop(self): + # After calling ._stop(), .is_alive() returns False and .join() returns + # immediately. ._tstate_lock must be released before calling ._stop(). + # + # Normal case: C code at the end of the thread's life + # (release_sentinel in _threadmodule.c) releases ._tstate_lock, and + # that's detected by our ._wait_for_tstate_lock(), called by .join() + # and .is_alive(). Any number of threads _may_ call ._stop() + # simultaneously (for example, if multiple threads are blocked in + # .join() calls), and they're not serialized. That's harmless - + # they'll just make redundant rebindings of ._is_stopped and + # ._tstate_lock. Obscure: we rebind ._tstate_lock last so that the + # "assert self._is_stopped" in ._wait_for_tstate_lock() always works + # (the assert is executed only if ._tstate_lock is None). + # + # Special case: _main_thread releases ._tstate_lock via this + # module's _shutdown() function. + lock = self._tstate_lock + if lock is not None: + assert not lock.locked() + self._is_stopped = True + self._tstate_lock = None + if not self.daemon: + with _shutdown_locks_lock: + _shutdown_locks.discard(lock) + + def _delete(self): + "Remove current thread from the dict of currently running threads." + with _active_limbo_lock: + del _active[get_ident()] + # There must not be any python code between the previous line + # and after the lock is released. Otherwise a tracing function + # could try to acquire the lock again in the same thread, (in + # current_thread()), and would block. + + def join(self, timeout=None): + """Wait until the thread terminates. + + This blocks the calling thread until the thread whose join() method is + called terminates -- either normally or through an unhandled exception + or until the optional timeout occurs. + + When the timeout argument is present and not None, it should be a + floating point number specifying a timeout for the operation in seconds + (or fractions thereof). As join() always returns None, you must call + is_alive() after join() to decide whether a timeout happened -- if the + thread is still alive, the join() call timed out. + + When the timeout argument is not present or None, the operation will + block until the thread terminates. + + A thread can be join()ed many times. + + join() raises a RuntimeError if an attempt is made to join the current + thread as that would cause a deadlock. It is also an error to join() a + thread before it has been started and attempts to do so raises the same + exception. + + """ + if not self._initialized: + raise RuntimeError("Thread.__init__() not called") + if not self._started.is_set(): + raise RuntimeError("cannot join thread before it is started") + if self is current_thread(): + raise RuntimeError("cannot join current thread") + + if timeout is None: + self._wait_for_tstate_lock() + else: + # the behavior of a negative timeout isn't documented, but + # historically .join(timeout=x) for x<0 has acted as if timeout=0 + self._wait_for_tstate_lock(timeout=max(timeout, 0)) + + def _wait_for_tstate_lock(self, block=True, timeout=-1): + # Issue #18808: wait for the thread state to be gone. + # At the end of the thread's life, after all knowledge of the thread + # is removed from C data structures, C code releases our _tstate_lock. + # This method passes its arguments to _tstate_lock.acquire(). + # If the lock is acquired, the C code is done, and self._stop() is + # called. That sets ._is_stopped to True, and ._tstate_lock to None. + lock = self._tstate_lock + if lock is None: # already determined that the C code is done + assert self._is_stopped + elif lock.acquire(block, timeout): + lock.release() + self._stop() + + @property + def name(self): + """A string used for identification purposes only. + + It has no semantics. Multiple threads may be given the same name. The + initial name is set by the constructor. + + """ + assert self._initialized, "Thread.__init__() not called" + return self._name + + @name.setter + def name(self, name): + assert self._initialized, "Thread.__init__() not called" + self._name = str(name) + + @property + def ident(self): + """Thread identifier of this thread or None if it has not been started. + + This is a nonzero integer. See the get_ident() function. Thread + identifiers may be recycled when a thread exits and another thread is + created. The identifier is available even after the thread has exited. + + """ + assert self._initialized, "Thread.__init__() not called" + return self._ident + + if _HAVE_THREAD_NATIVE_ID: + @property + def native_id(self): + """Native integral thread ID of this thread, or None if it has not been started. + + This is a non-negative integer. See the get_native_id() function. + This represents the Thread ID as reported by the kernel. + + """ + assert self._initialized, "Thread.__init__() not called" + return self._native_id + + def is_alive(self): + """Return whether the thread is alive. + + This method returns True just before the run() method starts until just + after the run() method terminates. The module function enumerate() + returns a list of all alive threads. + + """ + assert self._initialized, "Thread.__init__() not called" + if self._is_stopped or not self._started.is_set(): + return False + self._wait_for_tstate_lock(False) + return not self._is_stopped + + def isAlive(self): + """Return whether the thread is alive. + + This method is deprecated, use is_alive() instead. + """ + import warnings + warnings.warn('isAlive() is deprecated, use is_alive() instead', + DeprecationWarning, stacklevel=2) + return self.is_alive() + + @property + def daemon(self): + """A boolean value indicating whether this thread is a daemon thread. + + This must be set before start() is called, otherwise RuntimeError is + raised. Its initial value is inherited from the creating thread; the + main thread is not a daemon thread and therefore all threads created in + the main thread default to daemon = False. + + The entire Python program exits when only daemon threads are left. + + """ + assert self._initialized, "Thread.__init__() not called" + return self._daemonic + + @daemon.setter + def daemon(self, daemonic): + if not self._initialized: + raise RuntimeError("Thread.__init__() not called") + if self._started.is_set(): + raise RuntimeError("cannot set daemon status of active thread") + self._daemonic = daemonic + + def isDaemon(self): + return self.daemon + + def setDaemon(self, daemonic): + self.daemon = daemonic + + def getName(self): + return self.name + + def setName(self, name): + self.name = name + + +try: + from _thread import (_excepthook as excepthook, + _ExceptHookArgs as ExceptHookArgs) +except ImportError: + # Simple Python implementation if _thread._excepthook() is not available + from traceback import print_exception as _print_exception + from collections import namedtuple + + _ExceptHookArgs = namedtuple( + 'ExceptHookArgs', + 'exc_type exc_value exc_traceback thread') + + def ExceptHookArgs(args): + return _ExceptHookArgs(*args) + + def excepthook(args, /): + """ + Handle uncaught Thread.run() exception. + """ + if args.exc_type == SystemExit: + # silently ignore SystemExit + return + + if _sys is not None and _sys.stderr is not None: + stderr = _sys.stderr + elif args.thread is not None: + stderr = args.thread._stderr + if stderr is None: + # do nothing if sys.stderr is None and sys.stderr was None + # when the thread was created + return + else: + # do nothing if sys.stderr is None and args.thread is None + return + + if args.thread is not None: + name = args.thread.name + else: + name = get_ident() + print(f"Exception in thread {name}:", + file=stderr, flush=True) + _print_exception(args.exc_type, args.exc_value, args.exc_traceback, + file=stderr) + stderr.flush() + + +def _make_invoke_excepthook(): + # Create a local namespace to ensure that variables remain alive + # when _invoke_excepthook() is called, even if it is called late during + # Python shutdown. It is mostly needed for daemon threads. + + old_excepthook = excepthook + old_sys_excepthook = _sys.excepthook + if old_excepthook is None: + raise RuntimeError("threading.excepthook is None") + if old_sys_excepthook is None: + raise RuntimeError("sys.excepthook is None") + + sys_exc_info = _sys.exc_info + local_print = print + local_sys = _sys + + def invoke_excepthook(thread): + global excepthook + try: + hook = excepthook + if hook is None: + hook = old_excepthook + + args = ExceptHookArgs([*sys_exc_info(), thread]) + + hook(args) + except Exception as exc: + exc.__suppress_context__ = True + del exc + + if local_sys is not None and local_sys.stderr is not None: + stderr = local_sys.stderr + else: + stderr = thread._stderr + + local_print("Exception in threading.excepthook:", + file=stderr, flush=True) + + if local_sys is not None and local_sys.excepthook is not None: + sys_excepthook = local_sys.excepthook + else: + sys_excepthook = old_sys_excepthook + + sys_excepthook(*sys_exc_info()) + finally: + # Break reference cycle (exception stored in a variable) + args = None + + return invoke_excepthook + + +# The timer class was contributed by Itamar Shtull-Trauring + +class Timer(Thread): + """Call a function after a specified number of seconds: + + t = Timer(30.0, f, args=None, kwargs=None) + t.start() + t.cancel() # stop the timer's action if it's still waiting + + """ + + def __init__(self, interval, function, args=None, kwargs=None): + Thread.__init__(self) + self.interval = interval + self.function = function + self.args = args if args is not None else [] + self.kwargs = kwargs if kwargs is not None else {} + self.finished = Event() + + def cancel(self): + """Stop the timer if it hasn't finished yet.""" + self.finished.set() + + def run(self): + self.finished.wait(self.interval) + if not self.finished.is_set(): + self.function(*self.args, **self.kwargs) + self.finished.set() + + +# Special thread class to represent the main thread + +class _MainThread(Thread): + + def __init__(self): + Thread.__init__(self, name="MainThread", daemon=False) + self._set_tstate_lock() + self._started.set() + self._set_ident() + if _HAVE_THREAD_NATIVE_ID: + self._set_native_id() + with _active_limbo_lock: + _active[self._ident] = self + + +# Dummy thread class to represent threads not started here. +# These aren't garbage collected when they die, nor can they be waited for. +# If they invoke anything in threading.py that calls current_thread(), they +# leave an entry in the _active dict forever after. +# Their purpose is to return *something* from current_thread(). +# They are marked as daemon threads so we won't wait for them +# when we exit (conform previous semantics). + +class _DummyThread(Thread): + + def __init__(self): + Thread.__init__(self, name=_newname("Dummy-%d"), daemon=True) + + self._started.set() + self._set_ident() + if _HAVE_THREAD_NATIVE_ID: + self._set_native_id() + with _active_limbo_lock: + _active[self._ident] = self + + def _stop(self): + pass + + def is_alive(self): + assert not self._is_stopped and self._started.is_set() + return True + + def join(self, timeout=None): + assert False, "cannot join a dummy thread" + + +# Global API functions + +def current_thread(): + """Return the current Thread object, corresponding to the caller's thread of control. + + If the caller's thread of control was not created through the threading + module, a dummy thread object with limited functionality is returned. + + """ + try: + return _active[get_ident()] + except KeyError: + return _DummyThread() + +currentThread = current_thread + +def active_count(): + """Return the number of Thread objects currently alive. + + The returned count is equal to the length of the list returned by + enumerate(). + + """ + with _active_limbo_lock: + return len(_active) + len(_limbo) + +activeCount = active_count + +def _enumerate(): + # Same as enumerate(), but without the lock. Internal use only. + return list(_active.values()) + list(_limbo.values()) + +def enumerate(): + """Return a list of all Thread objects currently alive. + + The list includes daemonic threads, dummy thread objects created by + current_thread(), and the main thread. It excludes terminated threads and + threads that have not yet been started. + + """ + with _active_limbo_lock: + return list(_active.values()) + list(_limbo.values()) + +from _thread import stack_size + +# Create the main thread object, +# and make it available for the interpreter +# (Py_Main) as threading._shutdown. + +_main_thread = _MainThread() + +def _shutdown(): + """ + Wait until the Python thread state of all non-daemon threads get deleted. + """ + # Obscure: other threads may be waiting to join _main_thread. That's + # dubious, but some code does it. We can't wait for C code to release + # the main thread's tstate_lock - that won't happen until the interpreter + # is nearly dead. So we release it here. Note that just calling _stop() + # isn't enough: other threads may already be waiting on _tstate_lock. + if _main_thread._is_stopped: + # _shutdown() was already called + return + + # Main thread + tlock = _main_thread._tstate_lock + # The main thread isn't finished yet, so its thread state lock can't have + # been released. + assert tlock is not None + assert tlock.locked() + tlock.release() + _main_thread._stop() + + # Join all non-deamon threads + while True: + with _shutdown_locks_lock: + locks = list(_shutdown_locks) + _shutdown_locks.clear() + + if not locks: + break + + for lock in locks: + # mimick Thread.join() + lock.acquire() + lock.release() + + # new threads can be spawned while we were waiting for the other + # threads to complete + + +def main_thread(): + """Return the main thread object. + + In normal conditions, the main thread is the thread from which the + Python interpreter was started. + """ + return _main_thread + +# get thread-local implementation, either from the thread +# module, or from the python fallback + +try: + from _thread import _local as local +except ImportError: + from _threading_local import local + + +def _after_fork(): + """ + Cleanup threading module state that should not exist after a fork. + """ + # Reset _active_limbo_lock, in case we forked while the lock was held + # by another (non-forked) thread. http://bugs.python.org/issue874900 + global _active_limbo_lock, _main_thread + global _shutdown_locks_lock, _shutdown_locks + _active_limbo_lock = _allocate_lock() + + # fork() only copied the current thread; clear references to others. + new_active = {} + + try: + current = _active[get_ident()] + except KeyError: + # fork() was called in a thread which was not spawned + # by threading.Thread. For example, a thread spawned + # by thread.start_new_thread(). + current = _MainThread() + + _main_thread = current + + # reset _shutdown() locks: threads re-register their _tstate_lock below + _shutdown_locks_lock = _allocate_lock() + _shutdown_locks = set() + + with _active_limbo_lock: + # Dangling thread instances must still have their locks reset, + # because someone may join() them. + threads = set(_enumerate()) + threads.update(_dangling) + for thread in threads: + # Any lock/condition variable may be currently locked or in an + # invalid state, so we reinitialize them. + if thread is current: + # There is only one active thread. We reset the ident to + # its new value since it can have changed. + thread._reset_internal_locks(True) + ident = get_ident() + thread._ident = ident + new_active[ident] = thread + else: + # All the others are already stopped. + thread._reset_internal_locks(False) + thread._stop() + + _limbo.clear() + _active.clear() + _active.update(new_active) + assert len(_active) == 1 + + +if hasattr(_os, "register_at_fork"): + _os.register_at_fork(after_in_child=_after_fork) diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/tokenize.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/tokenize.py new file mode 100644 index 0000000000000000000000000000000000000000..1aee21b5e18fa716dfaa5306fc6aa8a96d253641 --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/tokenize.py @@ -0,0 +1,682 @@ +"""Tokenization help for Python programs. + +tokenize(readline) is a generator that breaks a stream of bytes into +Python tokens. It decodes the bytes according to PEP-0263 for +determining source file encoding. + +It accepts a readline-like method which is called repeatedly to get the +next line of input (or b"" for EOF). It generates 5-tuples with these +members: + + the token type (see token.py) + the token (a string) + the starting (row, column) indices of the token (a 2-tuple of ints) + the ending (row, column) indices of the token (a 2-tuple of ints) + the original line (string) + +It is designed to match the working of the Python tokenizer exactly, except +that it produces COMMENT tokens for comments and gives type OP for all +operators. Additionally, all token lists start with an ENCODING token +which tells you which encoding was used to decode the bytes stream. +""" + +__author__ = 'Ka-Ping Yee ' +__credits__ = ('GvR, ESR, Tim Peters, Thomas Wouters, Fred Drake, ' + 'Skip Montanaro, Raymond Hettinger, Trent Nelson, ' + 'Michael Foord') +from builtins import open as _builtin_open +from codecs import lookup, BOM_UTF8 +import collections +from io import TextIOWrapper +import itertools as _itertools +import re +import sys +from token import * +from token import EXACT_TOKEN_TYPES + +cookie_re = re.compile(r'^[ \t\f]*#.*?coding[:=][ \t]*([-\w.]+)', re.ASCII) +blank_re = re.compile(br'^[ \t\f]*(?:[#\r\n]|$)', re.ASCII) + +import token +__all__ = token.__all__ + ["tokenize", "generate_tokens", "detect_encoding", + "untokenize", "TokenInfo"] +del token + +class TokenInfo(collections.namedtuple('TokenInfo', 'type string start end line')): + def __repr__(self): + annotated_type = '%d (%s)' % (self.type, tok_name[self.type]) + return ('TokenInfo(type=%s, string=%r, start=%r, end=%r, line=%r)' % + self._replace(type=annotated_type)) + + @property + def exact_type(self): + if self.type == OP and self.string in EXACT_TOKEN_TYPES: + return EXACT_TOKEN_TYPES[self.string] + else: + return self.type + +def group(*choices): return '(' + '|'.join(choices) + ')' +def any(*choices): return group(*choices) + '*' +def maybe(*choices): return group(*choices) + '?' + +# Note: we use unicode matching for names ("\w") but ascii matching for +# number literals. +Whitespace = r'[ \f\t]*' +Comment = r'#[^\r\n]*' +Ignore = Whitespace + any(r'\\\r?\n' + Whitespace) + maybe(Comment) +Name = r'\w+' + +Hexnumber = r'0[xX](?:_?[0-9a-fA-F])+' +Binnumber = r'0[bB](?:_?[01])+' +Octnumber = r'0[oO](?:_?[0-7])+' +Decnumber = r'(?:0(?:_?0)*|[1-9](?:_?[0-9])*)' +Intnumber = group(Hexnumber, Binnumber, Octnumber, Decnumber) +Exponent = r'[eE][-+]?[0-9](?:_?[0-9])*' +Pointfloat = group(r'[0-9](?:_?[0-9])*\.(?:[0-9](?:_?[0-9])*)?', + r'\.[0-9](?:_?[0-9])*') + maybe(Exponent) +Expfloat = r'[0-9](?:_?[0-9])*' + Exponent +Floatnumber = group(Pointfloat, Expfloat) +Imagnumber = group(r'[0-9](?:_?[0-9])*[jJ]', Floatnumber + r'[jJ]') +Number = group(Imagnumber, Floatnumber, Intnumber) + +# Return the empty string, plus all of the valid string prefixes. +def _all_string_prefixes(): + # The valid string prefixes. Only contain the lower case versions, + # and don't contain any permutations (include 'fr', but not + # 'rf'). The various permutations will be generated. + _valid_string_prefixes = ['b', 'r', 'u', 'f', 'br', 'fr'] + # if we add binary f-strings, add: ['fb', 'fbr'] + result = {''} + for prefix in _valid_string_prefixes: + for t in _itertools.permutations(prefix): + # create a list with upper and lower versions of each + # character + for u in _itertools.product(*[(c, c.upper()) for c in t]): + result.add(''.join(u)) + return result + +def _compile(expr): + return re.compile(expr, re.UNICODE) + +# Note that since _all_string_prefixes includes the empty string, +# StringPrefix can be the empty string (making it optional). +StringPrefix = group(*_all_string_prefixes()) + +# Tail end of ' string. +Single = r"[^'\\]*(?:\\.[^'\\]*)*'" +# Tail end of " string. +Double = r'[^"\\]*(?:\\.[^"\\]*)*"' +# Tail end of ''' string. +Single3 = r"[^'\\]*(?:(?:\\.|'(?!''))[^'\\]*)*'''" +# Tail end of """ string. +Double3 = r'[^"\\]*(?:(?:\\.|"(?!""))[^"\\]*)*"""' +Triple = group(StringPrefix + "'''", StringPrefix + '"""') +# Single-line ' or " string. +String = group(StringPrefix + r"'[^\n'\\]*(?:\\.[^\n'\\]*)*'", + StringPrefix + r'"[^\n"\\]*(?:\\.[^\n"\\]*)*"') + +# Sorting in reverse order puts the long operators before their prefixes. +# Otherwise if = came before ==, == would get recognized as two instances +# of =. +Special = group(*map(re.escape, sorted(EXACT_TOKEN_TYPES, reverse=True))) +Funny = group(r'\r?\n', Special) + +PlainToken = group(Number, Funny, String, Name) +Token = Ignore + PlainToken + +# First (or only) line of ' or " string. +ContStr = group(StringPrefix + r"'[^\n'\\]*(?:\\.[^\n'\\]*)*" + + group("'", r'\\\r?\n'), + StringPrefix + r'"[^\n"\\]*(?:\\.[^\n"\\]*)*' + + group('"', r'\\\r?\n')) +PseudoExtras = group(r'\\\r?\n|\Z', Comment, Triple) +PseudoToken = Whitespace + group(PseudoExtras, Number, Funny, ContStr, Name) + +# For a given string prefix plus quotes, endpats maps it to a regex +# to match the remainder of that string. _prefix can be empty, for +# a normal single or triple quoted string (with no prefix). +endpats = {} +for _prefix in _all_string_prefixes(): + endpats[_prefix + "'"] = Single + endpats[_prefix + '"'] = Double + endpats[_prefix + "'''"] = Single3 + endpats[_prefix + '"""'] = Double3 + +# A set of all of the single and triple quoted string prefixes, +# including the opening quotes. +single_quoted = set() +triple_quoted = set() +for t in _all_string_prefixes(): + for u in (t + '"', t + "'"): + single_quoted.add(u) + for u in (t + '"""', t + "'''"): + triple_quoted.add(u) + +tabsize = 8 + +class TokenError(Exception): pass + +class StopTokenizing(Exception): pass + + +class Untokenizer: + + def __init__(self): + self.tokens = [] + self.prev_row = 1 + self.prev_col = 0 + self.encoding = None + + def add_whitespace(self, start): + row, col = start + if row < self.prev_row or row == self.prev_row and col < self.prev_col: + raise ValueError("start ({},{}) precedes previous end ({},{})" + .format(row, col, self.prev_row, self.prev_col)) + row_offset = row - self.prev_row + if row_offset: + self.tokens.append("\\\n" * row_offset) + self.prev_col = 0 + col_offset = col - self.prev_col + if col_offset: + self.tokens.append(" " * col_offset) + + def untokenize(self, iterable): + it = iter(iterable) + indents = [] + startline = False + for t in it: + if len(t) == 2: + self.compat(t, it) + break + tok_type, token, start, end, line = t + if tok_type == ENCODING: + self.encoding = token + continue + if tok_type == ENDMARKER: + break + if tok_type == INDENT: + indents.append(token) + continue + elif tok_type == DEDENT: + indents.pop() + self.prev_row, self.prev_col = end + continue + elif tok_type in (NEWLINE, NL): + startline = True + elif startline and indents: + indent = indents[-1] + if start[1] >= len(indent): + self.tokens.append(indent) + self.prev_col = len(indent) + startline = False + self.add_whitespace(start) + self.tokens.append(token) + self.prev_row, self.prev_col = end + if tok_type in (NEWLINE, NL): + self.prev_row += 1 + self.prev_col = 0 + return "".join(self.tokens) + + def compat(self, token, iterable): + indents = [] + toks_append = self.tokens.append + startline = token[0] in (NEWLINE, NL) + prevstring = False + + for tok in _itertools.chain([token], iterable): + toknum, tokval = tok[:2] + if toknum == ENCODING: + self.encoding = tokval + continue + + if toknum in (NAME, NUMBER): + tokval += ' ' + + # Insert a space between two consecutive strings + if toknum == STRING: + if prevstring: + tokval = ' ' + tokval + prevstring = True + else: + prevstring = False + + if toknum == INDENT: + indents.append(tokval) + continue + elif toknum == DEDENT: + indents.pop() + continue + elif toknum in (NEWLINE, NL): + startline = True + elif startline and indents: + toks_append(indents[-1]) + startline = False + toks_append(tokval) + + +def untokenize(iterable): + """Transform tokens back into Python source code. + It returns a bytes object, encoded using the ENCODING + token, which is the first token sequence output by tokenize. + + Each element returned by the iterable must be a token sequence + with at least two elements, a token number and token value. If + only two tokens are passed, the resulting output is poor. + + Round-trip invariant for full input: + Untokenized source will match input source exactly + + Round-trip invariant for limited input: + # Output bytes will tokenize back to the input + t1 = [tok[:2] for tok in tokenize(f.readline)] + newcode = untokenize(t1) + readline = BytesIO(newcode).readline + t2 = [tok[:2] for tok in tokenize(readline)] + assert t1 == t2 + """ + ut = Untokenizer() + out = ut.untokenize(iterable) + if ut.encoding is not None: + out = out.encode(ut.encoding) + return out + + +def _get_normal_name(orig_enc): + """Imitates get_normal_name in tokenizer.c.""" + # Only care about the first 12 characters. + enc = orig_enc[:12].lower().replace("_", "-") + if enc == "utf-8" or enc.startswith("utf-8-"): + return "utf-8" + if enc in ("latin-1", "iso-8859-1", "iso-latin-1") or \ + enc.startswith(("latin-1-", "iso-8859-1-", "iso-latin-1-")): + return "iso-8859-1" + return orig_enc + +def detect_encoding(readline): + """ + The detect_encoding() function is used to detect the encoding that should + be used to decode a Python source file. It requires one argument, readline, + in the same way as the tokenize() generator. + + It will call readline a maximum of twice, and return the encoding used + (as a string) and a list of any lines (left as bytes) it has read in. + + It detects the encoding from the presence of a utf-8 bom or an encoding + cookie as specified in pep-0263. If both a bom and a cookie are present, + but disagree, a SyntaxError will be raised. If the encoding cookie is an + invalid charset, raise a SyntaxError. Note that if a utf-8 bom is found, + 'utf-8-sig' is returned. + + If no encoding is specified, then the default of 'utf-8' will be returned. + """ + try: + filename = readline.__self__.name + except AttributeError: + filename = None + bom_found = False + encoding = None + default = 'utf-8' + def read_or_stop(): + try: + return readline() + except StopIteration: + return b'' + + def find_cookie(line): + try: + # Decode as UTF-8. Either the line is an encoding declaration, + # in which case it should be pure ASCII, or it must be UTF-8 + # per default encoding. + line_string = line.decode('utf-8') + except UnicodeDecodeError: + msg = "invalid or missing encoding declaration" + if filename is not None: + msg = '{} for {!r}'.format(msg, filename) + raise SyntaxError(msg) + + match = cookie_re.match(line_string) + if not match: + return None + encoding = _get_normal_name(match.group(1)) + try: + codec = lookup(encoding) + except LookupError: + # This behaviour mimics the Python interpreter + if filename is None: + msg = "unknown encoding: " + encoding + else: + msg = "unknown encoding for {!r}: {}".format(filename, + encoding) + raise SyntaxError(msg) + + if bom_found: + if encoding != 'utf-8': + # This behaviour mimics the Python interpreter + if filename is None: + msg = 'encoding problem: utf-8' + else: + msg = 'encoding problem for {!r}: utf-8'.format(filename) + raise SyntaxError(msg) + encoding += '-sig' + return encoding + + first = read_or_stop() + if first.startswith(BOM_UTF8): + bom_found = True + first = first[3:] + default = 'utf-8-sig' + if not first: + return default, [] + + encoding = find_cookie(first) + if encoding: + return encoding, [first] + if not blank_re.match(first): + return default, [first] + + second = read_or_stop() + if not second: + return default, [first] + + encoding = find_cookie(second) + if encoding: + return encoding, [first, second] + + return default, [first, second] + + +def open(filename): + """Open a file in read only mode using the encoding detected by + detect_encoding(). + """ + buffer = _builtin_open(filename, 'rb') + try: + encoding, lines = detect_encoding(buffer.readline) + buffer.seek(0) + text = TextIOWrapper(buffer, encoding, line_buffering=True) + text.mode = 'r' + return text + except: + buffer.close() + raise + + +def tokenize(readline): + """ + The tokenize() generator requires one argument, readline, which + must be a callable object which provides the same interface as the + readline() method of built-in file objects. Each call to the function + should return one line of input as bytes. Alternatively, readline + can be a callable function terminating with StopIteration: + readline = open(myfile, 'rb').__next__ # Example of alternate readline + + The generator produces 5-tuples with these members: the token type; the + token string; a 2-tuple (srow, scol) of ints specifying the row and + column where the token begins in the source; a 2-tuple (erow, ecol) of + ints specifying the row and column where the token ends in the source; + and the line on which the token was found. The line passed is the + physical line. + + The first token sequence will always be an ENCODING token + which tells you which encoding was used to decode the bytes stream. + """ + encoding, consumed = detect_encoding(readline) + empty = _itertools.repeat(b"") + rl_gen = _itertools.chain(consumed, iter(readline, b""), empty) + return _tokenize(rl_gen.__next__, encoding) + + +def _tokenize(readline, encoding): + lnum = parenlev = continued = 0 + numchars = '0123456789' + contstr, needcont = '', 0 + contline = None + indents = [0] + + if encoding is not None: + if encoding == "utf-8-sig": + # BOM will already have been stripped. + encoding = "utf-8" + yield TokenInfo(ENCODING, encoding, (0, 0), (0, 0), '') + last_line = b'' + line = b'' + while True: # loop over lines in stream + try: + # We capture the value of the line variable here because + # readline uses the empty string '' to signal end of input, + # hence `line` itself will always be overwritten at the end + # of this loop. + last_line = line + line = readline() + except StopIteration: + line = b'' + + if encoding is not None: + line = line.decode(encoding) + lnum += 1 + pos, max = 0, len(line) + + if contstr: # continued string + if not line: + raise TokenError("EOF in multi-line string", strstart) + endmatch = endprog.match(line) + if endmatch: + pos = end = endmatch.end(0) + yield TokenInfo(STRING, contstr + line[:end], + strstart, (lnum, end), contline + line) + contstr, needcont = '', 0 + contline = None + elif needcont and line[-2:] != '\\\n' and line[-3:] != '\\\r\n': + yield TokenInfo(ERRORTOKEN, contstr + line, + strstart, (lnum, len(line)), contline) + contstr = '' + contline = None + continue + else: + contstr = contstr + line + contline = contline + line + continue + + elif parenlev == 0 and not continued: # new statement + if not line: break + column = 0 + while pos < max: # measure leading whitespace + if line[pos] == ' ': + column += 1 + elif line[pos] == '\t': + column = (column//tabsize + 1)*tabsize + elif line[pos] == '\f': + column = 0 + else: + break + pos += 1 + if pos == max: + break + + if line[pos] in '#\r\n': # skip comments or blank lines + if line[pos] == '#': + comment_token = line[pos:].rstrip('\r\n') + yield TokenInfo(COMMENT, comment_token, + (lnum, pos), (lnum, pos + len(comment_token)), line) + pos += len(comment_token) + + yield TokenInfo(NL, line[pos:], + (lnum, pos), (lnum, len(line)), line) + continue + + if column > indents[-1]: # count indents or dedents + indents.append(column) + yield TokenInfo(INDENT, line[:pos], (lnum, 0), (lnum, pos), line) + while column < indents[-1]: + if column not in indents: + raise IndentationError( + "unindent does not match any outer indentation level", + ("", lnum, pos, line)) + indents = indents[:-1] + + yield TokenInfo(DEDENT, '', (lnum, pos), (lnum, pos), line) + + else: # continued statement + if not line: + raise TokenError("EOF in multi-line statement", (lnum, 0)) + continued = 0 + + while pos < max: + pseudomatch = _compile(PseudoToken).match(line, pos) + if pseudomatch: # scan for tokens + start, end = pseudomatch.span(1) + spos, epos, pos = (lnum, start), (lnum, end), end + if start == end: + continue + token, initial = line[start:end], line[start] + + if (initial in numchars or # ordinary number + (initial == '.' and token != '.' and token != '...')): + yield TokenInfo(NUMBER, token, spos, epos, line) + elif initial in '\r\n': + if parenlev > 0: + yield TokenInfo(NL, token, spos, epos, line) + else: + yield TokenInfo(NEWLINE, token, spos, epos, line) + + elif initial == '#': + assert not token.endswith("\n") + yield TokenInfo(COMMENT, token, spos, epos, line) + + elif token in triple_quoted: + endprog = _compile(endpats[token]) + endmatch = endprog.match(line, pos) + if endmatch: # all on one line + pos = endmatch.end(0) + token = line[start:pos] + yield TokenInfo(STRING, token, spos, (lnum, pos), line) + else: + strstart = (lnum, start) # multiple lines + contstr = line[start:] + contline = line + break + + # Check up to the first 3 chars of the token to see if + # they're in the single_quoted set. If so, they start + # a string. + # We're using the first 3, because we're looking for + # "rb'" (for example) at the start of the token. If + # we switch to longer prefixes, this needs to be + # adjusted. + # Note that initial == token[:1]. + # Also note that single quote checking must come after + # triple quote checking (above). + elif (initial in single_quoted or + token[:2] in single_quoted or + token[:3] in single_quoted): + if token[-1] == '\n': # continued string + strstart = (lnum, start) + # Again, using the first 3 chars of the + # token. This is looking for the matching end + # regex for the correct type of quote + # character. So it's really looking for + # endpats["'"] or endpats['"'], by trying to + # skip string prefix characters, if any. + endprog = _compile(endpats.get(initial) or + endpats.get(token[1]) or + endpats.get(token[2])) + contstr, needcont = line[start:], 1 + contline = line + break + else: # ordinary string + yield TokenInfo(STRING, token, spos, epos, line) + + elif initial.isidentifier(): # ordinary name + yield TokenInfo(NAME, token, spos, epos, line) + elif initial == '\\': # continued stmt + continued = 1 + else: + if initial in '([{': + parenlev += 1 + elif initial in ')]}': + parenlev -= 1 + yield TokenInfo(OP, token, spos, epos, line) + else: + yield TokenInfo(ERRORTOKEN, line[pos], + (lnum, pos), (lnum, pos+1), line) + pos += 1 + + # Add an implicit NEWLINE if the input doesn't end in one + if last_line and last_line[-1] not in '\r\n': + yield TokenInfo(NEWLINE, '', (lnum - 1, len(last_line)), (lnum - 1, len(last_line) + 1), '') + for indent in indents[1:]: # pop remaining indent levels + yield TokenInfo(DEDENT, '', (lnum, 0), (lnum, 0), '') + yield TokenInfo(ENDMARKER, '', (lnum, 0), (lnum, 0), '') + + +def generate_tokens(readline): + """Tokenize a source reading Python code as unicode strings. + + This has the same API as tokenize(), except that it expects the *readline* + callable to return str objects instead of bytes. + """ + return _tokenize(readline, None) + +def main(): + import argparse + + # Helper error handling routines + def perror(message): + sys.stderr.write(message) + sys.stderr.write('\n') + + def error(message, filename=None, location=None): + if location: + args = (filename,) + location + (message,) + perror("%s:%d:%d: error: %s" % args) + elif filename: + perror("%s: error: %s" % (filename, message)) + else: + perror("error: %s" % message) + sys.exit(1) + + # Parse the arguments and options + parser = argparse.ArgumentParser(prog='python -m tokenize') + parser.add_argument(dest='filename', nargs='?', + metavar='filename.py', + help='the file to tokenize; defaults to stdin') + parser.add_argument('-e', '--exact', dest='exact', action='store_true', + help='display token names using the exact type') + args = parser.parse_args() + + try: + # Tokenize the input + if args.filename: + filename = args.filename + with _builtin_open(filename, 'rb') as f: + tokens = list(tokenize(f.readline)) + else: + filename = "" + tokens = _tokenize(sys.stdin.readline, None) + + # Output the tokenization + for token in tokens: + token_type = token.type + if args.exact: + token_type = token.exact_type + token_range = "%d,%d-%d,%d:" % (token.start + token.end) + print("%-20s%-15s%-15r" % + (token_range, tok_name[token_type], token.string)) + except IndentationError as err: + line, column = err.args[1][1:3] + error(err.args[0], filename, (line, column)) + except TokenError as err: + line, column = err.args[1] + error(err.args[0], filename, (line, column)) + except SyntaxError as err: + error(err, filename) + except OSError as err: + error(err) + except KeyboardInterrupt: + print("interrupted\n") + except Exception as err: + perror("unexpected error: %s" % err) + raise + +if __name__ == "__main__": + main() diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/tty.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/tty.py new file mode 100644 index 0000000000000000000000000000000000000000..a72eb6755450bb07d9bb77fb9cdc3cf2f0148534 --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/tty.py @@ -0,0 +1,36 @@ +"""Terminal utilities.""" + +# Author: Steen Lumholt. + +from termios import * + +__all__ = ["setraw", "setcbreak"] + +# Indexes for termios list. +IFLAG = 0 +OFLAG = 1 +CFLAG = 2 +LFLAG = 3 +ISPEED = 4 +OSPEED = 5 +CC = 6 + +def setraw(fd, when=TCSAFLUSH): + """Put terminal into a raw mode.""" + mode = tcgetattr(fd) + mode[IFLAG] = mode[IFLAG] & ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON) + mode[OFLAG] = mode[OFLAG] & ~(OPOST) + mode[CFLAG] = mode[CFLAG] & ~(CSIZE | PARENB) + mode[CFLAG] = mode[CFLAG] | CS8 + mode[LFLAG] = mode[LFLAG] & ~(ECHO | ICANON | IEXTEN | ISIG) + mode[CC][VMIN] = 1 + mode[CC][VTIME] = 0 + tcsetattr(fd, when, mode) + +def setcbreak(fd, when=TCSAFLUSH): + """Put terminal into a cbreak mode.""" + mode = tcgetattr(fd) + mode[LFLAG] = mode[LFLAG] & ~(ECHO | ICANON) + mode[CC][VMIN] = 1 + mode[CC][VTIME] = 0 + tcsetattr(fd, when, mode) diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/turtle.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/turtle.py new file mode 100644 index 0000000000000000000000000000000000000000..6e19032cce2a43598ee35ff8d7f00f3dc13ad700 --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/turtle.py @@ -0,0 +1,4140 @@ +# +# turtle.py: a Tkinter based turtle graphics module for Python +# Version 1.1b - 4. 5. 2009 +# +# Copyright (C) 2006 - 2010 Gregor Lingl +# email: glingl@aon.at +# +# This software is provided 'as-is', without any express or implied +# warranty. In no event will the authors be held liable for any damages +# arising from the use of this software. +# +# Permission is granted to anyone to use this software for any purpose, +# including commercial applications, and to alter it and redistribute it +# freely, subject to the following restrictions: +# +# 1. The origin of this software must not be misrepresented; you must not +# claim that you wrote the original software. If you use this software +# in a product, an acknowledgment in the product documentation would be +# appreciated but is not required. +# 2. Altered source versions must be plainly marked as such, and must not be +# misrepresented as being the original software. +# 3. This notice may not be removed or altered from any source distribution. + + +""" +Turtle graphics is a popular way for introducing programming to +kids. It was part of the original Logo programming language developed +by Wally Feurzig and Seymour Papert in 1966. + +Imagine a robotic turtle starting at (0, 0) in the x-y plane. After an ``import turtle``, give it +the command turtle.forward(15), and it moves (on-screen!) 15 pixels in +the direction it is facing, drawing a line as it moves. Give it the +command turtle.right(25), and it rotates in-place 25 degrees clockwise. + +By combining together these and similar commands, intricate shapes and +pictures can easily be drawn. + +----- turtle.py + +This module is an extended reimplementation of turtle.py from the +Python standard distribution up to Python 2.5. (See: http://www.python.org) + +It tries to keep the merits of turtle.py and to be (nearly) 100% +compatible with it. This means in the first place to enable the +learning programmer to use all the commands, classes and methods +interactively when using the module from within IDLE run with +the -n switch. + +Roughly it has the following features added: + +- Better animation of the turtle movements, especially of turning the + turtle. So the turtles can more easily be used as a visual feedback + instrument by the (beginning) programmer. + +- Different turtle shapes, gif-images as turtle shapes, user defined + and user controllable turtle shapes, among them compound + (multicolored) shapes. Turtle shapes can be stretched and tilted, which + makes turtles very versatile geometrical objects. + +- Fine control over turtle movement and screen updates via delay(), + and enhanced tracer() and speed() methods. + +- Aliases for the most commonly used commands, like fd for forward etc., + following the early Logo traditions. This reduces the boring work of + typing long sequences of commands, which often occur in a natural way + when kids try to program fancy pictures on their first encounter with + turtle graphics. + +- Turtles now have an undo()-method with configurable undo-buffer. + +- Some simple commands/methods for creating event driven programs + (mouse-, key-, timer-events). Especially useful for programming games. + +- A scrollable Canvas class. The default scrollable Canvas can be + extended interactively as needed while playing around with the turtle(s). + +- A TurtleScreen class with methods controlling background color or + background image, window and canvas size and other properties of the + TurtleScreen. + +- There is a method, setworldcoordinates(), to install a user defined + coordinate-system for the TurtleScreen. + +- The implementation uses a 2-vector class named Vec2D, derived from tuple. + This class is public, so it can be imported by the application programmer, + which makes certain types of computations very natural and compact. + +- Appearance of the TurtleScreen and the Turtles at startup/import can be + configured by means of a turtle.cfg configuration file. + The default configuration mimics the appearance of the old turtle module. + +- If configured appropriately the module reads in docstrings from a docstring + dictionary in some different language, supplied separately and replaces + the English ones by those read in. There is a utility function + write_docstringdict() to write a dictionary with the original (English) + docstrings to disc, so it can serve as a template for translations. + +Behind the scenes there are some features included with possible +extensions in mind. These will be commented and documented elsewhere. + +""" + +_ver = "turtle 1.1b- - for Python 3.1 - 4. 5. 2009" + +# print(_ver) + +import tkinter as TK +import types +import math +import time +import inspect +import sys + +from os.path import isfile, split, join +from copy import deepcopy +from tkinter import simpledialog + +_tg_classes = ['ScrolledCanvas', 'TurtleScreen', 'Screen', + 'RawTurtle', 'Turtle', 'RawPen', 'Pen', 'Shape', 'Vec2D'] +_tg_screen_functions = ['addshape', 'bgcolor', 'bgpic', 'bye', + 'clearscreen', 'colormode', 'delay', 'exitonclick', 'getcanvas', + 'getshapes', 'listen', 'mainloop', 'mode', 'numinput', + 'onkey', 'onkeypress', 'onkeyrelease', 'onscreenclick', 'ontimer', + 'register_shape', 'resetscreen', 'screensize', 'setup', + 'setworldcoordinates', 'textinput', 'title', 'tracer', 'turtles', 'update', + 'window_height', 'window_width'] +_tg_turtle_functions = ['back', 'backward', 'begin_fill', 'begin_poly', 'bk', + 'circle', 'clear', 'clearstamp', 'clearstamps', 'clone', 'color', + 'degrees', 'distance', 'dot', 'down', 'end_fill', 'end_poly', 'fd', + 'fillcolor', 'filling', 'forward', 'get_poly', 'getpen', 'getscreen', 'get_shapepoly', + 'getturtle', 'goto', 'heading', 'hideturtle', 'home', 'ht', 'isdown', + 'isvisible', 'left', 'lt', 'onclick', 'ondrag', 'onrelease', 'pd', + 'pen', 'pencolor', 'pendown', 'pensize', 'penup', 'pos', 'position', + 'pu', 'radians', 'right', 'reset', 'resizemode', 'rt', + 'seth', 'setheading', 'setpos', 'setposition', 'settiltangle', + 'setundobuffer', 'setx', 'sety', 'shape', 'shapesize', 'shapetransform', 'shearfactor', 'showturtle', + 'speed', 'st', 'stamp', 'tilt', 'tiltangle', 'towards', + 'turtlesize', 'undo', 'undobufferentries', 'up', 'width', + 'write', 'xcor', 'ycor'] +_tg_utilities = ['write_docstringdict', 'done'] + +__all__ = (_tg_classes + _tg_screen_functions + _tg_turtle_functions + + _tg_utilities + ['Terminator']) # + _math_functions) + +_alias_list = ['addshape', 'backward', 'bk', 'fd', 'ht', 'lt', 'pd', 'pos', + 'pu', 'rt', 'seth', 'setpos', 'setposition', 'st', + 'turtlesize', 'up', 'width'] + +_CFG = {"width" : 0.5, # Screen + "height" : 0.75, + "canvwidth" : 400, + "canvheight": 300, + "leftright": None, + "topbottom": None, + "mode": "standard", # TurtleScreen + "colormode": 1.0, + "delay": 10, + "undobuffersize": 1000, # RawTurtle + "shape": "classic", + "pencolor" : "black", + "fillcolor" : "black", + "resizemode" : "noresize", + "visible" : True, + "language": "english", # docstrings + "exampleturtle": "turtle", + "examplescreen": "screen", + "title": "Python Turtle Graphics", + "using_IDLE": False + } + +def config_dict(filename): + """Convert content of config-file into dictionary.""" + with open(filename, "r") as f: + cfglines = f.readlines() + cfgdict = {} + for line in cfglines: + line = line.strip() + if not line or line.startswith("#"): + continue + try: + key, value = line.split("=") + except ValueError: + print("Bad line in config-file %s:\n%s" % (filename,line)) + continue + key = key.strip() + value = value.strip() + if value in ["True", "False", "None", "''", '""']: + value = eval(value) + else: + try: + if "." in value: + value = float(value) + else: + value = int(value) + except ValueError: + pass # value need not be converted + cfgdict[key] = value + return cfgdict + +def readconfig(cfgdict): + """Read config-files, change configuration-dict accordingly. + + If there is a turtle.cfg file in the current working directory, + read it from there. If this contains an importconfig-value, + say 'myway', construct filename turtle_mayway.cfg else use + turtle.cfg and read it from the import-directory, where + turtle.py is located. + Update configuration dictionary first according to config-file, + in the import directory, then according to config-file in the + current working directory. + If no config-file is found, the default configuration is used. + """ + default_cfg = "turtle.cfg" + cfgdict1 = {} + cfgdict2 = {} + if isfile(default_cfg): + cfgdict1 = config_dict(default_cfg) + if "importconfig" in cfgdict1: + default_cfg = "turtle_%s.cfg" % cfgdict1["importconfig"] + try: + head, tail = split(__file__) + cfg_file2 = join(head, default_cfg) + except Exception: + cfg_file2 = "" + if isfile(cfg_file2): + cfgdict2 = config_dict(cfg_file2) + _CFG.update(cfgdict2) + _CFG.update(cfgdict1) + +try: + readconfig(_CFG) +except Exception: + print ("No configfile read, reason unknown") + + +class Vec2D(tuple): + """A 2 dimensional vector class, used as a helper class + for implementing turtle graphics. + May be useful for turtle graphics programs also. + Derived from tuple, so a vector is a tuple! + + Provides (for a, b vectors, k number): + a+b vector addition + a-b vector subtraction + a*b inner product + k*a and a*k multiplication with scalar + |a| absolute value of a + a.rotate(angle) rotation + """ + def __new__(cls, x, y): + return tuple.__new__(cls, (x, y)) + def __add__(self, other): + return Vec2D(self[0]+other[0], self[1]+other[1]) + def __mul__(self, other): + if isinstance(other, Vec2D): + return self[0]*other[0]+self[1]*other[1] + return Vec2D(self[0]*other, self[1]*other) + def __rmul__(self, other): + if isinstance(other, int) or isinstance(other, float): + return Vec2D(self[0]*other, self[1]*other) + return NotImplemented + def __sub__(self, other): + return Vec2D(self[0]-other[0], self[1]-other[1]) + def __neg__(self): + return Vec2D(-self[0], -self[1]) + def __abs__(self): + return (self[0]**2 + self[1]**2)**0.5 + def rotate(self, angle): + """rotate self counterclockwise by angle + """ + perp = Vec2D(-self[1], self[0]) + angle = angle * math.pi / 180.0 + c, s = math.cos(angle), math.sin(angle) + return Vec2D(self[0]*c+perp[0]*s, self[1]*c+perp[1]*s) + def __getnewargs__(self): + return (self[0], self[1]) + def __repr__(self): + return "(%.2f,%.2f)" % self + + +############################################################################## +### From here up to line : Tkinter - Interface for turtle.py ### +### May be replaced by an interface to some different graphics toolkit ### +############################################################################## + +## helper functions for Scrolled Canvas, to forward Canvas-methods +## to ScrolledCanvas class + +def __methodDict(cls, _dict): + """helper function for Scrolled Canvas""" + baseList = list(cls.__bases__) + baseList.reverse() + for _super in baseList: + __methodDict(_super, _dict) + for key, value in cls.__dict__.items(): + if type(value) == types.FunctionType: + _dict[key] = value + +def __methods(cls): + """helper function for Scrolled Canvas""" + _dict = {} + __methodDict(cls, _dict) + return _dict.keys() + +__stringBody = ( + 'def %(method)s(self, *args, **kw): return ' + + 'self.%(attribute)s.%(method)s(*args, **kw)') + +def __forwardmethods(fromClass, toClass, toPart, exclude = ()): + ### MANY CHANGES ### + _dict_1 = {} + __methodDict(toClass, _dict_1) + _dict = {} + mfc = __methods(fromClass) + for ex in _dict_1.keys(): + if ex[:1] == '_' or ex[-1:] == '_' or ex in exclude or ex in mfc: + pass + else: + _dict[ex] = _dict_1[ex] + + for method, func in _dict.items(): + d = {'method': method, 'func': func} + if isinstance(toPart, str): + execString = \ + __stringBody % {'method' : method, 'attribute' : toPart} + exec(execString, d) + setattr(fromClass, method, d[method]) ### NEWU! + + +class ScrolledCanvas(TK.Frame): + """Modeled after the scrolled canvas class from Grayons's Tkinter book. + + Used as the default canvas, which pops up automatically when + using turtle graphics functions or the Turtle class. + """ + def __init__(self, master, width=500, height=350, + canvwidth=600, canvheight=500): + TK.Frame.__init__(self, master, width=width, height=height) + self._rootwindow = self.winfo_toplevel() + self.width, self.height = width, height + self.canvwidth, self.canvheight = canvwidth, canvheight + self.bg = "white" + self._canvas = TK.Canvas(master, width=width, height=height, + bg=self.bg, relief=TK.SUNKEN, borderwidth=2) + self.hscroll = TK.Scrollbar(master, command=self._canvas.xview, + orient=TK.HORIZONTAL) + self.vscroll = TK.Scrollbar(master, command=self._canvas.yview) + self._canvas.configure(xscrollcommand=self.hscroll.set, + yscrollcommand=self.vscroll.set) + self.rowconfigure(0, weight=1, minsize=0) + self.columnconfigure(0, weight=1, minsize=0) + self._canvas.grid(padx=1, in_ = self, pady=1, row=0, + column=0, rowspan=1, columnspan=1, sticky='news') + self.vscroll.grid(padx=1, in_ = self, pady=1, row=0, + column=1, rowspan=1, columnspan=1, sticky='news') + self.hscroll.grid(padx=1, in_ = self, pady=1, row=1, + column=0, rowspan=1, columnspan=1, sticky='news') + self.reset() + self._rootwindow.bind('', self.onResize) + + def reset(self, canvwidth=None, canvheight=None, bg = None): + """Adjust canvas and scrollbars according to given canvas size.""" + if canvwidth: + self.canvwidth = canvwidth + if canvheight: + self.canvheight = canvheight + if bg: + self.bg = bg + self._canvas.config(bg=bg, + scrollregion=(-self.canvwidth//2, -self.canvheight//2, + self.canvwidth//2, self.canvheight//2)) + self._canvas.xview_moveto(0.5*(self.canvwidth - self.width + 30) / + self.canvwidth) + self._canvas.yview_moveto(0.5*(self.canvheight- self.height + 30) / + self.canvheight) + self.adjustScrolls() + + + def adjustScrolls(self): + """ Adjust scrollbars according to window- and canvas-size. + """ + cwidth = self._canvas.winfo_width() + cheight = self._canvas.winfo_height() + self._canvas.xview_moveto(0.5*(self.canvwidth-cwidth)/self.canvwidth) + self._canvas.yview_moveto(0.5*(self.canvheight-cheight)/self.canvheight) + if cwidth < self.canvwidth or cheight < self.canvheight: + self.hscroll.grid(padx=1, in_ = self, pady=1, row=1, + column=0, rowspan=1, columnspan=1, sticky='news') + self.vscroll.grid(padx=1, in_ = self, pady=1, row=0, + column=1, rowspan=1, columnspan=1, sticky='news') + else: + self.hscroll.grid_forget() + self.vscroll.grid_forget() + + def onResize(self, event): + """self-explanatory""" + self.adjustScrolls() + + def bbox(self, *args): + """ 'forward' method, which canvas itself has inherited... + """ + return self._canvas.bbox(*args) + + def cget(self, *args, **kwargs): + """ 'forward' method, which canvas itself has inherited... + """ + return self._canvas.cget(*args, **kwargs) + + def config(self, *args, **kwargs): + """ 'forward' method, which canvas itself has inherited... + """ + self._canvas.config(*args, **kwargs) + + def bind(self, *args, **kwargs): + """ 'forward' method, which canvas itself has inherited... + """ + self._canvas.bind(*args, **kwargs) + + def unbind(self, *args, **kwargs): + """ 'forward' method, which canvas itself has inherited... + """ + self._canvas.unbind(*args, **kwargs) + + def focus_force(self): + """ 'forward' method, which canvas itself has inherited... + """ + self._canvas.focus_force() + +__forwardmethods(ScrolledCanvas, TK.Canvas, '_canvas') + + +class _Root(TK.Tk): + """Root class for Screen based on Tkinter.""" + def __init__(self): + TK.Tk.__init__(self) + + def setupcanvas(self, width, height, cwidth, cheight): + self._canvas = ScrolledCanvas(self, width, height, cwidth, cheight) + self._canvas.pack(expand=1, fill="both") + + def _getcanvas(self): + return self._canvas + + def set_geometry(self, width, height, startx, starty): + self.geometry("%dx%d%+d%+d"%(width, height, startx, starty)) + + def ondestroy(self, destroy): + self.wm_protocol("WM_DELETE_WINDOW", destroy) + + def win_width(self): + return self.winfo_screenwidth() + + def win_height(self): + return self.winfo_screenheight() + +Canvas = TK.Canvas + + +class TurtleScreenBase(object): + """Provide the basic graphics functionality. + Interface between Tkinter and turtle.py. + + To port turtle.py to some different graphics toolkit + a corresponding TurtleScreenBase class has to be implemented. + """ + + def _blankimage(self): + """return a blank image object + """ + img = TK.PhotoImage(width=1, height=1, master=self.cv) + img.blank() + return img + + def _image(self, filename): + """return an image object containing the + imagedata from a gif-file named filename. + """ + return TK.PhotoImage(file=filename, master=self.cv) + + def __init__(self, cv): + self.cv = cv + if isinstance(cv, ScrolledCanvas): + w = self.cv.canvwidth + h = self.cv.canvheight + else: # expected: ordinary TK.Canvas + w = int(self.cv.cget("width")) + h = int(self.cv.cget("height")) + self.cv.config(scrollregion = (-w//2, -h//2, w//2, h//2 )) + self.canvwidth = w + self.canvheight = h + self.xscale = self.yscale = 1.0 + + def _createpoly(self): + """Create an invisible polygon item on canvas self.cv) + """ + return self.cv.create_polygon((0, 0, 0, 0, 0, 0), fill="", outline="") + + def _drawpoly(self, polyitem, coordlist, fill=None, + outline=None, width=None, top=False): + """Configure polygonitem polyitem according to provided + arguments: + coordlist is sequence of coordinates + fill is filling color + outline is outline color + top is a boolean value, which specifies if polyitem + will be put on top of the canvas' displaylist so it + will not be covered by other items. + """ + cl = [] + for x, y in coordlist: + cl.append(x * self.xscale) + cl.append(-y * self.yscale) + self.cv.coords(polyitem, *cl) + if fill is not None: + self.cv.itemconfigure(polyitem, fill=fill) + if outline is not None: + self.cv.itemconfigure(polyitem, outline=outline) + if width is not None: + self.cv.itemconfigure(polyitem, width=width) + if top: + self.cv.tag_raise(polyitem) + + def _createline(self): + """Create an invisible line item on canvas self.cv) + """ + return self.cv.create_line(0, 0, 0, 0, fill="", width=2, + capstyle = TK.ROUND) + + def _drawline(self, lineitem, coordlist=None, + fill=None, width=None, top=False): + """Configure lineitem according to provided arguments: + coordlist is sequence of coordinates + fill is drawing color + width is width of drawn line. + top is a boolean value, which specifies if polyitem + will be put on top of the canvas' displaylist so it + will not be covered by other items. + """ + if coordlist is not None: + cl = [] + for x, y in coordlist: + cl.append(x * self.xscale) + cl.append(-y * self.yscale) + self.cv.coords(lineitem, *cl) + if fill is not None: + self.cv.itemconfigure(lineitem, fill=fill) + if width is not None: + self.cv.itemconfigure(lineitem, width=width) + if top: + self.cv.tag_raise(lineitem) + + def _delete(self, item): + """Delete graphics item from canvas. + If item is"all" delete all graphics items. + """ + self.cv.delete(item) + + def _update(self): + """Redraw graphics items on canvas + """ + self.cv.update() + + def _delay(self, delay): + """Delay subsequent canvas actions for delay ms.""" + self.cv.after(delay) + + def _iscolorstring(self, color): + """Check if the string color is a legal Tkinter color string. + """ + try: + rgb = self.cv.winfo_rgb(color) + ok = True + except TK.TclError: + ok = False + return ok + + def _bgcolor(self, color=None): + """Set canvas' backgroundcolor if color is not None, + else return backgroundcolor.""" + if color is not None: + self.cv.config(bg = color) + self._update() + else: + return self.cv.cget("bg") + + def _write(self, pos, txt, align, font, pencolor): + """Write txt at pos in canvas with specified font + and color. + Return text item and x-coord of right bottom corner + of text's bounding box.""" + x, y = pos + x = x * self.xscale + y = y * self.yscale + anchor = {"left":"sw", "center":"s", "right":"se" } + item = self.cv.create_text(x-1, -y, text = txt, anchor = anchor[align], + fill = pencolor, font = font) + x0, y0, x1, y1 = self.cv.bbox(item) + self.cv.update() + return item, x1-1 + +## def _dot(self, pos, size, color): +## """may be implemented for some other graphics toolkit""" + + def _onclick(self, item, fun, num=1, add=None): + """Bind fun to mouse-click event on turtle. + fun must be a function with two arguments, the coordinates + of the clicked point on the canvas. + num, the number of the mouse-button defaults to 1 + """ + if fun is None: + self.cv.tag_unbind(item, "" % num) + else: + def eventfun(event): + x, y = (self.cv.canvasx(event.x)/self.xscale, + -self.cv.canvasy(event.y)/self.yscale) + fun(x, y) + self.cv.tag_bind(item, "" % num, eventfun, add) + + def _onrelease(self, item, fun, num=1, add=None): + """Bind fun to mouse-button-release event on turtle. + fun must be a function with two arguments, the coordinates + of the point on the canvas where mouse button is released. + num, the number of the mouse-button defaults to 1 + + If a turtle is clicked, first _onclick-event will be performed, + then _onscreensclick-event. + """ + if fun is None: + self.cv.tag_unbind(item, "" % num) + else: + def eventfun(event): + x, y = (self.cv.canvasx(event.x)/self.xscale, + -self.cv.canvasy(event.y)/self.yscale) + fun(x, y) + self.cv.tag_bind(item, "" % num, + eventfun, add) + + def _ondrag(self, item, fun, num=1, add=None): + """Bind fun to mouse-move-event (with pressed mouse button) on turtle. + fun must be a function with two arguments, the coordinates of the + actual mouse position on the canvas. + num, the number of the mouse-button defaults to 1 + + Every sequence of mouse-move-events on a turtle is preceded by a + mouse-click event on that turtle. + """ + if fun is None: + self.cv.tag_unbind(item, "" % num) + else: + def eventfun(event): + try: + x, y = (self.cv.canvasx(event.x)/self.xscale, + -self.cv.canvasy(event.y)/self.yscale) + fun(x, y) + except Exception: + pass + self.cv.tag_bind(item, "" % num, eventfun, add) + + def _onscreenclick(self, fun, num=1, add=None): + """Bind fun to mouse-click event on canvas. + fun must be a function with two arguments, the coordinates + of the clicked point on the canvas. + num, the number of the mouse-button defaults to 1 + + If a turtle is clicked, first _onclick-event will be performed, + then _onscreensclick-event. + """ + if fun is None: + self.cv.unbind("" % num) + else: + def eventfun(event): + x, y = (self.cv.canvasx(event.x)/self.xscale, + -self.cv.canvasy(event.y)/self.yscale) + fun(x, y) + self.cv.bind("" % num, eventfun, add) + + def _onkeyrelease(self, fun, key): + """Bind fun to key-release event of key. + Canvas must have focus. See method listen + """ + if fun is None: + self.cv.unbind("" % key, None) + else: + def eventfun(event): + fun() + self.cv.bind("" % key, eventfun) + + def _onkeypress(self, fun, key=None): + """If key is given, bind fun to key-press event of key. + Otherwise bind fun to any key-press. + Canvas must have focus. See method listen. + """ + if fun is None: + if key is None: + self.cv.unbind("", None) + else: + self.cv.unbind("" % key, None) + else: + def eventfun(event): + fun() + if key is None: + self.cv.bind("", eventfun) + else: + self.cv.bind("" % key, eventfun) + + def _listen(self): + """Set focus on canvas (in order to collect key-events) + """ + self.cv.focus_force() + + def _ontimer(self, fun, t): + """Install a timer, which calls fun after t milliseconds. + """ + if t == 0: + self.cv.after_idle(fun) + else: + self.cv.after(t, fun) + + def _createimage(self, image): + """Create and return image item on canvas. + """ + return self.cv.create_image(0, 0, image=image) + + def _drawimage(self, item, pos, image): + """Configure image item as to draw image object + at position (x,y) on canvas) + """ + x, y = pos + self.cv.coords(item, (x * self.xscale, -y * self.yscale)) + self.cv.itemconfig(item, image=image) + + def _setbgpic(self, item, image): + """Configure image item as to draw image object + at center of canvas. Set item to the first item + in the displaylist, so it will be drawn below + any other item .""" + self.cv.itemconfig(item, image=image) + self.cv.tag_lower(item) + + def _type(self, item): + """Return 'line' or 'polygon' or 'image' depending on + type of item. + """ + return self.cv.type(item) + + def _pointlist(self, item): + """returns list of coordinate-pairs of points of item + Example (for insiders): + >>> from turtle import * + >>> getscreen()._pointlist(getturtle().turtle._item) + [(0.0, 9.9999999999999982), (0.0, -9.9999999999999982), + (9.9999999999999982, 0.0)] + >>> """ + cl = self.cv.coords(item) + pl = [(cl[i], -cl[i+1]) for i in range(0, len(cl), 2)] + return pl + + def _setscrollregion(self, srx1, sry1, srx2, sry2): + self.cv.config(scrollregion=(srx1, sry1, srx2, sry2)) + + def _rescale(self, xscalefactor, yscalefactor): + items = self.cv.find_all() + for item in items: + coordinates = list(self.cv.coords(item)) + newcoordlist = [] + while coordinates: + x, y = coordinates[:2] + newcoordlist.append(x * xscalefactor) + newcoordlist.append(y * yscalefactor) + coordinates = coordinates[2:] + self.cv.coords(item, *newcoordlist) + + def _resize(self, canvwidth=None, canvheight=None, bg=None): + """Resize the canvas the turtles are drawing on. Does + not alter the drawing window. + """ + # needs amendment + if not isinstance(self.cv, ScrolledCanvas): + return self.canvwidth, self.canvheight + if canvwidth is canvheight is bg is None: + return self.cv.canvwidth, self.cv.canvheight + if canvwidth is not None: + self.canvwidth = canvwidth + if canvheight is not None: + self.canvheight = canvheight + self.cv.reset(canvwidth, canvheight, bg) + + def _window_size(self): + """ Return the width and height of the turtle window. + """ + width = self.cv.winfo_width() + if width <= 1: # the window isn't managed by a geometry manager + width = self.cv['width'] + height = self.cv.winfo_height() + if height <= 1: # the window isn't managed by a geometry manager + height = self.cv['height'] + return width, height + + def mainloop(self): + """Starts event loop - calling Tkinter's mainloop function. + + No argument. + + Must be last statement in a turtle graphics program. + Must NOT be used if a script is run from within IDLE in -n mode + (No subprocess) - for interactive use of turtle graphics. + + Example (for a TurtleScreen instance named screen): + >>> screen.mainloop() + + """ + self.cv.tk.mainloop() + + def textinput(self, title, prompt): + """Pop up a dialog window for input of a string. + + Arguments: title is the title of the dialog window, + prompt is a text mostly describing what information to input. + + Return the string input + If the dialog is canceled, return None. + + Example (for a TurtleScreen instance named screen): + >>> screen.textinput("NIM", "Name of first player:") + + """ + return simpledialog.askstring(title, prompt, parent=self.cv) + + def numinput(self, title, prompt, default=None, minval=None, maxval=None): + """Pop up a dialog window for input of a number. + + Arguments: title is the title of the dialog window, + prompt is a text mostly describing what numerical information to input. + default: default value + minval: minimum value for input + maxval: maximum value for input + + The number input must be in the range minval .. maxval if these are + given. If not, a hint is issued and the dialog remains open for + correction. Return the number input. + If the dialog is canceled, return None. + + Example (for a TurtleScreen instance named screen): + >>> screen.numinput("Poker", "Your stakes:", 1000, minval=10, maxval=10000) + + """ + return simpledialog.askfloat(title, prompt, initialvalue=default, + minvalue=minval, maxvalue=maxval, + parent=self.cv) + + +############################################################################## +### End of Tkinter - interface ### +############################################################################## + + +class Terminator (Exception): + """Will be raised in TurtleScreen.update, if _RUNNING becomes False. + + This stops execution of a turtle graphics script. + Main purpose: use in the Demo-Viewer turtle.Demo.py. + """ + pass + + +class TurtleGraphicsError(Exception): + """Some TurtleGraphics Error + """ + + +class Shape(object): + """Data structure modeling shapes. + + attribute _type is one of "polygon", "image", "compound" + attribute _data is - depending on _type a poygon-tuple, + an image or a list constructed using the addcomponent method. + """ + def __init__(self, type_, data=None): + self._type = type_ + if type_ == "polygon": + if isinstance(data, list): + data = tuple(data) + elif type_ == "image": + if isinstance(data, str): + if data.lower().endswith(".gif") and isfile(data): + data = TurtleScreen._image(data) + # else data assumed to be Photoimage + elif type_ == "compound": + data = [] + else: + raise TurtleGraphicsError("There is no shape type %s" % type_) + self._data = data + + def addcomponent(self, poly, fill, outline=None): + """Add component to a shape of type compound. + + Arguments: poly is a polygon, i. e. a tuple of number pairs. + fill is the fillcolor of the component, + outline is the outline color of the component. + + call (for a Shapeobject namend s): + -- s.addcomponent(((0,0), (10,10), (-10,10)), "red", "blue") + + Example: + >>> poly = ((0,0),(10,-5),(0,10),(-10,-5)) + >>> s = Shape("compound") + >>> s.addcomponent(poly, "red", "blue") + >>> # .. add more components and then use register_shape() + """ + if self._type != "compound": + raise TurtleGraphicsError("Cannot add component to %s Shape" + % self._type) + if outline is None: + outline = fill + self._data.append([poly, fill, outline]) + + +class Tbuffer(object): + """Ring buffer used as undobuffer for RawTurtle objects.""" + def __init__(self, bufsize=10): + self.bufsize = bufsize + self.buffer = [[None]] * bufsize + self.ptr = -1 + self.cumulate = False + def reset(self, bufsize=None): + if bufsize is None: + for i in range(self.bufsize): + self.buffer[i] = [None] + else: + self.bufsize = bufsize + self.buffer = [[None]] * bufsize + self.ptr = -1 + def push(self, item): + if self.bufsize > 0: + if not self.cumulate: + self.ptr = (self.ptr + 1) % self.bufsize + self.buffer[self.ptr] = item + else: + self.buffer[self.ptr].append(item) + def pop(self): + if self.bufsize > 0: + item = self.buffer[self.ptr] + if item is None: + return None + else: + self.buffer[self.ptr] = [None] + self.ptr = (self.ptr - 1) % self.bufsize + return (item) + def nr_of_items(self): + return self.bufsize - self.buffer.count([None]) + def __repr__(self): + return str(self.buffer) + " " + str(self.ptr) + + + +class TurtleScreen(TurtleScreenBase): + """Provides screen oriented methods like setbg etc. + + Only relies upon the methods of TurtleScreenBase and NOT + upon components of the underlying graphics toolkit - + which is Tkinter in this case. + """ + _RUNNING = True + + def __init__(self, cv, mode=_CFG["mode"], + colormode=_CFG["colormode"], delay=_CFG["delay"]): + TurtleScreenBase.__init__(self, cv) + + self._shapes = { + "arrow" : Shape("polygon", ((-10,0), (10,0), (0,10))), + "turtle" : Shape("polygon", ((0,16), (-2,14), (-1,10), (-4,7), + (-7,9), (-9,8), (-6,5), (-7,1), (-5,-3), (-8,-6), + (-6,-8), (-4,-5), (0,-7), (4,-5), (6,-8), (8,-6), + (5,-3), (7,1), (6,5), (9,8), (7,9), (4,7), (1,10), + (2,14))), + "circle" : Shape("polygon", ((10,0), (9.51,3.09), (8.09,5.88), + (5.88,8.09), (3.09,9.51), (0,10), (-3.09,9.51), + (-5.88,8.09), (-8.09,5.88), (-9.51,3.09), (-10,0), + (-9.51,-3.09), (-8.09,-5.88), (-5.88,-8.09), + (-3.09,-9.51), (-0.00,-10.00), (3.09,-9.51), + (5.88,-8.09), (8.09,-5.88), (9.51,-3.09))), + "square" : Shape("polygon", ((10,-10), (10,10), (-10,10), + (-10,-10))), + "triangle" : Shape("polygon", ((10,-5.77), (0,11.55), + (-10,-5.77))), + "classic": Shape("polygon", ((0,0),(-5,-9),(0,-7),(5,-9))), + "blank" : Shape("image", self._blankimage()) + } + + self._bgpics = {"nopic" : ""} + + self._mode = mode + self._delayvalue = delay + self._colormode = _CFG["colormode"] + self._keys = [] + self.clear() + if sys.platform == 'darwin': + # Force Turtle window to the front on OS X. This is needed because + # the Turtle window will show behind the Terminal window when you + # start the demo from the command line. + rootwindow = cv.winfo_toplevel() + rootwindow.call('wm', 'attributes', '.', '-topmost', '1') + rootwindow.call('wm', 'attributes', '.', '-topmost', '0') + + def clear(self): + """Delete all drawings and all turtles from the TurtleScreen. + + No argument. + + Reset empty TurtleScreen to its initial state: white background, + no backgroundimage, no eventbindings and tracing on. + + Example (for a TurtleScreen instance named screen): + >>> screen.clear() + + Note: this method is not available as function. + """ + self._delayvalue = _CFG["delay"] + self._colormode = _CFG["colormode"] + self._delete("all") + self._bgpic = self._createimage("") + self._bgpicname = "nopic" + self._tracing = 1 + self._updatecounter = 0 + self._turtles = [] + self.bgcolor("white") + for btn in 1, 2, 3: + self.onclick(None, btn) + self.onkeypress(None) + for key in self._keys[:]: + self.onkey(None, key) + self.onkeypress(None, key) + Turtle._pen = None + + def mode(self, mode=None): + """Set turtle-mode ('standard', 'logo' or 'world') and perform reset. + + Optional argument: + mode -- one of the strings 'standard', 'logo' or 'world' + + Mode 'standard' is compatible with turtle.py. + Mode 'logo' is compatible with most Logo-Turtle-Graphics. + Mode 'world' uses userdefined 'worldcoordinates'. *Attention*: in + this mode angles appear distorted if x/y unit-ratio doesn't equal 1. + If mode is not given, return the current mode. + + Mode Initial turtle heading positive angles + ------------|-------------------------|------------------- + 'standard' to the right (east) counterclockwise + 'logo' upward (north) clockwise + + Examples: + >>> mode('logo') # resets turtle heading to north + >>> mode() + 'logo' + """ + if mode is None: + return self._mode + mode = mode.lower() + if mode not in ["standard", "logo", "world"]: + raise TurtleGraphicsError("No turtle-graphics-mode %s" % mode) + self._mode = mode + if mode in ["standard", "logo"]: + self._setscrollregion(-self.canvwidth//2, -self.canvheight//2, + self.canvwidth//2, self.canvheight//2) + self.xscale = self.yscale = 1.0 + self.reset() + + def setworldcoordinates(self, llx, lly, urx, ury): + """Set up a user defined coordinate-system. + + Arguments: + llx -- a number, x-coordinate of lower left corner of canvas + lly -- a number, y-coordinate of lower left corner of canvas + urx -- a number, x-coordinate of upper right corner of canvas + ury -- a number, y-coordinate of upper right corner of canvas + + Set up user coodinat-system and switch to mode 'world' if necessary. + This performs a screen.reset. If mode 'world' is already active, + all drawings are redrawn according to the new coordinates. + + But ATTENTION: in user-defined coordinatesystems angles may appear + distorted. (see Screen.mode()) + + Example (for a TurtleScreen instance named screen): + >>> screen.setworldcoordinates(-10,-0.5,50,1.5) + >>> for _ in range(36): + ... left(10) + ... forward(0.5) + """ + if self.mode() != "world": + self.mode("world") + xspan = float(urx - llx) + yspan = float(ury - lly) + wx, wy = self._window_size() + self.screensize(wx-20, wy-20) + oldxscale, oldyscale = self.xscale, self.yscale + self.xscale = self.canvwidth / xspan + self.yscale = self.canvheight / yspan + srx1 = llx * self.xscale + sry1 = -ury * self.yscale + srx2 = self.canvwidth + srx1 + sry2 = self.canvheight + sry1 + self._setscrollregion(srx1, sry1, srx2, sry2) + self._rescale(self.xscale/oldxscale, self.yscale/oldyscale) + self.update() + + def register_shape(self, name, shape=None): + """Adds a turtle shape to TurtleScreen's shapelist. + + Arguments: + (1) name is the name of a gif-file and shape is None. + Installs the corresponding image shape. + !! Image-shapes DO NOT rotate when turning the turtle, + !! so they do not display the heading of the turtle! + (2) name is an arbitrary string and shape is a tuple + of pairs of coordinates. Installs the corresponding + polygon shape + (3) name is an arbitrary string and shape is a + (compound) Shape object. Installs the corresponding + compound shape. + To use a shape, you have to issue the command shape(shapename). + + call: register_shape("turtle.gif") + --or: register_shape("tri", ((0,0), (10,10), (-10,10))) + + Example (for a TurtleScreen instance named screen): + >>> screen.register_shape("triangle", ((5,-3),(0,5),(-5,-3))) + + """ + if shape is None: + # image + if name.lower().endswith(".gif"): + shape = Shape("image", self._image(name)) + else: + raise TurtleGraphicsError("Bad arguments for register_shape.\n" + + "Use help(register_shape)" ) + elif isinstance(shape, tuple): + shape = Shape("polygon", shape) + ## else shape assumed to be Shape-instance + self._shapes[name] = shape + + def _colorstr(self, color): + """Return color string corresponding to args. + + Argument may be a string or a tuple of three + numbers corresponding to actual colormode, + i.e. in the range 0<=n<=colormode. + + If the argument doesn't represent a color, + an error is raised. + """ + if len(color) == 1: + color = color[0] + if isinstance(color, str): + if self._iscolorstring(color) or color == "": + return color + else: + raise TurtleGraphicsError("bad color string: %s" % str(color)) + try: + r, g, b = color + except (TypeError, ValueError): + raise TurtleGraphicsError("bad color arguments: %s" % str(color)) + if self._colormode == 1.0: + r, g, b = [round(255.0*x) for x in (r, g, b)] + if not ((0 <= r <= 255) and (0 <= g <= 255) and (0 <= b <= 255)): + raise TurtleGraphicsError("bad color sequence: %s" % str(color)) + return "#%02x%02x%02x" % (r, g, b) + + def _color(self, cstr): + if not cstr.startswith("#"): + return cstr + if len(cstr) == 7: + cl = [int(cstr[i:i+2], 16) for i in (1, 3, 5)] + elif len(cstr) == 4: + cl = [16*int(cstr[h], 16) for h in cstr[1:]] + else: + raise TurtleGraphicsError("bad colorstring: %s" % cstr) + return tuple(c * self._colormode/255 for c in cl) + + def colormode(self, cmode=None): + """Return the colormode or set it to 1.0 or 255. + + Optional argument: + cmode -- one of the values 1.0 or 255 + + r, g, b values of colortriples have to be in range 0..cmode. + + Example (for a TurtleScreen instance named screen): + >>> screen.colormode() + 1.0 + >>> screen.colormode(255) + >>> pencolor(240,160,80) + """ + if cmode is None: + return self._colormode + if cmode == 1.0: + self._colormode = float(cmode) + elif cmode == 255: + self._colormode = int(cmode) + + def reset(self): + """Reset all Turtles on the Screen to their initial state. + + No argument. + + Example (for a TurtleScreen instance named screen): + >>> screen.reset() + """ + for turtle in self._turtles: + turtle._setmode(self._mode) + turtle.reset() + + def turtles(self): + """Return the list of turtles on the screen. + + Example (for a TurtleScreen instance named screen): + >>> screen.turtles() + [] + """ + return self._turtles + + def bgcolor(self, *args): + """Set or return backgroundcolor of the TurtleScreen. + + Arguments (if given): a color string or three numbers + in the range 0..colormode or a 3-tuple of such numbers. + + Example (for a TurtleScreen instance named screen): + >>> screen.bgcolor("orange") + >>> screen.bgcolor() + 'orange' + >>> screen.bgcolor(0.5,0,0.5) + >>> screen.bgcolor() + '#800080' + """ + if args: + color = self._colorstr(args) + else: + color = None + color = self._bgcolor(color) + if color is not None: + color = self._color(color) + return color + + def tracer(self, n=None, delay=None): + """Turns turtle animation on/off and set delay for update drawings. + + Optional arguments: + n -- nonnegative integer + delay -- nonnegative integer + + If n is given, only each n-th regular screen update is really performed. + (Can be used to accelerate the drawing of complex graphics.) + Second arguments sets delay value (see RawTurtle.delay()) + + Example (for a TurtleScreen instance named screen): + >>> screen.tracer(8, 25) + >>> dist = 2 + >>> for i in range(200): + ... fd(dist) + ... rt(90) + ... dist += 2 + """ + if n is None: + return self._tracing + self._tracing = int(n) + self._updatecounter = 0 + if delay is not None: + self._delayvalue = int(delay) + if self._tracing: + self.update() + + def delay(self, delay=None): + """ Return or set the drawing delay in milliseconds. + + Optional argument: + delay -- positive integer + + Example (for a TurtleScreen instance named screen): + >>> screen.delay(15) + >>> screen.delay() + 15 + """ + if delay is None: + return self._delayvalue + self._delayvalue = int(delay) + + def _incrementudc(self): + """Increment update counter.""" + if not TurtleScreen._RUNNING: + TurtleScreen._RUNNING = True + raise Terminator + if self._tracing > 0: + self._updatecounter += 1 + self._updatecounter %= self._tracing + + def update(self): + """Perform a TurtleScreen update. + """ + tracing = self._tracing + self._tracing = True + for t in self.turtles(): + t._update_data() + t._drawturtle() + self._tracing = tracing + self._update() + + def window_width(self): + """ Return the width of the turtle window. + + Example (for a TurtleScreen instance named screen): + >>> screen.window_width() + 640 + """ + return self._window_size()[0] + + def window_height(self): + """ Return the height of the turtle window. + + Example (for a TurtleScreen instance named screen): + >>> screen.window_height() + 480 + """ + return self._window_size()[1] + + def getcanvas(self): + """Return the Canvas of this TurtleScreen. + + No argument. + + Example (for a Screen instance named screen): + >>> cv = screen.getcanvas() + >>> cv + + """ + return self.cv + + def getshapes(self): + """Return a list of names of all currently available turtle shapes. + + No argument. + + Example (for a TurtleScreen instance named screen): + >>> screen.getshapes() + ['arrow', 'blank', 'circle', ... , 'turtle'] + """ + return sorted(self._shapes.keys()) + + def onclick(self, fun, btn=1, add=None): + """Bind fun to mouse-click event on canvas. + + Arguments: + fun -- a function with two arguments, the coordinates of the + clicked point on the canvas. + btn -- the number of the mouse-button, defaults to 1 + + Example (for a TurtleScreen instance named screen) + + >>> screen.onclick(goto) + >>> # Subsequently clicking into the TurtleScreen will + >>> # make the turtle move to the clicked point. + >>> screen.onclick(None) + """ + self._onscreenclick(fun, btn, add) + + def onkey(self, fun, key): + """Bind fun to key-release event of key. + + Arguments: + fun -- a function with no arguments + key -- a string: key (e.g. "a") or key-symbol (e.g. "space") + + In order to be able to register key-events, TurtleScreen + must have focus. (See method listen.) + + Example (for a TurtleScreen instance named screen): + + >>> def f(): + ... fd(50) + ... lt(60) + ... + >>> screen.onkey(f, "Up") + >>> screen.listen() + + Subsequently the turtle can be moved by repeatedly pressing + the up-arrow key, consequently drawing a hexagon + + """ + if fun is None: + if key in self._keys: + self._keys.remove(key) + elif key not in self._keys: + self._keys.append(key) + self._onkeyrelease(fun, key) + + def onkeypress(self, fun, key=None): + """Bind fun to key-press event of key if key is given, + or to any key-press-event if no key is given. + + Arguments: + fun -- a function with no arguments + key -- a string: key (e.g. "a") or key-symbol (e.g. "space") + + In order to be able to register key-events, TurtleScreen + must have focus. (See method listen.) + + Example (for a TurtleScreen instance named screen + and a Turtle instance named turtle): + + >>> def f(): + ... fd(50) + ... lt(60) + ... + >>> screen.onkeypress(f, "Up") + >>> screen.listen() + + Subsequently the turtle can be moved by repeatedly pressing + the up-arrow key, or by keeping pressed the up-arrow key. + consequently drawing a hexagon. + """ + if fun is None: + if key in self._keys: + self._keys.remove(key) + elif key is not None and key not in self._keys: + self._keys.append(key) + self._onkeypress(fun, key) + + def listen(self, xdummy=None, ydummy=None): + """Set focus on TurtleScreen (in order to collect key-events) + + No arguments. + Dummy arguments are provided in order + to be able to pass listen to the onclick method. + + Example (for a TurtleScreen instance named screen): + >>> screen.listen() + """ + self._listen() + + def ontimer(self, fun, t=0): + """Install a timer, which calls fun after t milliseconds. + + Arguments: + fun -- a function with no arguments. + t -- a number >= 0 + + Example (for a TurtleScreen instance named screen): + + >>> running = True + >>> def f(): + ... if running: + ... fd(50) + ... lt(60) + ... screen.ontimer(f, 250) + ... + >>> f() # makes the turtle marching around + >>> running = False + """ + self._ontimer(fun, t) + + def bgpic(self, picname=None): + """Set background image or return name of current backgroundimage. + + Optional argument: + picname -- a string, name of a gif-file or "nopic". + + If picname is a filename, set the corresponding image as background. + If picname is "nopic", delete backgroundimage, if present. + If picname is None, return the filename of the current backgroundimage. + + Example (for a TurtleScreen instance named screen): + >>> screen.bgpic() + 'nopic' + >>> screen.bgpic("landscape.gif") + >>> screen.bgpic() + 'landscape.gif' + """ + if picname is None: + return self._bgpicname + if picname not in self._bgpics: + self._bgpics[picname] = self._image(picname) + self._setbgpic(self._bgpic, self._bgpics[picname]) + self._bgpicname = picname + + def screensize(self, canvwidth=None, canvheight=None, bg=None): + """Resize the canvas the turtles are drawing on. + + Optional arguments: + canvwidth -- positive integer, new width of canvas in pixels + canvheight -- positive integer, new height of canvas in pixels + bg -- colorstring or color-tuple, new backgroundcolor + If no arguments are given, return current (canvaswidth, canvasheight) + + Do not alter the drawing window. To observe hidden parts of + the canvas use the scrollbars. (Can make visible those parts + of a drawing, which were outside the canvas before!) + + Example (for a Turtle instance named turtle): + >>> turtle.screensize(2000,1500) + >>> # e.g. to search for an erroneously escaped turtle ;-) + """ + return self._resize(canvwidth, canvheight, bg) + + onscreenclick = onclick + resetscreen = reset + clearscreen = clear + addshape = register_shape + onkeyrelease = onkey + +class TNavigator(object): + """Navigation part of the RawTurtle. + Implements methods for turtle movement. + """ + START_ORIENTATION = { + "standard": Vec2D(1.0, 0.0), + "world" : Vec2D(1.0, 0.0), + "logo" : Vec2D(0.0, 1.0) } + DEFAULT_MODE = "standard" + DEFAULT_ANGLEOFFSET = 0 + DEFAULT_ANGLEORIENT = 1 + + def __init__(self, mode=DEFAULT_MODE): + self._angleOffset = self.DEFAULT_ANGLEOFFSET + self._angleOrient = self.DEFAULT_ANGLEORIENT + self._mode = mode + self.undobuffer = None + self.degrees() + self._mode = None + self._setmode(mode) + TNavigator.reset(self) + + def reset(self): + """reset turtle to its initial values + + Will be overwritten by parent class + """ + self._position = Vec2D(0.0, 0.0) + self._orient = TNavigator.START_ORIENTATION[self._mode] + + def _setmode(self, mode=None): + """Set turtle-mode to 'standard', 'world' or 'logo'. + """ + if mode is None: + return self._mode + if mode not in ["standard", "logo", "world"]: + return + self._mode = mode + if mode in ["standard", "world"]: + self._angleOffset = 0 + self._angleOrient = 1 + else: # mode == "logo": + self._angleOffset = self._fullcircle/4. + self._angleOrient = -1 + + def _setDegreesPerAU(self, fullcircle): + """Helper function for degrees() and radians()""" + self._fullcircle = fullcircle + self._degreesPerAU = 360/fullcircle + if self._mode == "standard": + self._angleOffset = 0 + else: + self._angleOffset = fullcircle/4. + + def degrees(self, fullcircle=360.0): + """ Set angle measurement units to degrees. + + Optional argument: + fullcircle - a number + + Set angle measurement units, i. e. set number + of 'degrees' for a full circle. Default value is + 360 degrees. + + Example (for a Turtle instance named turtle): + >>> turtle.left(90) + >>> turtle.heading() + 90 + + Change angle measurement unit to grad (also known as gon, + grade, or gradian and equals 1/100-th of the right angle.) + >>> turtle.degrees(400.0) + >>> turtle.heading() + 100 + + """ + self._setDegreesPerAU(fullcircle) + + def radians(self): + """ Set the angle measurement units to radians. + + No arguments. + + Example (for a Turtle instance named turtle): + >>> turtle.heading() + 90 + >>> turtle.radians() + >>> turtle.heading() + 1.5707963267948966 + """ + self._setDegreesPerAU(2*math.pi) + + def _go(self, distance): + """move turtle forward by specified distance""" + ende = self._position + self._orient * distance + self._goto(ende) + + def _rotate(self, angle): + """Turn turtle counterclockwise by specified angle if angle > 0.""" + angle *= self._degreesPerAU + self._orient = self._orient.rotate(angle) + + def _goto(self, end): + """move turtle to position end.""" + self._position = end + + def forward(self, distance): + """Move the turtle forward by the specified distance. + + Aliases: forward | fd + + Argument: + distance -- a number (integer or float) + + Move the turtle forward by the specified distance, in the direction + the turtle is headed. + + Example (for a Turtle instance named turtle): + >>> turtle.position() + (0.00, 0.00) + >>> turtle.forward(25) + >>> turtle.position() + (25.00,0.00) + >>> turtle.forward(-75) + >>> turtle.position() + (-50.00,0.00) + """ + self._go(distance) + + def back(self, distance): + """Move the turtle backward by distance. + + Aliases: back | backward | bk + + Argument: + distance -- a number + + Move the turtle backward by distance, opposite to the direction the + turtle is headed. Do not change the turtle's heading. + + Example (for a Turtle instance named turtle): + >>> turtle.position() + (0.00, 0.00) + >>> turtle.backward(30) + >>> turtle.position() + (-30.00, 0.00) + """ + self._go(-distance) + + def right(self, angle): + """Turn turtle right by angle units. + + Aliases: right | rt + + Argument: + angle -- a number (integer or float) + + Turn turtle right by angle units. (Units are by default degrees, + but can be set via the degrees() and radians() functions.) + Angle orientation depends on mode. (See this.) + + Example (for a Turtle instance named turtle): + >>> turtle.heading() + 22.0 + >>> turtle.right(45) + >>> turtle.heading() + 337.0 + """ + self._rotate(-angle) + + def left(self, angle): + """Turn turtle left by angle units. + + Aliases: left | lt + + Argument: + angle -- a number (integer or float) + + Turn turtle left by angle units. (Units are by default degrees, + but can be set via the degrees() and radians() functions.) + Angle orientation depends on mode. (See this.) + + Example (for a Turtle instance named turtle): + >>> turtle.heading() + 22.0 + >>> turtle.left(45) + >>> turtle.heading() + 67.0 + """ + self._rotate(angle) + + def pos(self): + """Return the turtle's current location (x,y), as a Vec2D-vector. + + Aliases: pos | position + + No arguments. + + Example (for a Turtle instance named turtle): + >>> turtle.pos() + (0.00, 240.00) + """ + return self._position + + def xcor(self): + """ Return the turtle's x coordinate. + + No arguments. + + Example (for a Turtle instance named turtle): + >>> reset() + >>> turtle.left(60) + >>> turtle.forward(100) + >>> print turtle.xcor() + 50.0 + """ + return self._position[0] + + def ycor(self): + """ Return the turtle's y coordinate + --- + No arguments. + + Example (for a Turtle instance named turtle): + >>> reset() + >>> turtle.left(60) + >>> turtle.forward(100) + >>> print turtle.ycor() + 86.6025403784 + """ + return self._position[1] + + + def goto(self, x, y=None): + """Move turtle to an absolute position. + + Aliases: setpos | setposition | goto: + + Arguments: + x -- a number or a pair/vector of numbers + y -- a number None + + call: goto(x, y) # two coordinates + --or: goto((x, y)) # a pair (tuple) of coordinates + --or: goto(vec) # e.g. as returned by pos() + + Move turtle to an absolute position. If the pen is down, + a line will be drawn. The turtle's orientation does not change. + + Example (for a Turtle instance named turtle): + >>> tp = turtle.pos() + >>> tp + (0.00, 0.00) + >>> turtle.setpos(60,30) + >>> turtle.pos() + (60.00,30.00) + >>> turtle.setpos((20,80)) + >>> turtle.pos() + (20.00,80.00) + >>> turtle.setpos(tp) + >>> turtle.pos() + (0.00,0.00) + """ + if y is None: + self._goto(Vec2D(*x)) + else: + self._goto(Vec2D(x, y)) + + def home(self): + """Move turtle to the origin - coordinates (0,0). + + No arguments. + + Move turtle to the origin - coordinates (0,0) and set its + heading to its start-orientation (which depends on mode). + + Example (for a Turtle instance named turtle): + >>> turtle.home() + """ + self.goto(0, 0) + self.setheading(0) + + def setx(self, x): + """Set the turtle's first coordinate to x + + Argument: + x -- a number (integer or float) + + Set the turtle's first coordinate to x, leave second coordinate + unchanged. + + Example (for a Turtle instance named turtle): + >>> turtle.position() + (0.00, 240.00) + >>> turtle.setx(10) + >>> turtle.position() + (10.00, 240.00) + """ + self._goto(Vec2D(x, self._position[1])) + + def sety(self, y): + """Set the turtle's second coordinate to y + + Argument: + y -- a number (integer or float) + + Set the turtle's first coordinate to x, second coordinate remains + unchanged. + + Example (for a Turtle instance named turtle): + >>> turtle.position() + (0.00, 40.00) + >>> turtle.sety(-10) + >>> turtle.position() + (0.00, -10.00) + """ + self._goto(Vec2D(self._position[0], y)) + + def distance(self, x, y=None): + """Return the distance from the turtle to (x,y) in turtle step units. + + Arguments: + x -- a number or a pair/vector of numbers or a turtle instance + y -- a number None None + + call: distance(x, y) # two coordinates + --or: distance((x, y)) # a pair (tuple) of coordinates + --or: distance(vec) # e.g. as returned by pos() + --or: distance(mypen) # where mypen is another turtle + + Example (for a Turtle instance named turtle): + >>> turtle.pos() + (0.00, 0.00) + >>> turtle.distance(30,40) + 50.0 + >>> pen = Turtle() + >>> pen.forward(77) + >>> turtle.distance(pen) + 77.0 + """ + if y is not None: + pos = Vec2D(x, y) + if isinstance(x, Vec2D): + pos = x + elif isinstance(x, tuple): + pos = Vec2D(*x) + elif isinstance(x, TNavigator): + pos = x._position + return abs(pos - self._position) + + def towards(self, x, y=None): + """Return the angle of the line from the turtle's position to (x, y). + + Arguments: + x -- a number or a pair/vector of numbers or a turtle instance + y -- a number None None + + call: distance(x, y) # two coordinates + --or: distance((x, y)) # a pair (tuple) of coordinates + --or: distance(vec) # e.g. as returned by pos() + --or: distance(mypen) # where mypen is another turtle + + Return the angle, between the line from turtle-position to position + specified by x, y and the turtle's start orientation. (Depends on + modes - "standard" or "logo") + + Example (for a Turtle instance named turtle): + >>> turtle.pos() + (10.00, 10.00) + >>> turtle.towards(0,0) + 225.0 + """ + if y is not None: + pos = Vec2D(x, y) + if isinstance(x, Vec2D): + pos = x + elif isinstance(x, tuple): + pos = Vec2D(*x) + elif isinstance(x, TNavigator): + pos = x._position + x, y = pos - self._position + result = round(math.atan2(y, x)*180.0/math.pi, 10) % 360.0 + result /= self._degreesPerAU + return (self._angleOffset + self._angleOrient*result) % self._fullcircle + + def heading(self): + """ Return the turtle's current heading. + + No arguments. + + Example (for a Turtle instance named turtle): + >>> turtle.left(67) + >>> turtle.heading() + 67.0 + """ + x, y = self._orient + result = round(math.atan2(y, x)*180.0/math.pi, 10) % 360.0 + result /= self._degreesPerAU + return (self._angleOffset + self._angleOrient*result) % self._fullcircle + + def setheading(self, to_angle): + """Set the orientation of the turtle to to_angle. + + Aliases: setheading | seth + + Argument: + to_angle -- a number (integer or float) + + Set the orientation of the turtle to to_angle. + Here are some common directions in degrees: + + standard - mode: logo-mode: + -------------------|-------------------- + 0 - east 0 - north + 90 - north 90 - east + 180 - west 180 - south + 270 - south 270 - west + + Example (for a Turtle instance named turtle): + >>> turtle.setheading(90) + >>> turtle.heading() + 90 + """ + angle = (to_angle - self.heading())*self._angleOrient + full = self._fullcircle + angle = (angle+full/2.)%full - full/2. + self._rotate(angle) + + def circle(self, radius, extent = None, steps = None): + """ Draw a circle with given radius. + + Arguments: + radius -- a number + extent (optional) -- a number + steps (optional) -- an integer + + Draw a circle with given radius. The center is radius units left + of the turtle; extent - an angle - determines which part of the + circle is drawn. If extent is not given, draw the entire circle. + If extent is not a full circle, one endpoint of the arc is the + current pen position. Draw the arc in counterclockwise direction + if radius is positive, otherwise in clockwise direction. Finally + the direction of the turtle is changed by the amount of extent. + + As the circle is approximated by an inscribed regular polygon, + steps determines the number of steps to use. If not given, + it will be calculated automatically. Maybe used to draw regular + polygons. + + call: circle(radius) # full circle + --or: circle(radius, extent) # arc + --or: circle(radius, extent, steps) + --or: circle(radius, steps=6) # 6-sided polygon + + Example (for a Turtle instance named turtle): + >>> turtle.circle(50) + >>> turtle.circle(120, 180) # semicircle + """ + if self.undobuffer: + self.undobuffer.push(["seq"]) + self.undobuffer.cumulate = True + speed = self.speed() + if extent is None: + extent = self._fullcircle + if steps is None: + frac = abs(extent)/self._fullcircle + steps = 1+int(min(11+abs(radius)/6.0, 59.0)*frac) + w = 1.0 * extent / steps + w2 = 0.5 * w + l = 2.0 * radius * math.sin(w2*math.pi/180.0*self._degreesPerAU) + if radius < 0: + l, w, w2 = -l, -w, -w2 + tr = self._tracer() + dl = self._delay() + if speed == 0: + self._tracer(0, 0) + else: + self.speed(0) + self._rotate(w2) + for i in range(steps): + self.speed(speed) + self._go(l) + self.speed(0) + self._rotate(w) + self._rotate(-w2) + if speed == 0: + self._tracer(tr, dl) + self.speed(speed) + if self.undobuffer: + self.undobuffer.cumulate = False + +## three dummy methods to be implemented by child class: + + def speed(self, s=0): + """dummy method - to be overwritten by child class""" + def _tracer(self, a=None, b=None): + """dummy method - to be overwritten by child class""" + def _delay(self, n=None): + """dummy method - to be overwritten by child class""" + + fd = forward + bk = back + backward = back + rt = right + lt = left + position = pos + setpos = goto + setposition = goto + seth = setheading + + +class TPen(object): + """Drawing part of the RawTurtle. + Implements drawing properties. + """ + def __init__(self, resizemode=_CFG["resizemode"]): + self._resizemode = resizemode # or "user" or "noresize" + self.undobuffer = None + TPen._reset(self) + + def _reset(self, pencolor=_CFG["pencolor"], + fillcolor=_CFG["fillcolor"]): + self._pensize = 1 + self._shown = True + self._pencolor = pencolor + self._fillcolor = fillcolor + self._drawing = True + self._speed = 3 + self._stretchfactor = (1., 1.) + self._shearfactor = 0. + self._tilt = 0. + self._shapetrafo = (1., 0., 0., 1.) + self._outlinewidth = 1 + + def resizemode(self, rmode=None): + """Set resizemode to one of the values: "auto", "user", "noresize". + + (Optional) Argument: + rmode -- one of the strings "auto", "user", "noresize" + + Different resizemodes have the following effects: + - "auto" adapts the appearance of the turtle + corresponding to the value of pensize. + - "user" adapts the appearance of the turtle according to the + values of stretchfactor and outlinewidth (outline), + which are set by shapesize() + - "noresize" no adaption of the turtle's appearance takes place. + If no argument is given, return current resizemode. + resizemode("user") is called by a call of shapesize with arguments. + + + Examples (for a Turtle instance named turtle): + >>> turtle.resizemode("noresize") + >>> turtle.resizemode() + 'noresize' + """ + if rmode is None: + return self._resizemode + rmode = rmode.lower() + if rmode in ["auto", "user", "noresize"]: + self.pen(resizemode=rmode) + + def pensize(self, width=None): + """Set or return the line thickness. + + Aliases: pensize | width + + Argument: + width -- positive number + + Set the line thickness to width or return it. If resizemode is set + to "auto" and turtleshape is a polygon, that polygon is drawn with + the same line thickness. If no argument is given, current pensize + is returned. + + Example (for a Turtle instance named turtle): + >>> turtle.pensize() + 1 + >>> turtle.pensize(10) # from here on lines of width 10 are drawn + """ + if width is None: + return self._pensize + self.pen(pensize=width) + + + def penup(self): + """Pull the pen up -- no drawing when moving. + + Aliases: penup | pu | up + + No argument + + Example (for a Turtle instance named turtle): + >>> turtle.penup() + """ + if not self._drawing: + return + self.pen(pendown=False) + + def pendown(self): + """Pull the pen down -- drawing when moving. + + Aliases: pendown | pd | down + + No argument. + + Example (for a Turtle instance named turtle): + >>> turtle.pendown() + """ + if self._drawing: + return + self.pen(pendown=True) + + def isdown(self): + """Return True if pen is down, False if it's up. + + No argument. + + Example (for a Turtle instance named turtle): + >>> turtle.penup() + >>> turtle.isdown() + False + >>> turtle.pendown() + >>> turtle.isdown() + True + """ + return self._drawing + + def speed(self, speed=None): + """ Return or set the turtle's speed. + + Optional argument: + speed -- an integer in the range 0..10 or a speedstring (see below) + + Set the turtle's speed to an integer value in the range 0 .. 10. + If no argument is given: return current speed. + + If input is a number greater than 10 or smaller than 0.5, + speed is set to 0. + Speedstrings are mapped to speedvalues in the following way: + 'fastest' : 0 + 'fast' : 10 + 'normal' : 6 + 'slow' : 3 + 'slowest' : 1 + speeds from 1 to 10 enforce increasingly faster animation of + line drawing and turtle turning. + + Attention: + speed = 0 : *no* animation takes place. forward/back makes turtle jump + and likewise left/right make the turtle turn instantly. + + Example (for a Turtle instance named turtle): + >>> turtle.speed(3) + """ + speeds = {'fastest':0, 'fast':10, 'normal':6, 'slow':3, 'slowest':1 } + if speed is None: + return self._speed + if speed in speeds: + speed = speeds[speed] + elif 0.5 < speed < 10.5: + speed = int(round(speed)) + else: + speed = 0 + self.pen(speed=speed) + + def color(self, *args): + """Return or set the pencolor and fillcolor. + + Arguments: + Several input formats are allowed. + They use 0, 1, 2, or 3 arguments as follows: + + color() + Return the current pencolor and the current fillcolor + as a pair of color specification strings as are returned + by pencolor and fillcolor. + color(colorstring), color((r,g,b)), color(r,g,b) + inputs as in pencolor, set both, fillcolor and pencolor, + to the given value. + color(colorstring1, colorstring2), + color((r1,g1,b1), (r2,g2,b2)) + equivalent to pencolor(colorstring1) and fillcolor(colorstring2) + and analogously, if the other input format is used. + + If turtleshape is a polygon, outline and interior of that polygon + is drawn with the newly set colors. + For more info see: pencolor, fillcolor + + Example (for a Turtle instance named turtle): + >>> turtle.color('red', 'green') + >>> turtle.color() + ('red', 'green') + >>> colormode(255) + >>> color((40, 80, 120), (160, 200, 240)) + >>> color() + ('#285078', '#a0c8f0') + """ + if args: + l = len(args) + if l == 1: + pcolor = fcolor = args[0] + elif l == 2: + pcolor, fcolor = args + elif l == 3: + pcolor = fcolor = args + pcolor = self._colorstr(pcolor) + fcolor = self._colorstr(fcolor) + self.pen(pencolor=pcolor, fillcolor=fcolor) + else: + return self._color(self._pencolor), self._color(self._fillcolor) + + def pencolor(self, *args): + """ Return or set the pencolor. + + Arguments: + Four input formats are allowed: + - pencolor() + Return the current pencolor as color specification string, + possibly in hex-number format (see example). + May be used as input to another color/pencolor/fillcolor call. + - pencolor(colorstring) + s is a Tk color specification string, such as "red" or "yellow" + - pencolor((r, g, b)) + *a tuple* of r, g, and b, which represent, an RGB color, + and each of r, g, and b are in the range 0..colormode, + where colormode is either 1.0 or 255 + - pencolor(r, g, b) + r, g, and b represent an RGB color, and each of r, g, and b + are in the range 0..colormode + + If turtleshape is a polygon, the outline of that polygon is drawn + with the newly set pencolor. + + Example (for a Turtle instance named turtle): + >>> turtle.pencolor('brown') + >>> tup = (0.2, 0.8, 0.55) + >>> turtle.pencolor(tup) + >>> turtle.pencolor() + '#33cc8c' + """ + if args: + color = self._colorstr(args) + if color == self._pencolor: + return + self.pen(pencolor=color) + else: + return self._color(self._pencolor) + + def fillcolor(self, *args): + """ Return or set the fillcolor. + + Arguments: + Four input formats are allowed: + - fillcolor() + Return the current fillcolor as color specification string, + possibly in hex-number format (see example). + May be used as input to another color/pencolor/fillcolor call. + - fillcolor(colorstring) + s is a Tk color specification string, such as "red" or "yellow" + - fillcolor((r, g, b)) + *a tuple* of r, g, and b, which represent, an RGB color, + and each of r, g, and b are in the range 0..colormode, + where colormode is either 1.0 or 255 + - fillcolor(r, g, b) + r, g, and b represent an RGB color, and each of r, g, and b + are in the range 0..colormode + + If turtleshape is a polygon, the interior of that polygon is drawn + with the newly set fillcolor. + + Example (for a Turtle instance named turtle): + >>> turtle.fillcolor('violet') + >>> col = turtle.pencolor() + >>> turtle.fillcolor(col) + >>> turtle.fillcolor(0, .5, 0) + """ + if args: + color = self._colorstr(args) + if color == self._fillcolor: + return + self.pen(fillcolor=color) + else: + return self._color(self._fillcolor) + + def showturtle(self): + """Makes the turtle visible. + + Aliases: showturtle | st + + No argument. + + Example (for a Turtle instance named turtle): + >>> turtle.hideturtle() + >>> turtle.showturtle() + """ + self.pen(shown=True) + + def hideturtle(self): + """Makes the turtle invisible. + + Aliases: hideturtle | ht + + No argument. + + It's a good idea to do this while you're in the + middle of a complicated drawing, because hiding + the turtle speeds up the drawing observably. + + Example (for a Turtle instance named turtle): + >>> turtle.hideturtle() + """ + self.pen(shown=False) + + def isvisible(self): + """Return True if the Turtle is shown, False if it's hidden. + + No argument. + + Example (for a Turtle instance named turtle): + >>> turtle.hideturtle() + >>> print turtle.isvisible(): + False + """ + return self._shown + + def pen(self, pen=None, **pendict): + """Return or set the pen's attributes. + + Arguments: + pen -- a dictionary with some or all of the below listed keys. + **pendict -- one or more keyword-arguments with the below + listed keys as keywords. + + Return or set the pen's attributes in a 'pen-dictionary' + with the following key/value pairs: + "shown" : True/False + "pendown" : True/False + "pencolor" : color-string or color-tuple + "fillcolor" : color-string or color-tuple + "pensize" : positive number + "speed" : number in range 0..10 + "resizemode" : "auto" or "user" or "noresize" + "stretchfactor": (positive number, positive number) + "shearfactor": number + "outline" : positive number + "tilt" : number + + This dictionary can be used as argument for a subsequent + pen()-call to restore the former pen-state. Moreover one + or more of these attributes can be provided as keyword-arguments. + This can be used to set several pen attributes in one statement. + + + Examples (for a Turtle instance named turtle): + >>> turtle.pen(fillcolor="black", pencolor="red", pensize=10) + >>> turtle.pen() + {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1, + 'pencolor': 'red', 'pendown': True, 'fillcolor': 'black', + 'stretchfactor': (1,1), 'speed': 3, 'shearfactor': 0.0} + >>> penstate=turtle.pen() + >>> turtle.color("yellow","") + >>> turtle.penup() + >>> turtle.pen() + {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1, + 'pencolor': 'yellow', 'pendown': False, 'fillcolor': '', + 'stretchfactor': (1,1), 'speed': 3, 'shearfactor': 0.0} + >>> p.pen(penstate, fillcolor="green") + >>> p.pen() + {'pensize': 10, 'shown': True, 'resizemode': 'auto', 'outline': 1, + 'pencolor': 'red', 'pendown': True, 'fillcolor': 'green', + 'stretchfactor': (1,1), 'speed': 3, 'shearfactor': 0.0} + """ + _pd = {"shown" : self._shown, + "pendown" : self._drawing, + "pencolor" : self._pencolor, + "fillcolor" : self._fillcolor, + "pensize" : self._pensize, + "speed" : self._speed, + "resizemode" : self._resizemode, + "stretchfactor" : self._stretchfactor, + "shearfactor" : self._shearfactor, + "outline" : self._outlinewidth, + "tilt" : self._tilt + } + + if not (pen or pendict): + return _pd + + if isinstance(pen, dict): + p = pen + else: + p = {} + p.update(pendict) + + _p_buf = {} + for key in p: + _p_buf[key] = _pd[key] + + if self.undobuffer: + self.undobuffer.push(("pen", _p_buf)) + + newLine = False + if "pendown" in p: + if self._drawing != p["pendown"]: + newLine = True + if "pencolor" in p: + if isinstance(p["pencolor"], tuple): + p["pencolor"] = self._colorstr((p["pencolor"],)) + if self._pencolor != p["pencolor"]: + newLine = True + if "pensize" in p: + if self._pensize != p["pensize"]: + newLine = True + if newLine: + self._newLine() + if "pendown" in p: + self._drawing = p["pendown"] + if "pencolor" in p: + self._pencolor = p["pencolor"] + if "pensize" in p: + self._pensize = p["pensize"] + if "fillcolor" in p: + if isinstance(p["fillcolor"], tuple): + p["fillcolor"] = self._colorstr((p["fillcolor"],)) + self._fillcolor = p["fillcolor"] + if "speed" in p: + self._speed = p["speed"] + if "resizemode" in p: + self._resizemode = p["resizemode"] + if "stretchfactor" in p: + sf = p["stretchfactor"] + if isinstance(sf, (int, float)): + sf = (sf, sf) + self._stretchfactor = sf + if "shearfactor" in p: + self._shearfactor = p["shearfactor"] + if "outline" in p: + self._outlinewidth = p["outline"] + if "shown" in p: + self._shown = p["shown"] + if "tilt" in p: + self._tilt = p["tilt"] + if "stretchfactor" in p or "tilt" in p or "shearfactor" in p: + scx, scy = self._stretchfactor + shf = self._shearfactor + sa, ca = math.sin(self._tilt), math.cos(self._tilt) + self._shapetrafo = ( scx*ca, scy*(shf*ca + sa), + -scx*sa, scy*(ca - shf*sa)) + self._update() + +## three dummy methods to be implemented by child class: + + def _newLine(self, usePos = True): + """dummy method - to be overwritten by child class""" + def _update(self, count=True, forced=False): + """dummy method - to be overwritten by child class""" + def _color(self, args): + """dummy method - to be overwritten by child class""" + def _colorstr(self, args): + """dummy method - to be overwritten by child class""" + + width = pensize + up = penup + pu = penup + pd = pendown + down = pendown + st = showturtle + ht = hideturtle + + +class _TurtleImage(object): + """Helper class: Datatype to store Turtle attributes + """ + + def __init__(self, screen, shapeIndex): + self.screen = screen + self._type = None + self._setshape(shapeIndex) + + def _setshape(self, shapeIndex): + screen = self.screen + self.shapeIndex = shapeIndex + if self._type == "polygon" == screen._shapes[shapeIndex]._type: + return + if self._type == "image" == screen._shapes[shapeIndex]._type: + return + if self._type in ["image", "polygon"]: + screen._delete(self._item) + elif self._type == "compound": + for item in self._item: + screen._delete(item) + self._type = screen._shapes[shapeIndex]._type + if self._type == "polygon": + self._item = screen._createpoly() + elif self._type == "image": + self._item = screen._createimage(screen._shapes["blank"]._data) + elif self._type == "compound": + self._item = [screen._createpoly() for item in + screen._shapes[shapeIndex]._data] + + +class RawTurtle(TPen, TNavigator): + """Animation part of the RawTurtle. + Puts RawTurtle upon a TurtleScreen and provides tools for + its animation. + """ + screens = [] + + def __init__(self, canvas=None, + shape=_CFG["shape"], + undobuffersize=_CFG["undobuffersize"], + visible=_CFG["visible"]): + if isinstance(canvas, _Screen): + self.screen = canvas + elif isinstance(canvas, TurtleScreen): + if canvas not in RawTurtle.screens: + RawTurtle.screens.append(canvas) + self.screen = canvas + elif isinstance(canvas, (ScrolledCanvas, Canvas)): + for screen in RawTurtle.screens: + if screen.cv == canvas: + self.screen = screen + break + else: + self.screen = TurtleScreen(canvas) + RawTurtle.screens.append(self.screen) + else: + raise TurtleGraphicsError("bad canvas argument %s" % canvas) + + screen = self.screen + TNavigator.__init__(self, screen.mode()) + TPen.__init__(self) + screen._turtles.append(self) + self.drawingLineItem = screen._createline() + self.turtle = _TurtleImage(screen, shape) + self._poly = None + self._creatingPoly = False + self._fillitem = self._fillpath = None + self._shown = visible + self._hidden_from_screen = False + self.currentLineItem = screen._createline() + self.currentLine = [self._position] + self.items = [self.currentLineItem] + self.stampItems = [] + self._undobuffersize = undobuffersize + self.undobuffer = Tbuffer(undobuffersize) + self._update() + + def reset(self): + """Delete the turtle's drawings and restore its default values. + + No argument. + + Delete the turtle's drawings from the screen, re-center the turtle + and set variables to the default values. + + Example (for a Turtle instance named turtle): + >>> turtle.position() + (0.00,-22.00) + >>> turtle.heading() + 100.0 + >>> turtle.reset() + >>> turtle.position() + (0.00,0.00) + >>> turtle.heading() + 0.0 + """ + TNavigator.reset(self) + TPen._reset(self) + self._clear() + self._drawturtle() + self._update() + + def setundobuffer(self, size): + """Set or disable undobuffer. + + Argument: + size -- an integer or None + + If size is an integer an empty undobuffer of given size is installed. + Size gives the maximum number of turtle-actions that can be undone + by the undo() function. + If size is None, no undobuffer is present. + + Example (for a Turtle instance named turtle): + >>> turtle.setundobuffer(42) + """ + if size is None or size <= 0: + self.undobuffer = None + else: + self.undobuffer = Tbuffer(size) + + def undobufferentries(self): + """Return count of entries in the undobuffer. + + No argument. + + Example (for a Turtle instance named turtle): + >>> while undobufferentries(): + ... undo() + """ + if self.undobuffer is None: + return 0 + return self.undobuffer.nr_of_items() + + def _clear(self): + """Delete all of pen's drawings""" + self._fillitem = self._fillpath = None + for item in self.items: + self.screen._delete(item) + self.currentLineItem = self.screen._createline() + self.currentLine = [] + if self._drawing: + self.currentLine.append(self._position) + self.items = [self.currentLineItem] + self.clearstamps() + self.setundobuffer(self._undobuffersize) + + + def clear(self): + """Delete the turtle's drawings from the screen. Do not move turtle. + + No arguments. + + Delete the turtle's drawings from the screen. Do not move turtle. + State and position of the turtle as well as drawings of other + turtles are not affected. + + Examples (for a Turtle instance named turtle): + >>> turtle.clear() + """ + self._clear() + self._update() + + def _update_data(self): + self.screen._incrementudc() + if self.screen._updatecounter != 0: + return + if len(self.currentLine)>1: + self.screen._drawline(self.currentLineItem, self.currentLine, + self._pencolor, self._pensize) + + def _update(self): + """Perform a Turtle-data update. + """ + screen = self.screen + if screen._tracing == 0: + return + elif screen._tracing == 1: + self._update_data() + self._drawturtle() + screen._update() # TurtleScreenBase + screen._delay(screen._delayvalue) # TurtleScreenBase + else: + self._update_data() + if screen._updatecounter == 0: + for t in screen.turtles(): + t._drawturtle() + screen._update() + + def _tracer(self, flag=None, delay=None): + """Turns turtle animation on/off and set delay for update drawings. + + Optional arguments: + n -- nonnegative integer + delay -- nonnegative integer + + If n is given, only each n-th regular screen update is really performed. + (Can be used to accelerate the drawing of complex graphics.) + Second arguments sets delay value (see RawTurtle.delay()) + + Example (for a Turtle instance named turtle): + >>> turtle.tracer(8, 25) + >>> dist = 2 + >>> for i in range(200): + ... turtle.fd(dist) + ... turtle.rt(90) + ... dist += 2 + """ + return self.screen.tracer(flag, delay) + + def _color(self, args): + return self.screen._color(args) + + def _colorstr(self, args): + return self.screen._colorstr(args) + + def _cc(self, args): + """Convert colortriples to hexstrings. + """ + if isinstance(args, str): + return args + try: + r, g, b = args + except (TypeError, ValueError): + raise TurtleGraphicsError("bad color arguments: %s" % str(args)) + if self.screen._colormode == 1.0: + r, g, b = [round(255.0*x) for x in (r, g, b)] + if not ((0 <= r <= 255) and (0 <= g <= 255) and (0 <= b <= 255)): + raise TurtleGraphicsError("bad color sequence: %s" % str(args)) + return "#%02x%02x%02x" % (r, g, b) + + def clone(self): + """Create and return a clone of the turtle. + + No argument. + + Create and return a clone of the turtle with same position, heading + and turtle properties. + + Example (for a Turtle instance named mick): + mick = Turtle() + joe = mick.clone() + """ + screen = self.screen + self._newLine(self._drawing) + + turtle = self.turtle + self.screen = None + self.turtle = None # too make self deepcopy-able + + q = deepcopy(self) + + self.screen = screen + self.turtle = turtle + + q.screen = screen + q.turtle = _TurtleImage(screen, self.turtle.shapeIndex) + + screen._turtles.append(q) + ttype = screen._shapes[self.turtle.shapeIndex]._type + if ttype == "polygon": + q.turtle._item = screen._createpoly() + elif ttype == "image": + q.turtle._item = screen._createimage(screen._shapes["blank"]._data) + elif ttype == "compound": + q.turtle._item = [screen._createpoly() for item in + screen._shapes[self.turtle.shapeIndex]._data] + q.currentLineItem = screen._createline() + q._update() + return q + + def shape(self, name=None): + """Set turtle shape to shape with given name / return current shapename. + + Optional argument: + name -- a string, which is a valid shapename + + Set turtle shape to shape with given name or, if name is not given, + return name of current shape. + Shape with name must exist in the TurtleScreen's shape dictionary. + Initially there are the following polygon shapes: + 'arrow', 'turtle', 'circle', 'square', 'triangle', 'classic'. + To learn about how to deal with shapes see Screen-method register_shape. + + Example (for a Turtle instance named turtle): + >>> turtle.shape() + 'arrow' + >>> turtle.shape("turtle") + >>> turtle.shape() + 'turtle' + """ + if name is None: + return self.turtle.shapeIndex + if not name in self.screen.getshapes(): + raise TurtleGraphicsError("There is no shape named %s" % name) + self.turtle._setshape(name) + self._update() + + def shapesize(self, stretch_wid=None, stretch_len=None, outline=None): + """Set/return turtle's stretchfactors/outline. Set resizemode to "user". + + Optional arguments: + stretch_wid : positive number + stretch_len : positive number + outline : positive number + + Return or set the pen's attributes x/y-stretchfactors and/or outline. + Set resizemode to "user". + If and only if resizemode is set to "user", the turtle will be displayed + stretched according to its stretchfactors: + stretch_wid is stretchfactor perpendicular to orientation + stretch_len is stretchfactor in direction of turtles orientation. + outline determines the width of the shapes's outline. + + Examples (for a Turtle instance named turtle): + >>> turtle.resizemode("user") + >>> turtle.shapesize(5, 5, 12) + >>> turtle.shapesize(outline=8) + """ + if stretch_wid is stretch_len is outline is None: + stretch_wid, stretch_len = self._stretchfactor + return stretch_wid, stretch_len, self._outlinewidth + if stretch_wid == 0 or stretch_len == 0: + raise TurtleGraphicsError("stretch_wid/stretch_len must not be zero") + if stretch_wid is not None: + if stretch_len is None: + stretchfactor = stretch_wid, stretch_wid + else: + stretchfactor = stretch_wid, stretch_len + elif stretch_len is not None: + stretchfactor = self._stretchfactor[0], stretch_len + else: + stretchfactor = self._stretchfactor + if outline is None: + outline = self._outlinewidth + self.pen(resizemode="user", + stretchfactor=stretchfactor, outline=outline) + + def shearfactor(self, shear=None): + """Set or return the current shearfactor. + + Optional argument: shear -- number, tangent of the shear angle + + Shear the turtleshape according to the given shearfactor shear, + which is the tangent of the shear angle. DO NOT change the + turtle's heading (direction of movement). + If shear is not given: return the current shearfactor, i. e. the + tangent of the shear angle, by which lines parallel to the + heading of the turtle are sheared. + + Examples (for a Turtle instance named turtle): + >>> turtle.shape("circle") + >>> turtle.shapesize(5,2) + >>> turtle.shearfactor(0.5) + >>> turtle.shearfactor() + >>> 0.5 + """ + if shear is None: + return self._shearfactor + self.pen(resizemode="user", shearfactor=shear) + + def settiltangle(self, angle): + """Rotate the turtleshape to point in the specified direction + + Argument: angle -- number + + Rotate the turtleshape to point in the direction specified by angle, + regardless of its current tilt-angle. DO NOT change the turtle's + heading (direction of movement). + + + Examples (for a Turtle instance named turtle): + >>> turtle.shape("circle") + >>> turtle.shapesize(5,2) + >>> turtle.settiltangle(45) + >>> stamp() + >>> turtle.fd(50) + >>> turtle.settiltangle(-45) + >>> stamp() + >>> turtle.fd(50) + """ + tilt = -angle * self._degreesPerAU * self._angleOrient + tilt = (tilt * math.pi / 180.0) % (2*math.pi) + self.pen(resizemode="user", tilt=tilt) + + def tiltangle(self, angle=None): + """Set or return the current tilt-angle. + + Optional argument: angle -- number + + Rotate the turtleshape to point in the direction specified by angle, + regardless of its current tilt-angle. DO NOT change the turtle's + heading (direction of movement). + If angle is not given: return the current tilt-angle, i. e. the angle + between the orientation of the turtleshape and the heading of the + turtle (its direction of movement). + + Deprecated since Python 3.1 + + Examples (for a Turtle instance named turtle): + >>> turtle.shape("circle") + >>> turtle.shapesize(5,2) + >>> turtle.tilt(45) + >>> turtle.tiltangle() + """ + if angle is None: + tilt = -self._tilt * (180.0/math.pi) * self._angleOrient + return (tilt / self._degreesPerAU) % self._fullcircle + else: + self.settiltangle(angle) + + def tilt(self, angle): + """Rotate the turtleshape by angle. + + Argument: + angle - a number + + Rotate the turtleshape by angle from its current tilt-angle, + but do NOT change the turtle's heading (direction of movement). + + Examples (for a Turtle instance named turtle): + >>> turtle.shape("circle") + >>> turtle.shapesize(5,2) + >>> turtle.tilt(30) + >>> turtle.fd(50) + >>> turtle.tilt(30) + >>> turtle.fd(50) + """ + self.settiltangle(angle + self.tiltangle()) + + def shapetransform(self, t11=None, t12=None, t21=None, t22=None): + """Set or return the current transformation matrix of the turtle shape. + + Optional arguments: t11, t12, t21, t22 -- numbers. + + If none of the matrix elements are given, return the transformation + matrix. + Otherwise set the given elements and transform the turtleshape + according to the matrix consisting of first row t11, t12 and + second row t21, 22. + Modify stretchfactor, shearfactor and tiltangle according to the + given matrix. + + Examples (for a Turtle instance named turtle): + >>> turtle.shape("square") + >>> turtle.shapesize(4,2) + >>> turtle.shearfactor(-0.5) + >>> turtle.shapetransform() + (4.0, -1.0, -0.0, 2.0) + """ + if t11 is t12 is t21 is t22 is None: + return self._shapetrafo + m11, m12, m21, m22 = self._shapetrafo + if t11 is not None: m11 = t11 + if t12 is not None: m12 = t12 + if t21 is not None: m21 = t21 + if t22 is not None: m22 = t22 + if t11 * t22 - t12 * t21 == 0: + raise TurtleGraphicsError("Bad shape transform matrix: must not be singular") + self._shapetrafo = (m11, m12, m21, m22) + alfa = math.atan2(-m21, m11) % (2 * math.pi) + sa, ca = math.sin(alfa), math.cos(alfa) + a11, a12, a21, a22 = (ca*m11 - sa*m21, ca*m12 - sa*m22, + sa*m11 + ca*m21, sa*m12 + ca*m22) + self._stretchfactor = a11, a22 + self._shearfactor = a12/a22 + self._tilt = alfa + self.pen(resizemode="user") + + + def _polytrafo(self, poly): + """Computes transformed polygon shapes from a shape + according to current position and heading. + """ + screen = self.screen + p0, p1 = self._position + e0, e1 = self._orient + e = Vec2D(e0, e1 * screen.yscale / screen.xscale) + e0, e1 = (1.0 / abs(e)) * e + return [(p0+(e1*x+e0*y)/screen.xscale, p1+(-e0*x+e1*y)/screen.yscale) + for (x, y) in poly] + + def get_shapepoly(self): + """Return the current shape polygon as tuple of coordinate pairs. + + No argument. + + Examples (for a Turtle instance named turtle): + >>> turtle.shape("square") + >>> turtle.shapetransform(4, -1, 0, 2) + >>> turtle.get_shapepoly() + ((50, -20), (30, 20), (-50, 20), (-30, -20)) + + """ + shape = self.screen._shapes[self.turtle.shapeIndex] + if shape._type == "polygon": + return self._getshapepoly(shape._data, shape._type == "compound") + # else return None + + def _getshapepoly(self, polygon, compound=False): + """Calculate transformed shape polygon according to resizemode + and shapetransform. + """ + if self._resizemode == "user" or compound: + t11, t12, t21, t22 = self._shapetrafo + elif self._resizemode == "auto": + l = max(1, self._pensize/5.0) + t11, t12, t21, t22 = l, 0, 0, l + elif self._resizemode == "noresize": + return polygon + return tuple((t11*x + t12*y, t21*x + t22*y) for (x, y) in polygon) + + def _drawturtle(self): + """Manages the correct rendering of the turtle with respect to + its shape, resizemode, stretch and tilt etc.""" + screen = self.screen + shape = screen._shapes[self.turtle.shapeIndex] + ttype = shape._type + titem = self.turtle._item + if self._shown and screen._updatecounter == 0 and screen._tracing > 0: + self._hidden_from_screen = False + tshape = shape._data + if ttype == "polygon": + if self._resizemode == "noresize": w = 1 + elif self._resizemode == "auto": w = self._pensize + else: w =self._outlinewidth + shape = self._polytrafo(self._getshapepoly(tshape)) + fc, oc = self._fillcolor, self._pencolor + screen._drawpoly(titem, shape, fill=fc, outline=oc, + width=w, top=True) + elif ttype == "image": + screen._drawimage(titem, self._position, tshape) + elif ttype == "compound": + for item, (poly, fc, oc) in zip(titem, tshape): + poly = self._polytrafo(self._getshapepoly(poly, True)) + screen._drawpoly(item, poly, fill=self._cc(fc), + outline=self._cc(oc), width=self._outlinewidth, top=True) + else: + if self._hidden_from_screen: + return + if ttype == "polygon": + screen._drawpoly(titem, ((0, 0), (0, 0), (0, 0)), "", "") + elif ttype == "image": + screen._drawimage(titem, self._position, + screen._shapes["blank"]._data) + elif ttype == "compound": + for item in titem: + screen._drawpoly(item, ((0, 0), (0, 0), (0, 0)), "", "") + self._hidden_from_screen = True + +############################## stamp stuff ############################### + + def stamp(self): + """Stamp a copy of the turtleshape onto the canvas and return its id. + + No argument. + + Stamp a copy of the turtle shape onto the canvas at the current + turtle position. Return a stamp_id for that stamp, which can be + used to delete it by calling clearstamp(stamp_id). + + Example (for a Turtle instance named turtle): + >>> turtle.color("blue") + >>> turtle.stamp() + 13 + >>> turtle.fd(50) + """ + screen = self.screen + shape = screen._shapes[self.turtle.shapeIndex] + ttype = shape._type + tshape = shape._data + if ttype == "polygon": + stitem = screen._createpoly() + if self._resizemode == "noresize": w = 1 + elif self._resizemode == "auto": w = self._pensize + else: w =self._outlinewidth + shape = self._polytrafo(self._getshapepoly(tshape)) + fc, oc = self._fillcolor, self._pencolor + screen._drawpoly(stitem, shape, fill=fc, outline=oc, + width=w, top=True) + elif ttype == "image": + stitem = screen._createimage("") + screen._drawimage(stitem, self._position, tshape) + elif ttype == "compound": + stitem = [] + for element in tshape: + item = screen._createpoly() + stitem.append(item) + stitem = tuple(stitem) + for item, (poly, fc, oc) in zip(stitem, tshape): + poly = self._polytrafo(self._getshapepoly(poly, True)) + screen._drawpoly(item, poly, fill=self._cc(fc), + outline=self._cc(oc), width=self._outlinewidth, top=True) + self.stampItems.append(stitem) + self.undobuffer.push(("stamp", stitem)) + return stitem + + def _clearstamp(self, stampid): + """does the work for clearstamp() and clearstamps() + """ + if stampid in self.stampItems: + if isinstance(stampid, tuple): + for subitem in stampid: + self.screen._delete(subitem) + else: + self.screen._delete(stampid) + self.stampItems.remove(stampid) + # Delete stampitem from undobuffer if necessary + # if clearstamp is called directly. + item = ("stamp", stampid) + buf = self.undobuffer + if item not in buf.buffer: + return + index = buf.buffer.index(item) + buf.buffer.remove(item) + if index <= buf.ptr: + buf.ptr = (buf.ptr - 1) % buf.bufsize + buf.buffer.insert((buf.ptr+1)%buf.bufsize, [None]) + + def clearstamp(self, stampid): + """Delete stamp with given stampid + + Argument: + stampid - an integer, must be return value of previous stamp() call. + + Example (for a Turtle instance named turtle): + >>> turtle.color("blue") + >>> astamp = turtle.stamp() + >>> turtle.fd(50) + >>> turtle.clearstamp(astamp) + """ + self._clearstamp(stampid) + self._update() + + def clearstamps(self, n=None): + """Delete all or first/last n of turtle's stamps. + + Optional argument: + n -- an integer + + If n is None, delete all of pen's stamps, + else if n > 0 delete first n stamps + else if n < 0 delete last n stamps. + + Example (for a Turtle instance named turtle): + >>> for i in range(8): + ... turtle.stamp(); turtle.fd(30) + ... + >>> turtle.clearstamps(2) + >>> turtle.clearstamps(-2) + >>> turtle.clearstamps() + """ + if n is None: + toDelete = self.stampItems[:] + elif n >= 0: + toDelete = self.stampItems[:n] + else: + toDelete = self.stampItems[n:] + for item in toDelete: + self._clearstamp(item) + self._update() + + def _goto(self, end): + """Move the pen to the point end, thereby drawing a line + if pen is down. All other methods for turtle movement depend + on this one. + """ + ## Version with undo-stuff + go_modes = ( self._drawing, + self._pencolor, + self._pensize, + isinstance(self._fillpath, list)) + screen = self.screen + undo_entry = ("go", self._position, end, go_modes, + (self.currentLineItem, + self.currentLine[:], + screen._pointlist(self.currentLineItem), + self.items[:]) + ) + if self.undobuffer: + self.undobuffer.push(undo_entry) + start = self._position + if self._speed and screen._tracing == 1: + diff = (end-start) + diffsq = (diff[0]*screen.xscale)**2 + (diff[1]*screen.yscale)**2 + nhops = 1+int((diffsq**0.5)/(3*(1.1**self._speed)*self._speed)) + delta = diff * (1.0/nhops) + for n in range(1, nhops): + if n == 1: + top = True + else: + top = False + self._position = start + delta * n + if self._drawing: + screen._drawline(self.drawingLineItem, + (start, self._position), + self._pencolor, self._pensize, top) + self._update() + if self._drawing: + screen._drawline(self.drawingLineItem, ((0, 0), (0, 0)), + fill="", width=self._pensize) + # Turtle now at end, + if self._drawing: # now update currentLine + self.currentLine.append(end) + if isinstance(self._fillpath, list): + self._fillpath.append(end) + ###### vererbung!!!!!!!!!!!!!!!!!!!!!! + self._position = end + if self._creatingPoly: + self._poly.append(end) + if len(self.currentLine) > 42: # 42! answer to the ultimate question + # of life, the universe and everything + self._newLine() + self._update() #count=True) + + def _undogoto(self, entry): + """Reverse a _goto. Used for undo() + """ + old, new, go_modes, coodata = entry + drawing, pc, ps, filling = go_modes + cLI, cL, pl, items = coodata + screen = self.screen + if abs(self._position - new) > 0.5: + print ("undogoto: HALLO-DA-STIMMT-WAS-NICHT!") + # restore former situation + self.currentLineItem = cLI + self.currentLine = cL + + if pl == [(0, 0), (0, 0)]: + usepc = "" + else: + usepc = pc + screen._drawline(cLI, pl, fill=usepc, width=ps) + + todelete = [i for i in self.items if (i not in items) and + (screen._type(i) == "line")] + for i in todelete: + screen._delete(i) + self.items.remove(i) + + start = old + if self._speed and screen._tracing == 1: + diff = old - new + diffsq = (diff[0]*screen.xscale)**2 + (diff[1]*screen.yscale)**2 + nhops = 1+int((diffsq**0.5)/(3*(1.1**self._speed)*self._speed)) + delta = diff * (1.0/nhops) + for n in range(1, nhops): + if n == 1: + top = True + else: + top = False + self._position = new + delta * n + if drawing: + screen._drawline(self.drawingLineItem, + (start, self._position), + pc, ps, top) + self._update() + if drawing: + screen._drawline(self.drawingLineItem, ((0, 0), (0, 0)), + fill="", width=ps) + # Turtle now at position old, + self._position = old + ## if undo is done during creating a polygon, the last vertex + ## will be deleted. if the polygon is entirely deleted, + ## creatingPoly will be set to False. + ## Polygons created before the last one will not be affected by undo() + if self._creatingPoly: + if len(self._poly) > 0: + self._poly.pop() + if self._poly == []: + self._creatingPoly = False + self._poly = None + if filling: + if self._fillpath == []: + self._fillpath = None + print("Unwahrscheinlich in _undogoto!") + elif self._fillpath is not None: + self._fillpath.pop() + self._update() #count=True) + + def _rotate(self, angle): + """Turns pen clockwise by angle. + """ + if self.undobuffer: + self.undobuffer.push(("rot", angle, self._degreesPerAU)) + angle *= self._degreesPerAU + neworient = self._orient.rotate(angle) + tracing = self.screen._tracing + if tracing == 1 and self._speed > 0: + anglevel = 3.0 * self._speed + steps = 1 + int(abs(angle)/anglevel) + delta = 1.0*angle/steps + for _ in range(steps): + self._orient = self._orient.rotate(delta) + self._update() + self._orient = neworient + self._update() + + def _newLine(self, usePos=True): + """Closes current line item and starts a new one. + Remark: if current line became too long, animation + performance (via _drawline) slowed down considerably. + """ + if len(self.currentLine) > 1: + self.screen._drawline(self.currentLineItem, self.currentLine, + self._pencolor, self._pensize) + self.currentLineItem = self.screen._createline() + self.items.append(self.currentLineItem) + else: + self.screen._drawline(self.currentLineItem, top=True) + self.currentLine = [] + if usePos: + self.currentLine = [self._position] + + def filling(self): + """Return fillstate (True if filling, False else). + + No argument. + + Example (for a Turtle instance named turtle): + >>> turtle.begin_fill() + >>> if turtle.filling(): + ... turtle.pensize(5) + ... else: + ... turtle.pensize(3) + """ + return isinstance(self._fillpath, list) + + def begin_fill(self): + """Called just before drawing a shape to be filled. + + No argument. + + Example (for a Turtle instance named turtle): + >>> turtle.color("black", "red") + >>> turtle.begin_fill() + >>> turtle.circle(60) + >>> turtle.end_fill() + """ + if not self.filling(): + self._fillitem = self.screen._createpoly() + self.items.append(self._fillitem) + self._fillpath = [self._position] + self._newLine() + if self.undobuffer: + self.undobuffer.push(("beginfill", self._fillitem)) + self._update() + + + def end_fill(self): + """Fill the shape drawn after the call begin_fill(). + + No argument. + + Example (for a Turtle instance named turtle): + >>> turtle.color("black", "red") + >>> turtle.begin_fill() + >>> turtle.circle(60) + >>> turtle.end_fill() + """ + if self.filling(): + if len(self._fillpath) > 2: + self.screen._drawpoly(self._fillitem, self._fillpath, + fill=self._fillcolor) + if self.undobuffer: + self.undobuffer.push(("dofill", self._fillitem)) + self._fillitem = self._fillpath = None + self._update() + + def dot(self, size=None, *color): + """Draw a dot with diameter size, using color. + + Optional arguments: + size -- an integer >= 1 (if given) + color -- a colorstring or a numeric color tuple + + Draw a circular dot with diameter size, using color. + If size is not given, the maximum of pensize+4 and 2*pensize is used. + + Example (for a Turtle instance named turtle): + >>> turtle.dot() + >>> turtle.fd(50); turtle.dot(20, "blue"); turtle.fd(50) + """ + if not color: + if isinstance(size, (str, tuple)): + color = self._colorstr(size) + size = self._pensize + max(self._pensize, 4) + else: + color = self._pencolor + if not size: + size = self._pensize + max(self._pensize, 4) + else: + if size is None: + size = self._pensize + max(self._pensize, 4) + color = self._colorstr(color) + if hasattr(self.screen, "_dot"): + item = self.screen._dot(self._position, size, color) + self.items.append(item) + if self.undobuffer: + self.undobuffer.push(("dot", item)) + else: + pen = self.pen() + if self.undobuffer: + self.undobuffer.push(["seq"]) + self.undobuffer.cumulate = True + try: + if self.resizemode() == 'auto': + self.ht() + self.pendown() + self.pensize(size) + self.pencolor(color) + self.forward(0) + finally: + self.pen(pen) + if self.undobuffer: + self.undobuffer.cumulate = False + + def _write(self, txt, align, font): + """Performs the writing for write() + """ + item, end = self.screen._write(self._position, txt, align, font, + self._pencolor) + self.items.append(item) + if self.undobuffer: + self.undobuffer.push(("wri", item)) + return end + + def write(self, arg, move=False, align="left", font=("Arial", 8, "normal")): + """Write text at the current turtle position. + + Arguments: + arg -- info, which is to be written to the TurtleScreen + move (optional) -- True/False + align (optional) -- one of the strings "left", "center" or right" + font (optional) -- a triple (fontname, fontsize, fonttype) + + Write text - the string representation of arg - at the current + turtle position according to align ("left", "center" or right") + and with the given font. + If move is True, the pen is moved to the bottom-right corner + of the text. By default, move is False. + + Example (for a Turtle instance named turtle): + >>> turtle.write('Home = ', True, align="center") + >>> turtle.write((0,0), True) + """ + if self.undobuffer: + self.undobuffer.push(["seq"]) + self.undobuffer.cumulate = True + end = self._write(str(arg), align.lower(), font) + if move: + x, y = self.pos() + self.setpos(end, y) + if self.undobuffer: + self.undobuffer.cumulate = False + + def begin_poly(self): + """Start recording the vertices of a polygon. + + No argument. + + Start recording the vertices of a polygon. Current turtle position + is first point of polygon. + + Example (for a Turtle instance named turtle): + >>> turtle.begin_poly() + """ + self._poly = [self._position] + self._creatingPoly = True + + def end_poly(self): + """Stop recording the vertices of a polygon. + + No argument. + + Stop recording the vertices of a polygon. Current turtle position is + last point of polygon. This will be connected with the first point. + + Example (for a Turtle instance named turtle): + >>> turtle.end_poly() + """ + self._creatingPoly = False + + def get_poly(self): + """Return the lastly recorded polygon. + + No argument. + + Example (for a Turtle instance named turtle): + >>> p = turtle.get_poly() + >>> turtle.register_shape("myFavouriteShape", p) + """ + ## check if there is any poly? + if self._poly is not None: + return tuple(self._poly) + + def getscreen(self): + """Return the TurtleScreen object, the turtle is drawing on. + + No argument. + + Return the TurtleScreen object, the turtle is drawing on. + So TurtleScreen-methods can be called for that object. + + Example (for a Turtle instance named turtle): + >>> ts = turtle.getscreen() + >>> ts + + >>> ts.bgcolor("pink") + """ + return self.screen + + def getturtle(self): + """Return the Turtleobject itself. + + No argument. + + Only reasonable use: as a function to return the 'anonymous turtle': + + Example: + >>> pet = getturtle() + >>> pet.fd(50) + >>> pet + + >>> turtles() + [] + """ + return self + + getpen = getturtle + + + ################################################################ + ### screen oriented methods recurring to methods of TurtleScreen + ################################################################ + + def _delay(self, delay=None): + """Set delay value which determines speed of turtle animation. + """ + return self.screen.delay(delay) + + def onclick(self, fun, btn=1, add=None): + """Bind fun to mouse-click event on this turtle on canvas. + + Arguments: + fun -- a function with two arguments, to which will be assigned + the coordinates of the clicked point on the canvas. + btn -- number of the mouse-button defaults to 1 (left mouse button). + add -- True or False. If True, new binding will be added, otherwise + it will replace a former binding. + + Example for the anonymous turtle, i. e. the procedural way: + + >>> def turn(x, y): + ... left(360) + ... + >>> onclick(turn) # Now clicking into the turtle will turn it. + >>> onclick(None) # event-binding will be removed + """ + self.screen._onclick(self.turtle._item, fun, btn, add) + self._update() + + def onrelease(self, fun, btn=1, add=None): + """Bind fun to mouse-button-release event on this turtle on canvas. + + Arguments: + fun -- a function with two arguments, to which will be assigned + the coordinates of the clicked point on the canvas. + btn -- number of the mouse-button defaults to 1 (left mouse button). + + Example (for a MyTurtle instance named joe): + >>> class MyTurtle(Turtle): + ... def glow(self,x,y): + ... self.fillcolor("red") + ... def unglow(self,x,y): + ... self.fillcolor("") + ... + >>> joe = MyTurtle() + >>> joe.onclick(joe.glow) + >>> joe.onrelease(joe.unglow) + + Clicking on joe turns fillcolor red, unclicking turns it to + transparent. + """ + self.screen._onrelease(self.turtle._item, fun, btn, add) + self._update() + + def ondrag(self, fun, btn=1, add=None): + """Bind fun to mouse-move event on this turtle on canvas. + + Arguments: + fun -- a function with two arguments, to which will be assigned + the coordinates of the clicked point on the canvas. + btn -- number of the mouse-button defaults to 1 (left mouse button). + + Every sequence of mouse-move-events on a turtle is preceded by a + mouse-click event on that turtle. + + Example (for a Turtle instance named turtle): + >>> turtle.ondrag(turtle.goto) + + Subsequently clicking and dragging a Turtle will move it + across the screen thereby producing handdrawings (if pen is + down). + """ + self.screen._ondrag(self.turtle._item, fun, btn, add) + + + def _undo(self, action, data): + """Does the main part of the work for undo() + """ + if self.undobuffer is None: + return + if action == "rot": + angle, degPAU = data + self._rotate(-angle*degPAU/self._degreesPerAU) + dummy = self.undobuffer.pop() + elif action == "stamp": + stitem = data[0] + self.clearstamp(stitem) + elif action == "go": + self._undogoto(data) + elif action in ["wri", "dot"]: + item = data[0] + self.screen._delete(item) + self.items.remove(item) + elif action == "dofill": + item = data[0] + self.screen._drawpoly(item, ((0, 0),(0, 0),(0, 0)), + fill="", outline="") + elif action == "beginfill": + item = data[0] + self._fillitem = self._fillpath = None + if item in self.items: + self.screen._delete(item) + self.items.remove(item) + elif action == "pen": + TPen.pen(self, data[0]) + self.undobuffer.pop() + + def undo(self): + """undo (repeatedly) the last turtle action. + + No argument. + + undo (repeatedly) the last turtle action. + Number of available undo actions is determined by the size of + the undobuffer. + + Example (for a Turtle instance named turtle): + >>> for i in range(4): + ... turtle.fd(50); turtle.lt(80) + ... + >>> for i in range(8): + ... turtle.undo() + ... + """ + if self.undobuffer is None: + return + item = self.undobuffer.pop() + action = item[0] + data = item[1:] + if action == "seq": + while data: + item = data.pop() + self._undo(item[0], item[1:]) + else: + self._undo(action, data) + + turtlesize = shapesize + +RawPen = RawTurtle + +### Screen - Singleton ######################## + +def Screen(): + """Return the singleton screen object. + If none exists at the moment, create a new one and return it, + else return the existing one.""" + if Turtle._screen is None: + Turtle._screen = _Screen() + return Turtle._screen + +class _Screen(TurtleScreen): + + _root = None + _canvas = None + _title = _CFG["title"] + + def __init__(self): + # XXX there is no need for this code to be conditional, + # as there will be only a single _Screen instance, anyway + # XXX actually, the turtle demo is injecting root window, + # so perhaps the conditional creation of a root should be + # preserved (perhaps by passing it as an optional parameter) + if _Screen._root is None: + _Screen._root = self._root = _Root() + self._root.title(_Screen._title) + self._root.ondestroy(self._destroy) + if _Screen._canvas is None: + width = _CFG["width"] + height = _CFG["height"] + canvwidth = _CFG["canvwidth"] + canvheight = _CFG["canvheight"] + leftright = _CFG["leftright"] + topbottom = _CFG["topbottom"] + self._root.setupcanvas(width, height, canvwidth, canvheight) + _Screen._canvas = self._root._getcanvas() + TurtleScreen.__init__(self, _Screen._canvas) + self.setup(width, height, leftright, topbottom) + + def setup(self, width=_CFG["width"], height=_CFG["height"], + startx=_CFG["leftright"], starty=_CFG["topbottom"]): + """ Set the size and position of the main window. + + Arguments: + width: as integer a size in pixels, as float a fraction of the screen. + Default is 50% of screen. + height: as integer the height in pixels, as float a fraction of the + screen. Default is 75% of screen. + startx: if positive, starting position in pixels from the left + edge of the screen, if negative from the right edge + Default, startx=None is to center window horizontally. + starty: if positive, starting position in pixels from the top + edge of the screen, if negative from the bottom edge + Default, starty=None is to center window vertically. + + Examples (for a Screen instance named screen): + >>> screen.setup (width=200, height=200, startx=0, starty=0) + + sets window to 200x200 pixels, in upper left of screen + + >>> screen.setup(width=.75, height=0.5, startx=None, starty=None) + + sets window to 75% of screen by 50% of screen and centers + """ + if not hasattr(self._root, "set_geometry"): + return + sw = self._root.win_width() + sh = self._root.win_height() + if isinstance(width, float) and 0 <= width <= 1: + width = sw*width + if startx is None: + startx = (sw - width) / 2 + if isinstance(height, float) and 0 <= height <= 1: + height = sh*height + if starty is None: + starty = (sh - height) / 2 + self._root.set_geometry(width, height, startx, starty) + self.update() + + def title(self, titlestring): + """Set title of turtle-window + + Argument: + titlestring -- a string, to appear in the titlebar of the + turtle graphics window. + + This is a method of Screen-class. Not available for TurtleScreen- + objects. + + Example (for a Screen instance named screen): + >>> screen.title("Welcome to the turtle-zoo!") + """ + if _Screen._root is not None: + _Screen._root.title(titlestring) + _Screen._title = titlestring + + def _destroy(self): + root = self._root + if root is _Screen._root: + Turtle._pen = None + Turtle._screen = None + _Screen._root = None + _Screen._canvas = None + TurtleScreen._RUNNING = False + root.destroy() + + def bye(self): + """Shut the turtlegraphics window. + + Example (for a TurtleScreen instance named screen): + >>> screen.bye() + """ + self._destroy() + + def exitonclick(self): + """Go into mainloop until the mouse is clicked. + + No arguments. + + Bind bye() method to mouseclick on TurtleScreen. + If "using_IDLE" - value in configuration dictionary is False + (default value), enter mainloop. + If IDLE with -n switch (no subprocess) is used, this value should be + set to True in turtle.cfg. In this case IDLE's mainloop + is active also for the client script. + + This is a method of the Screen-class and not available for + TurtleScreen instances. + + Example (for a Screen instance named screen): + >>> screen.exitonclick() + + """ + def exitGracefully(x, y): + """Screen.bye() with two dummy-parameters""" + self.bye() + self.onclick(exitGracefully) + if _CFG["using_IDLE"]: + return + try: + mainloop() + except AttributeError: + exit(0) + +class Turtle(RawTurtle): + """RawTurtle auto-creating (scrolled) canvas. + + When a Turtle object is created or a function derived from some + Turtle method is called a TurtleScreen object is automatically created. + """ + _pen = None + _screen = None + + def __init__(self, + shape=_CFG["shape"], + undobuffersize=_CFG["undobuffersize"], + visible=_CFG["visible"]): + if Turtle._screen is None: + Turtle._screen = Screen() + RawTurtle.__init__(self, Turtle._screen, + shape=shape, + undobuffersize=undobuffersize, + visible=visible) + +Pen = Turtle + +def write_docstringdict(filename="turtle_docstringdict"): + """Create and write docstring-dictionary to file. + + Optional argument: + filename -- a string, used as filename + default value is turtle_docstringdict + + Has to be called explicitly, (not used by the turtle-graphics classes) + The docstring dictionary will be written to the Python script .py + It is intended to serve as a template for translation of the docstrings + into different languages. + """ + docsdict = {} + + for methodname in _tg_screen_functions: + key = "_Screen."+methodname + docsdict[key] = eval(key).__doc__ + for methodname in _tg_turtle_functions: + key = "Turtle."+methodname + docsdict[key] = eval(key).__doc__ + + with open("%s.py" % filename,"w") as f: + keys = sorted(x for x in docsdict + if x.split('.')[1] not in _alias_list) + f.write('docsdict = {\n\n') + for key in keys[:-1]: + f.write('%s :\n' % repr(key)) + f.write(' """%s\n""",\n\n' % docsdict[key]) + key = keys[-1] + f.write('%s :\n' % repr(key)) + f.write(' """%s\n"""\n\n' % docsdict[key]) + f.write("}\n") + f.close() + +def read_docstrings(lang): + """Read in docstrings from lang-specific docstring dictionary. + + Transfer docstrings, translated to lang, from a dictionary-file + to the methods of classes Screen and Turtle and - in revised form - + to the corresponding functions. + """ + modname = "turtle_docstringdict_%(language)s" % {'language':lang.lower()} + module = __import__(modname) + docsdict = module.docsdict + for key in docsdict: + try: +# eval(key).im_func.__doc__ = docsdict[key] + eval(key).__doc__ = docsdict[key] + except Exception: + print("Bad docstring-entry: %s" % key) + +_LANGUAGE = _CFG["language"] + +try: + if _LANGUAGE != "english": + read_docstrings(_LANGUAGE) +except ImportError: + print("Cannot find docsdict for", _LANGUAGE) +except Exception: + print ("Unknown Error when trying to import %s-docstring-dictionary" % + _LANGUAGE) + + +def getmethparlist(ob): + """Get strings describing the arguments for the given object + + Returns a pair of strings representing function parameter lists + including parenthesis. The first string is suitable for use in + function definition and the second is suitable for use in function + call. The "self" parameter is not included. + """ + defText = callText = "" + # bit of a hack for methods - turn it into a function + # but we drop the "self" param. + # Try and build one for Python defined functions + args, varargs, varkw = inspect.getargs(ob.__code__) + items2 = args[1:] + realArgs = args[1:] + defaults = ob.__defaults__ or [] + defaults = ["=%r" % (value,) for value in defaults] + defaults = [""] * (len(realArgs)-len(defaults)) + defaults + items1 = [arg + dflt for arg, dflt in zip(realArgs, defaults)] + if varargs is not None: + items1.append("*" + varargs) + items2.append("*" + varargs) + if varkw is not None: + items1.append("**" + varkw) + items2.append("**" + varkw) + defText = ", ".join(items1) + defText = "(%s)" % defText + callText = ", ".join(items2) + callText = "(%s)" % callText + return defText, callText + +def _turtle_docrevise(docstr): + """To reduce docstrings from RawTurtle class for functions + """ + import re + if docstr is None: + return None + turtlename = _CFG["exampleturtle"] + newdocstr = docstr.replace("%s." % turtlename,"") + parexp = re.compile(r' \(.+ %s\):' % turtlename) + newdocstr = parexp.sub(":", newdocstr) + return newdocstr + +def _screen_docrevise(docstr): + """To reduce docstrings from TurtleScreen class for functions + """ + import re + if docstr is None: + return None + screenname = _CFG["examplescreen"] + newdocstr = docstr.replace("%s." % screenname,"") + parexp = re.compile(r' \(.+ %s\):' % screenname) + newdocstr = parexp.sub(":", newdocstr) + return newdocstr + +## The following mechanism makes all methods of RawTurtle and Turtle available +## as functions. So we can enhance, change, add, delete methods to these +## classes and do not need to change anything here. + +__func_body = """\ +def {name}{paramslist}: + if {obj} is None: + if not TurtleScreen._RUNNING: + TurtleScreen._RUNNING = True + raise Terminator + {obj} = {init} + try: + return {obj}.{name}{argslist} + except TK.TclError: + if not TurtleScreen._RUNNING: + TurtleScreen._RUNNING = True + raise Terminator + raise +""" + +def _make_global_funcs(functions, cls, obj, init, docrevise): + for methodname in functions: + method = getattr(cls, methodname) + pl1, pl2 = getmethparlist(method) + if pl1 == "": + print(">>>>>>", pl1, pl2) + continue + defstr = __func_body.format(obj=obj, init=init, name=methodname, + paramslist=pl1, argslist=pl2) + exec(defstr, globals()) + globals()[methodname].__doc__ = docrevise(method.__doc__) + +_make_global_funcs(_tg_screen_functions, _Screen, + 'Turtle._screen', 'Screen()', _screen_docrevise) +_make_global_funcs(_tg_turtle_functions, Turtle, + 'Turtle._pen', 'Turtle()', _turtle_docrevise) + + +done = mainloop + +if __name__ == "__main__": + def switchpen(): + if isdown(): + pu() + else: + pd() + + def demo1(): + """Demo of old turtle.py - module""" + reset() + tracer(True) + up() + backward(100) + down() + # draw 3 squares; the last filled + width(3) + for i in range(3): + if i == 2: + begin_fill() + for _ in range(4): + forward(20) + left(90) + if i == 2: + color("maroon") + end_fill() + up() + forward(30) + down() + width(1) + color("black") + # move out of the way + tracer(False) + up() + right(90) + forward(100) + right(90) + forward(100) + right(180) + down() + # some text + write("startstart", 1) + write("start", 1) + color("red") + # staircase + for i in range(5): + forward(20) + left(90) + forward(20) + right(90) + # filled staircase + tracer(True) + begin_fill() + for i in range(5): + forward(20) + left(90) + forward(20) + right(90) + end_fill() + # more text + + def demo2(): + """Demo of some new features.""" + speed(1) + st() + pensize(3) + setheading(towards(0, 0)) + radius = distance(0, 0)/2.0 + rt(90) + for _ in range(18): + switchpen() + circle(radius, 10) + write("wait a moment...") + while undobufferentries(): + undo() + reset() + lt(90) + colormode(255) + laenge = 10 + pencolor("green") + pensize(3) + lt(180) + for i in range(-2, 16): + if i > 0: + begin_fill() + fillcolor(255-15*i, 0, 15*i) + for _ in range(3): + fd(laenge) + lt(120) + end_fill() + laenge += 10 + lt(15) + speed((speed()+1)%12) + #end_fill() + + lt(120) + pu() + fd(70) + rt(30) + pd() + color("red","yellow") + speed(0) + begin_fill() + for _ in range(4): + circle(50, 90) + rt(90) + fd(30) + rt(90) + end_fill() + lt(90) + pu() + fd(30) + pd() + shape("turtle") + + tri = getturtle() + tri.resizemode("auto") + turtle = Turtle() + turtle.resizemode("auto") + turtle.shape("turtle") + turtle.reset() + turtle.left(90) + turtle.speed(0) + turtle.up() + turtle.goto(280, 40) + turtle.lt(30) + turtle.down() + turtle.speed(6) + turtle.color("blue","orange") + turtle.pensize(2) + tri.speed(6) + setheading(towards(turtle)) + count = 1 + while tri.distance(turtle) > 4: + turtle.fd(3.5) + turtle.lt(0.6) + tri.setheading(tri.towards(turtle)) + tri.fd(4) + if count % 20 == 0: + turtle.stamp() + tri.stamp() + switchpen() + count += 1 + tri.write("CAUGHT! ", font=("Arial", 16, "bold"), align="right") + tri.pencolor("black") + tri.pencolor("red") + + def baba(xdummy, ydummy): + clearscreen() + bye() + + time.sleep(2) + + while undobufferentries(): + tri.undo() + turtle.undo() + tri.fd(50) + tri.write(" Click me!", font = ("Courier", 12, "bold") ) + tri.onclick(baba, 1) + + demo1() + demo2() + exitonclick() diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/uuid.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/uuid.py new file mode 100644 index 0000000000000000000000000000000000000000..9540c21e6569fff7e3b33e09005f8dbafab4289b --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/uuid.py @@ -0,0 +1,795 @@ +r"""UUID objects (universally unique identifiers) according to RFC 4122. + +This module provides immutable UUID objects (class UUID) and the functions +uuid1(), uuid3(), uuid4(), uuid5() for generating version 1, 3, 4, and 5 +UUIDs as specified in RFC 4122. + +If all you want is a unique ID, you should probably call uuid1() or uuid4(). +Note that uuid1() may compromise privacy since it creates a UUID containing +the computer's network address. uuid4() creates a random UUID. + +Typical usage: + + >>> import uuid + + # make a UUID based on the host ID and current time + >>> uuid.uuid1() # doctest: +SKIP + UUID('a8098c1a-f86e-11da-bd1a-00112444be1e') + + # make a UUID using an MD5 hash of a namespace UUID and a name + >>> uuid.uuid3(uuid.NAMESPACE_DNS, 'python.org') + UUID('6fa459ea-ee8a-3ca4-894e-db77e160355e') + + # make a random UUID + >>> uuid.uuid4() # doctest: +SKIP + UUID('16fd2706-8baf-433b-82eb-8c7fada847da') + + # make a UUID using a SHA-1 hash of a namespace UUID and a name + >>> uuid.uuid5(uuid.NAMESPACE_DNS, 'python.org') + UUID('886313e1-3b8a-5372-9b90-0c9aee199e5d') + + # make a UUID from a string of hex digits (braces and hyphens ignored) + >>> x = uuid.UUID('{00010203-0405-0607-0809-0a0b0c0d0e0f}') + + # convert a UUID to a string of hex digits in standard form + >>> str(x) + '00010203-0405-0607-0809-0a0b0c0d0e0f' + + # get the raw 16 bytes of the UUID + >>> x.bytes + b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f' + + # make a UUID from a 16-byte string + >>> uuid.UUID(bytes=x.bytes) + UUID('00010203-0405-0607-0809-0a0b0c0d0e0f') +""" + +import os +import sys + +from enum import Enum + + +__author__ = 'Ka-Ping Yee ' + +# The recognized platforms - known behaviors +if sys.platform in ('win32', 'darwin'): + _AIX = _LINUX = False +else: + import platform + _platform_system = platform.system() + _AIX = _platform_system == 'AIX' + _LINUX = _platform_system == 'Linux' + +RESERVED_NCS, RFC_4122, RESERVED_MICROSOFT, RESERVED_FUTURE = [ + 'reserved for NCS compatibility', 'specified in RFC 4122', + 'reserved for Microsoft compatibility', 'reserved for future definition'] + +int_ = int # The built-in int type +bytes_ = bytes # The built-in bytes type + + +class SafeUUID(Enum): + safe = 0 + unsafe = -1 + unknown = None + + +class UUID: + """Instances of the UUID class represent UUIDs as specified in RFC 4122. + UUID objects are immutable, hashable, and usable as dictionary keys. + Converting a UUID to a string with str() yields something in the form + '12345678-1234-1234-1234-123456789abc'. The UUID constructor accepts + five possible forms: a similar string of hexadecimal digits, or a tuple + of six integer fields (with 32-bit, 16-bit, 16-bit, 8-bit, 8-bit, and + 48-bit values respectively) as an argument named 'fields', or a string + of 16 bytes (with all the integer fields in big-endian order) as an + argument named 'bytes', or a string of 16 bytes (with the first three + fields in little-endian order) as an argument named 'bytes_le', or a + single 128-bit integer as an argument named 'int'. + + UUIDs have these read-only attributes: + + bytes the UUID as a 16-byte string (containing the six + integer fields in big-endian byte order) + + bytes_le the UUID as a 16-byte string (with time_low, time_mid, + and time_hi_version in little-endian byte order) + + fields a tuple of the six integer fields of the UUID, + which are also available as six individual attributes + and two derived attributes: + + time_low the first 32 bits of the UUID + time_mid the next 16 bits of the UUID + time_hi_version the next 16 bits of the UUID + clock_seq_hi_variant the next 8 bits of the UUID + clock_seq_low the next 8 bits of the UUID + node the last 48 bits of the UUID + + time the 60-bit timestamp + clock_seq the 14-bit sequence number + + hex the UUID as a 32-character hexadecimal string + + int the UUID as a 128-bit integer + + urn the UUID as a URN as specified in RFC 4122 + + variant the UUID variant (one of the constants RESERVED_NCS, + RFC_4122, RESERVED_MICROSOFT, or RESERVED_FUTURE) + + version the UUID version number (1 through 5, meaningful only + when the variant is RFC_4122) + + is_safe An enum indicating whether the UUID has been generated in + a way that is safe for multiprocessing applications, via + uuid_generate_time_safe(3). + """ + + __slots__ = ('int', 'is_safe', '__weakref__') + + def __init__(self, hex=None, bytes=None, bytes_le=None, fields=None, + int=None, version=None, + *, is_safe=SafeUUID.unknown): + r"""Create a UUID from either a string of 32 hexadecimal digits, + a string of 16 bytes as the 'bytes' argument, a string of 16 bytes + in little-endian order as the 'bytes_le' argument, a tuple of six + integers (32-bit time_low, 16-bit time_mid, 16-bit time_hi_version, + 8-bit clock_seq_hi_variant, 8-bit clock_seq_low, 48-bit node) as + the 'fields' argument, or a single 128-bit integer as the 'int' + argument. When a string of hex digits is given, curly braces, + hyphens, and a URN prefix are all optional. For example, these + expressions all yield the same UUID: + + UUID('{12345678-1234-5678-1234-567812345678}') + UUID('12345678123456781234567812345678') + UUID('urn:uuid:12345678-1234-5678-1234-567812345678') + UUID(bytes='\x12\x34\x56\x78'*4) + UUID(bytes_le='\x78\x56\x34\x12\x34\x12\x78\x56' + + '\x12\x34\x56\x78\x12\x34\x56\x78') + UUID(fields=(0x12345678, 0x1234, 0x5678, 0x12, 0x34, 0x567812345678)) + UUID(int=0x12345678123456781234567812345678) + + Exactly one of 'hex', 'bytes', 'bytes_le', 'fields', or 'int' must + be given. The 'version' argument is optional; if given, the resulting + UUID will have its variant and version set according to RFC 4122, + overriding the given 'hex', 'bytes', 'bytes_le', 'fields', or 'int'. + + is_safe is an enum exposed as an attribute on the instance. It + indicates whether the UUID has been generated in a way that is safe + for multiprocessing applications, via uuid_generate_time_safe(3). + """ + + if [hex, bytes, bytes_le, fields, int].count(None) != 4: + raise TypeError('one of the hex, bytes, bytes_le, fields, ' + 'or int arguments must be given') + if hex is not None: + hex = hex.replace('urn:', '').replace('uuid:', '') + hex = hex.strip('{}').replace('-', '') + if len(hex) != 32: + raise ValueError('badly formed hexadecimal UUID string') + int = int_(hex, 16) + if bytes_le is not None: + if len(bytes_le) != 16: + raise ValueError('bytes_le is not a 16-char string') + bytes = (bytes_le[4-1::-1] + bytes_le[6-1:4-1:-1] + + bytes_le[8-1:6-1:-1] + bytes_le[8:]) + if bytes is not None: + if len(bytes) != 16: + raise ValueError('bytes is not a 16-char string') + assert isinstance(bytes, bytes_), repr(bytes) + int = int_.from_bytes(bytes, byteorder='big') + if fields is not None: + if len(fields) != 6: + raise ValueError('fields is not a 6-tuple') + (time_low, time_mid, time_hi_version, + clock_seq_hi_variant, clock_seq_low, node) = fields + if not 0 <= time_low < 1<<32: + raise ValueError('field 1 out of range (need a 32-bit value)') + if not 0 <= time_mid < 1<<16: + raise ValueError('field 2 out of range (need a 16-bit value)') + if not 0 <= time_hi_version < 1<<16: + raise ValueError('field 3 out of range (need a 16-bit value)') + if not 0 <= clock_seq_hi_variant < 1<<8: + raise ValueError('field 4 out of range (need an 8-bit value)') + if not 0 <= clock_seq_low < 1<<8: + raise ValueError('field 5 out of range (need an 8-bit value)') + if not 0 <= node < 1<<48: + raise ValueError('field 6 out of range (need a 48-bit value)') + clock_seq = (clock_seq_hi_variant << 8) | clock_seq_low + int = ((time_low << 96) | (time_mid << 80) | + (time_hi_version << 64) | (clock_seq << 48) | node) + if int is not None: + if not 0 <= int < 1<<128: + raise ValueError('int is out of range (need a 128-bit value)') + if version is not None: + if not 1 <= version <= 5: + raise ValueError('illegal version number') + # Set the variant to RFC 4122. + int &= ~(0xc000 << 48) + int |= 0x8000 << 48 + # Set the version number. + int &= ~(0xf000 << 64) + int |= version << 76 + object.__setattr__(self, 'int', int) + object.__setattr__(self, 'is_safe', is_safe) + + def __getstate__(self): + d = {'int': self.int} + if self.is_safe != SafeUUID.unknown: + # is_safe is a SafeUUID instance. Return just its value, so that + # it can be un-pickled in older Python versions without SafeUUID. + d['is_safe'] = self.is_safe.value + return d + + def __setstate__(self, state): + object.__setattr__(self, 'int', state['int']) + # is_safe was added in 3.7; it is also omitted when it is "unknown" + object.__setattr__(self, 'is_safe', + SafeUUID(state['is_safe']) + if 'is_safe' in state else SafeUUID.unknown) + + def __eq__(self, other): + if isinstance(other, UUID): + return self.int == other.int + return NotImplemented + + # Q. What's the value of being able to sort UUIDs? + # A. Use them as keys in a B-Tree or similar mapping. + + def __lt__(self, other): + if isinstance(other, UUID): + return self.int < other.int + return NotImplemented + + def __gt__(self, other): + if isinstance(other, UUID): + return self.int > other.int + return NotImplemented + + def __le__(self, other): + if isinstance(other, UUID): + return self.int <= other.int + return NotImplemented + + def __ge__(self, other): + if isinstance(other, UUID): + return self.int >= other.int + return NotImplemented + + def __hash__(self): + return hash(self.int) + + def __int__(self): + return self.int + + def __repr__(self): + return '%s(%r)' % (self.__class__.__name__, str(self)) + + def __setattr__(self, name, value): + raise TypeError('UUID objects are immutable') + + def __str__(self): + hex = '%032x' % self.int + return '%s-%s-%s-%s-%s' % ( + hex[:8], hex[8:12], hex[12:16], hex[16:20], hex[20:]) + + @property + def bytes(self): + return self.int.to_bytes(16, 'big') + + @property + def bytes_le(self): + bytes = self.bytes + return (bytes[4-1::-1] + bytes[6-1:4-1:-1] + bytes[8-1:6-1:-1] + + bytes[8:]) + + @property + def fields(self): + return (self.time_low, self.time_mid, self.time_hi_version, + self.clock_seq_hi_variant, self.clock_seq_low, self.node) + + @property + def time_low(self): + return self.int >> 96 + + @property + def time_mid(self): + return (self.int >> 80) & 0xffff + + @property + def time_hi_version(self): + return (self.int >> 64) & 0xffff + + @property + def clock_seq_hi_variant(self): + return (self.int >> 56) & 0xff + + @property + def clock_seq_low(self): + return (self.int >> 48) & 0xff + + @property + def time(self): + return (((self.time_hi_version & 0x0fff) << 48) | + (self.time_mid << 32) | self.time_low) + + @property + def clock_seq(self): + return (((self.clock_seq_hi_variant & 0x3f) << 8) | + self.clock_seq_low) + + @property + def node(self): + return self.int & 0xffffffffffff + + @property + def hex(self): + return '%032x' % self.int + + @property + def urn(self): + return 'urn:uuid:' + str(self) + + @property + def variant(self): + if not self.int & (0x8000 << 48): + return RESERVED_NCS + elif not self.int & (0x4000 << 48): + return RFC_4122 + elif not self.int & (0x2000 << 48): + return RESERVED_MICROSOFT + else: + return RESERVED_FUTURE + + @property + def version(self): + # The version bits are only meaningful for RFC 4122 UUIDs. + if self.variant == RFC_4122: + return int((self.int >> 76) & 0xf) + +def _popen(command, *args): + import os, shutil, subprocess + executable = shutil.which(command) + if executable is None: + path = os.pathsep.join(('/sbin', '/usr/sbin')) + executable = shutil.which(command, path=path) + if executable is None: + return None + # LC_ALL=C to ensure English output, stderr=DEVNULL to prevent output + # on stderr (Note: we don't have an example where the words we search + # for are actually localized, but in theory some system could do so.) + env = dict(os.environ) + env['LC_ALL'] = 'C' + proc = subprocess.Popen((executable,) + args, + stdout=subprocess.PIPE, + stderr=subprocess.DEVNULL, + env=env) + return proc + +# For MAC (a.k.a. IEEE 802, or EUI-48) addresses, the second least significant +# bit of the first octet signifies whether the MAC address is universally (0) +# or locally (1) administered. Network cards from hardware manufacturers will +# always be universally administered to guarantee global uniqueness of the MAC +# address, but any particular machine may have other interfaces which are +# locally administered. An example of the latter is the bridge interface to +# the Touch Bar on MacBook Pros. +# +# This bit works out to be the 42nd bit counting from 1 being the least +# significant, or 1<<41. We'll prefer universally administered MAC addresses +# over locally administered ones since the former are globally unique, but +# we'll return the first of the latter found if that's all the machine has. +# +# See https://en.wikipedia.org/wiki/MAC_address#Universal_vs._local + +def _is_universal(mac): + return not (mac & (1 << 41)) + +def _find_mac(command, args, hw_identifiers, get_index): + first_local_mac = None + try: + proc = _popen(command, *args.split()) + if not proc: + return None + with proc: + for line in proc.stdout: + words = line.lower().rstrip().split() + for i in range(len(words)): + if words[i] in hw_identifiers: + try: + word = words[get_index(i)] + mac = int(word.replace(b':', b''), 16) + if _is_universal(mac): + return mac + first_local_mac = first_local_mac or mac + except (ValueError, IndexError): + # Virtual interfaces, such as those provided by + # VPNs, do not have a colon-delimited MAC address + # as expected, but a 16-byte HWAddr separated by + # dashes. These should be ignored in favor of a + # real MAC address + pass + except OSError: + pass + return first_local_mac or None + +def _ifconfig_getnode(): + """Get the hardware address on Unix by running ifconfig.""" + # This works on Linux ('' or '-a'), Tru64 ('-av'), but not all Unixes. + keywords = (b'hwaddr', b'ether', b'address:', b'lladdr') + for args in ('', '-a', '-av'): + mac = _find_mac('ifconfig', args, keywords, lambda i: i+1) + if mac: + return mac + return None + +def _ip_getnode(): + """Get the hardware address on Unix by running ip.""" + # This works on Linux with iproute2. + mac = _find_mac('ip', 'link', [b'link/ether'], lambda i: i+1) + if mac: + return mac + return None + +def _arp_getnode(): + """Get the hardware address on Unix by running arp.""" + import os, socket + try: + ip_addr = socket.gethostbyname(socket.gethostname()) + except OSError: + return None + + # Try getting the MAC addr from arp based on our IP address (Solaris). + mac = _find_mac('arp', '-an', [os.fsencode(ip_addr)], lambda i: -1) + if mac: + return mac + + # This works on OpenBSD + mac = _find_mac('arp', '-an', [os.fsencode(ip_addr)], lambda i: i+1) + if mac: + return mac + + # This works on Linux, FreeBSD and NetBSD + mac = _find_mac('arp', '-an', [os.fsencode('(%s)' % ip_addr)], + lambda i: i+2) + # Return None instead of 0. + if mac: + return mac + return None + +def _lanscan_getnode(): + """Get the hardware address on Unix by running lanscan.""" + # This might work on HP-UX. + return _find_mac('lanscan', '-ai', [b'lan0'], lambda i: 0) + +def _netstat_getnode(): + """Get the hardware address on Unix by running netstat.""" + # This might work on AIX, Tru64 UNIX. + first_local_mac = None + try: + proc = _popen('netstat', '-ia') + if not proc: + return None + with proc: + words = proc.stdout.readline().rstrip().split() + try: + i = words.index(b'Address') + except ValueError: + return None + for line in proc.stdout: + try: + words = line.rstrip().split() + word = words[i] + if len(word) == 17 and word.count(b':') == 5: + mac = int(word.replace(b':', b''), 16) + if _is_universal(mac): + return mac + first_local_mac = first_local_mac or mac + except (ValueError, IndexError): + pass + except OSError: + pass + return first_local_mac or None + +def _ipconfig_getnode(): + """Get the hardware address on Windows by running ipconfig.exe.""" + import os, re, subprocess + first_local_mac = None + dirs = ['', r'c:\windows\system32', r'c:\winnt\system32'] + try: + import ctypes + buffer = ctypes.create_string_buffer(300) + ctypes.windll.kernel32.GetSystemDirectoryA(buffer, 300) + dirs.insert(0, buffer.value.decode('mbcs')) + except: + pass + for dir in dirs: + try: + proc = subprocess.Popen([os.path.join(dir, 'ipconfig'), '/all'], + stdout=subprocess.PIPE, + encoding="oem") + except OSError: + continue + with proc: + for line in proc.stdout: + value = line.split(':')[-1].strip().lower() + if re.fullmatch('(?:[0-9a-f][0-9a-f]-){5}[0-9a-f][0-9a-f]', value): + mac = int(value.replace('-', ''), 16) + if _is_universal(mac): + return mac + first_local_mac = first_local_mac or mac + return first_local_mac or None + +def _netbios_getnode(): + """Get the hardware address on Windows using NetBIOS calls. + See http://support.microsoft.com/kb/118623 for details.""" + import win32wnet, netbios + first_local_mac = None + ncb = netbios.NCB() + ncb.Command = netbios.NCBENUM + ncb.Buffer = adapters = netbios.LANA_ENUM() + adapters._pack() + if win32wnet.Netbios(ncb) != 0: + return None + adapters._unpack() + for i in range(adapters.length): + ncb.Reset() + ncb.Command = netbios.NCBRESET + ncb.Lana_num = ord(adapters.lana[i]) + if win32wnet.Netbios(ncb) != 0: + continue + ncb.Reset() + ncb.Command = netbios.NCBASTAT + ncb.Lana_num = ord(adapters.lana[i]) + ncb.Callname = '*'.ljust(16) + ncb.Buffer = status = netbios.ADAPTER_STATUS() + if win32wnet.Netbios(ncb) != 0: + continue + status._unpack() + bytes = status.adapter_address[:6] + if len(bytes) != 6: + continue + mac = int.from_bytes(bytes, 'big') + if _is_universal(mac): + return mac + first_local_mac = first_local_mac or mac + return first_local_mac or None + + +_generate_time_safe = _UuidCreate = None +_has_uuid_generate_time_safe = None + +# Import optional C extension at toplevel, to help disabling it when testing +try: + import _uuid +except ImportError: + _uuid = None + + +def _load_system_functions(): + """ + Try to load platform-specific functions for generating uuids. + """ + global _generate_time_safe, _UuidCreate, _has_uuid_generate_time_safe + + if _has_uuid_generate_time_safe is not None: + return + + _has_uuid_generate_time_safe = False + + if sys.platform == "darwin" and int(os.uname().release.split('.')[0]) < 9: + # The uuid_generate_* functions are broken on MacOS X 10.5, as noted + # in issue #8621 the function generates the same sequence of values + # in the parent process and all children created using fork (unless + # those children use exec as well). + # + # Assume that the uuid_generate functions are broken from 10.5 onward, + # the test can be adjusted when a later version is fixed. + pass + elif _uuid is not None: + _generate_time_safe = _uuid.generate_time_safe + _has_uuid_generate_time_safe = _uuid.has_uuid_generate_time_safe + return + + try: + # If we couldn't find an extension module, try ctypes to find + # system routines for UUID generation. + # Thanks to Thomas Heller for ctypes and for his help with its use here. + import ctypes + import ctypes.util + + # The uuid_generate_* routines are provided by libuuid on at least + # Linux and FreeBSD, and provided by libc on Mac OS X. + _libnames = ['uuid'] + if not sys.platform.startswith('win'): + _libnames.append('c') + for libname in _libnames: + try: + lib = ctypes.CDLL(ctypes.util.find_library(libname)) + except Exception: # pragma: nocover + continue + # Try to find the safe variety first. + if hasattr(lib, 'uuid_generate_time_safe'): + _uuid_generate_time_safe = lib.uuid_generate_time_safe + # int uuid_generate_time_safe(uuid_t out); + def _generate_time_safe(): + _buffer = ctypes.create_string_buffer(16) + res = _uuid_generate_time_safe(_buffer) + return bytes(_buffer.raw), res + _has_uuid_generate_time_safe = True + break + + elif hasattr(lib, 'uuid_generate_time'): # pragma: nocover + _uuid_generate_time = lib.uuid_generate_time + # void uuid_generate_time(uuid_t out); + _uuid_generate_time.restype = None + def _generate_time_safe(): + _buffer = ctypes.create_string_buffer(16) + _uuid_generate_time(_buffer) + return bytes(_buffer.raw), None + break + + # On Windows prior to 2000, UuidCreate gives a UUID containing the + # hardware address. On Windows 2000 and later, UuidCreate makes a + # random UUID and UuidCreateSequential gives a UUID containing the + # hardware address. These routines are provided by the RPC runtime. + # NOTE: at least on Tim's WinXP Pro SP2 desktop box, while the last + # 6 bytes returned by UuidCreateSequential are fixed, they don't appear + # to bear any relationship to the MAC address of any network device + # on the box. + try: + lib = ctypes.windll.rpcrt4 + except: + lib = None + _UuidCreate = getattr(lib, 'UuidCreateSequential', + getattr(lib, 'UuidCreate', None)) + + except Exception as exc: + import warnings + warnings.warn(f"Could not find fallback ctypes uuid functions: {exc}", + ImportWarning) + + +def _unix_getnode(): + """Get the hardware address on Unix using the _uuid extension module + or ctypes.""" + _load_system_functions() + uuid_time, _ = _generate_time_safe() + return UUID(bytes=uuid_time).node + +def _windll_getnode(): + """Get the hardware address on Windows using ctypes.""" + import ctypes + _load_system_functions() + _buffer = ctypes.create_string_buffer(16) + if _UuidCreate(_buffer) == 0: + return UUID(bytes=bytes_(_buffer.raw)).node + +def _random_getnode(): + """Get a random node ID.""" + # RFC 4122, $4.1.6 says "For systems with no IEEE address, a randomly or + # pseudo-randomly generated value may be used; see Section 4.5. The + # multicast bit must be set in such addresses, in order that they will + # never conflict with addresses obtained from network cards." + # + # The "multicast bit" of a MAC address is defined to be "the least + # significant bit of the first octet". This works out to be the 41st bit + # counting from 1 being the least significant bit, or 1<<40. + # + # See https://en.wikipedia.org/wiki/MAC_address#Unicast_vs._multicast + import random + return random.getrandbits(48) | (1 << 40) + + +# _OS_GETTERS, when known, are targeted for a specific OS or platform. +# The order is by 'common practice' on the specified platform. +# Note: 'posix' and 'windows' _OS_GETTERS are prefixed by a dll/dlload() method +# which, when successful, means none of these "external" methods are called. +# _GETTERS is (also) used by test_uuid.py to SkipUnless(), e.g., +# @unittest.skipUnless(_uuid._ifconfig_getnode in _uuid._GETTERS, ...) +if _LINUX: + _OS_GETTERS = [_ip_getnode, _ifconfig_getnode] +elif sys.platform == 'darwin': + _OS_GETTERS = [_ifconfig_getnode, _arp_getnode, _netstat_getnode] +elif sys.platform == 'win32': + _OS_GETTERS = [_netbios_getnode, _ipconfig_getnode] +elif _AIX: + _OS_GETTERS = [_netstat_getnode] +else: + _OS_GETTERS = [_ifconfig_getnode, _ip_getnode, _arp_getnode, + _netstat_getnode, _lanscan_getnode] +if os.name == 'posix': + _GETTERS = [_unix_getnode] + _OS_GETTERS +elif os.name == 'nt': + _GETTERS = [_windll_getnode] + _OS_GETTERS +else: + _GETTERS = _OS_GETTERS + +_node = None + +def getnode(*, getters=None): + """Get the hardware address as a 48-bit positive integer. + + The first time this runs, it may launch a separate program, which could + be quite slow. If all attempts to obtain the hardware address fail, we + choose a random 48-bit number with its eighth bit set to 1 as recommended + in RFC 4122. + """ + global _node + if _node is not None: + return _node + + for getter in _GETTERS + [_random_getnode]: + try: + _node = getter() + except: + continue + if (_node is not None) and (0 <= _node < (1 << 48)): + return _node + assert False, '_random_getnode() returned invalid value: {}'.format(_node) + + +_last_timestamp = None + +def uuid1(node=None, clock_seq=None): + """Generate a UUID from a host ID, sequence number, and the current time. + If 'node' is not given, getnode() is used to obtain the hardware + address. If 'clock_seq' is given, it is used as the sequence number; + otherwise a random 14-bit sequence number is chosen.""" + + # When the system provides a version-1 UUID generator, use it (but don't + # use UuidCreate here because its UUIDs don't conform to RFC 4122). + _load_system_functions() + if _generate_time_safe is not None and node is clock_seq is None: + uuid_time, safely_generated = _generate_time_safe() + try: + is_safe = SafeUUID(safely_generated) + except ValueError: + is_safe = SafeUUID.unknown + return UUID(bytes=uuid_time, is_safe=is_safe) + + global _last_timestamp + import time + nanoseconds = time.time_ns() + # 0x01b21dd213814000 is the number of 100-ns intervals between the + # UUID epoch 1582-10-15 00:00:00 and the Unix epoch 1970-01-01 00:00:00. + timestamp = nanoseconds // 100 + 0x01b21dd213814000 + if _last_timestamp is not None and timestamp <= _last_timestamp: + timestamp = _last_timestamp + 1 + _last_timestamp = timestamp + if clock_seq is None: + import random + clock_seq = random.getrandbits(14) # instead of stable storage + time_low = timestamp & 0xffffffff + time_mid = (timestamp >> 32) & 0xffff + time_hi_version = (timestamp >> 48) & 0x0fff + clock_seq_low = clock_seq & 0xff + clock_seq_hi_variant = (clock_seq >> 8) & 0x3f + if node is None: + node = getnode() + return UUID(fields=(time_low, time_mid, time_hi_version, + clock_seq_hi_variant, clock_seq_low, node), version=1) + +def uuid3(namespace, name): + """Generate a UUID from the MD5 hash of a namespace UUID and a name.""" + from hashlib import md5 + hash = md5(namespace.bytes + bytes(name, "utf-8")).digest() + return UUID(bytes=hash[:16], version=3) + +def uuid4(): + """Generate a random UUID.""" + return UUID(bytes=os.urandom(16), version=4) + +def uuid5(namespace, name): + """Generate a UUID from the SHA-1 hash of a namespace UUID and a name.""" + from hashlib import sha1 + hash = sha1(namespace.bytes + bytes(name, "utf-8")).digest() + return UUID(bytes=hash[:16], version=5) + +# The following standard UUIDs are for use with uuid3() or uuid5(). + +NAMESPACE_DNS = UUID('6ba7b810-9dad-11d1-80b4-00c04fd430c8') +NAMESPACE_URL = UUID('6ba7b811-9dad-11d1-80b4-00c04fd430c8') +NAMESPACE_OID = UUID('6ba7b812-9dad-11d1-80b4-00c04fd430c8') +NAMESPACE_X500 = UUID('6ba7b814-9dad-11d1-80b4-00c04fd430c8') diff --git a/my_container_sandbox/workspace/anaconda3/lib/python3.8/wave.py b/my_container_sandbox/workspace/anaconda3/lib/python3.8/wave.py new file mode 100644 index 0000000000000000000000000000000000000000..823f091deaaad9e1b7564014a79c12432fd41801 --- /dev/null +++ b/my_container_sandbox/workspace/anaconda3/lib/python3.8/wave.py @@ -0,0 +1,519 @@ +"""Stuff to parse WAVE files. + +Usage. + +Reading WAVE files: + f = wave.open(file, 'r') +where file is either the name of a file or an open file pointer. +The open file pointer must have methods read(), seek(), and close(). +When the setpos() and rewind() methods are not used, the seek() +method is not necessary. + +This returns an instance of a class with the following public methods: + getnchannels() -- returns number of audio channels (1 for + mono, 2 for stereo) + getsampwidth() -- returns sample width in bytes + getframerate() -- returns sampling frequency + getnframes() -- returns number of audio frames + getcomptype() -- returns compression type ('NONE' for linear samples) + getcompname() -- returns human-readable version of + compression type ('not compressed' linear samples) + getparams() -- returns a namedtuple consisting of all of the + above in the above order + getmarkers() -- returns None (for compatibility with the + aifc module) + getmark(id) -- raises an error since the mark does not + exist (for compatibility with the aifc module) + readframes(n) -- returns at most n frames of audio + rewind() -- rewind to the beginning of the audio stream + setpos(pos) -- seek to the specified position + tell() -- return the current position + close() -- close the instance (make it unusable) +The position returned by tell() and the position given to setpos() +are compatible and have nothing to do with the actual position in the +file. +The close() method is called automatically when the class instance +is destroyed. + +Writing WAVE files: + f = wave.open(file, 'w') +where file is either the name of a file or an open file pointer. +The open file pointer must have methods write(), tell(), seek(), and +close(). + +This returns an instance of a class with the following public methods: + setnchannels(n) -- set the number of channels + setsampwidth(n) -- set the sample width + setframerate(n) -- set the frame rate + setnframes(n) -- set the number of frames + setcomptype(type, name) + -- set the compression type and the + human-readable compression type + setparams(tuple) + -- set all parameters at once + tell() -- return current position in output file + writeframesraw(data) + -- write audio frames without patching up the + file header + writeframes(data) + -- write audio frames and patch up the file header + close() -- patch up the file header and close the + output file +You should set the parameters before the first writeframesraw or +writeframes. The total number of frames does not need to be set, +but when it is set to the correct value, the header does not have to +be patched up. +It is best to first set all parameters, perhaps possibly the +compression type, and then write audio frames using writeframesraw. +When all frames have been written, either call writeframes(b'') or +close() to patch up the sizes in the header. +The close() method is called automatically when the class instance +is destroyed. +""" + +import builtins + +__all__ = ["open", "openfp", "Error", "Wave_read", "Wave_write"] + +class Error(Exception): + pass + +WAVE_FORMAT_PCM = 0x0001 + +_array_fmts = None, 'b', 'h', None, 'i' + +import audioop +import struct +import sys +from chunk import Chunk +from collections import namedtuple +import warnings + +_wave_params = namedtuple('_wave_params', + 'nchannels sampwidth framerate nframes comptype compname') + +class Wave_read: + """Variables used in this class: + + These variables are available to the user though appropriate + methods of this class: + _file -- the open file with methods read(), close(), and seek() + set through the __init__() method + _nchannels -- the number of audio channels + available through the getnchannels() method + _nframes -- the number of audio frames + available through the getnframes() method + _sampwidth -- the number of bytes per audio sample + available through the getsampwidth() method + _framerate -- the sampling frequency + available through the getframerate() method + _comptype -- the AIFF-C compression type ('NONE' if AIFF) + available through the getcomptype() method + _compname -- the human-readable AIFF-C compression type + available through the getcomptype() method + _soundpos -- the position in the audio stream + available through the tell() method, set through the + setpos() method + + These variables are used internally only: + _fmt_chunk_read -- 1 iff the FMT chunk has been read + _data_seek_needed -- 1 iff positioned correctly in audio + file for readframes() + _data_chunk -- instantiation of a chunk class for the DATA chunk + _framesize -- size of one frame in the file + """ + + def initfp(self, file): + self._convert = None + self._soundpos = 0 + self._file = Chunk(file, bigendian = 0) + if self._file.getname() != b'RIFF': + raise Error('file does not start with RIFF id') + if self._file.read(4) != b'WAVE': + raise Error('not a WAVE file') + self._fmt_chunk_read = 0 + self._data_chunk = None + while 1: + self._data_seek_needed = 1 + try: + chunk = Chunk(self._file, bigendian = 0) + except EOFError: + break + chunkname = chunk.getname() + if chunkname == b'fmt ': + self._read_fmt_chunk(chunk) + self._fmt_chunk_read = 1 + elif chunkname == b'data': + if not self._fmt_chunk_read: + raise Error('data chunk before fmt chunk') + self._data_chunk = chunk + self._nframes = chunk.chunksize // self._framesize + self._data_seek_needed = 0 + break + chunk.skip() + if not self._fmt_chunk_read or not self._data_chunk: + raise Error('fmt chunk and/or data chunk missing') + + def __init__(self, f): + self._i_opened_the_file = None + if isinstance(f, str): + f = builtins.open(f, 'rb') + self._i_opened_the_file = f + # else, assume it is an open file object already + try: + self.initfp(f) + except: + if self._i_opened_the_file: + f.close() + raise + + def __del__(self): + self.close() + + def __enter__(self): + return self + + def __exit__(self, *args): + self.close() + + # + # User visible methods. + # + def getfp(self): + return self._file + + def rewind(self): + self._data_seek_needed = 1 + self._soundpos = 0 + + def close(self): + self._file = None + file = self._i_opened_the_file + if file: + self._i_opened_the_file = None + file.close() + + def tell(self): + return self._soundpos + + def getnchannels(self): + return self._nchannels + + def getnframes(self): + return self._nframes + + def getsampwidth(self): + return self._sampwidth + + def getframerate(self): + return self._framerate + + def getcomptype(self): + return self._comptype + + def getcompname(self): + return self._compname + + def getparams(self): + return _wave_params(self.getnchannels(), self.getsampwidth(), + self.getframerate(), self.getnframes(), + self.getcomptype(), self.getcompname()) + + def getmarkers(self): + return None + + def getmark(self, id): + raise Error('no marks') + + def setpos(self, pos): + if pos < 0 or pos > self._nframes: + raise Error('position not in range') + self._soundpos = pos + self._data_seek_needed = 1 + + def readframes(self, nframes): + if self._data_seek_needed: + self._data_chunk.seek(0, 0) + pos = self._soundpos * self._framesize + if pos: + self._data_chunk.seek(pos, 0) + self._data_seek_needed = 0 + if nframes == 0: + return b'' + data = self._data_chunk.read(nframes * self._framesize) + if self._sampwidth != 1 and sys.byteorder == 'big': + data = audioop.byteswap(data, self._sampwidth) + if self._convert and data: + data = self._convert(data) + self._soundpos = self._soundpos + len(data) // (self._nchannels * self._sampwidth) + return data + + # + # Internal methods. + # + + def _read_fmt_chunk(self, chunk): + try: + wFormatTag, self._nchannels, self._framerate, dwAvgBytesPerSec, wBlockAlign = struct.unpack_from(' 4: + raise Error('bad sample width') + self._sampwidth = sampwidth + + def getsampwidth(self): + if not self._sampwidth: + raise Error('sample width not set') + return self._sampwidth + + def setframerate(self, framerate): + if self._datawritten: + raise Error('cannot change parameters after starting to write') + if framerate <= 0: + raise Error('bad frame rate') + self._framerate = int(round(framerate)) + + def getframerate(self): + if not self._framerate: + raise Error('frame rate not set') + return self._framerate + + def setnframes(self, nframes): + if self._datawritten: + raise Error('cannot change parameters after starting to write') + self._nframes = nframes + + def getnframes(self): + return self._nframeswritten + + def setcomptype(self, comptype, compname): + if self._datawritten: + raise Error('cannot change parameters after starting to write') + if comptype not in ('NONE',): + raise Error('unsupported compression type') + self._comptype = comptype + self._compname = compname + + def getcomptype(self): + return self._comptype + + def getcompname(self): + return self._compname + + def setparams(self, params): + nchannels, sampwidth, framerate, nframes, comptype, compname = params + if self._datawritten: + raise Error('cannot change parameters after starting to write') + self.setnchannels(nchannels) + self.setsampwidth(sampwidth) + self.setframerate(framerate) + self.setnframes(nframes) + self.setcomptype(comptype, compname) + + def getparams(self): + if not self._nchannels or not self._sampwidth or not self._framerate: + raise Error('not all parameters set') + return _wave_params(self._nchannels, self._sampwidth, self._framerate, + self._nframes, self._comptype, self._compname) + + def setmark(self, id, pos, name): + raise Error('setmark() not supported') + + def getmark(self, id): + raise Error('no marks') + + def getmarkers(self): + return None + + def tell(self): + return self._nframeswritten + + def writeframesraw(self, data): + if not isinstance(data, (bytes, bytearray)): + data = memoryview(data).cast('B') + self._ensure_header_written(len(data)) + nframes = len(data) // (self._sampwidth * self._nchannels) + if self._convert: + data = self._convert(data) + if self._sampwidth != 1 and sys.byteorder == 'big': + data = audioop.byteswap(data, self._sampwidth) + self._file.write(data) + self._datawritten += len(data) + self._nframeswritten = self._nframeswritten + nframes + + def writeframes(self, data): + self.writeframesraw(data) + if self._datalength != self._datawritten: + self._patchheader() + + def close(self): + try: + if self._file: + self._ensure_header_written(0) + if self._datalength != self._datawritten: + self._patchheader() + self._file.flush() + finally: + self._file = None + file = self._i_opened_the_file + if file: + self._i_opened_the_file = None + file.close() + + # + # Internal methods. + # + + def _ensure_header_written(self, datasize): + if not self._headerwritten: + if not self._nchannels: + raise Error('# channels not specified') + if not self._sampwidth: + raise Error('sample width not specified') + if not self._framerate: + raise Error('sampling rate not specified') + self._write_header(datasize) + + def _write_header(self, initlength): + assert not self._headerwritten + self._file.write(b'RIFF') + if not self._nframes: + self._nframes = initlength // (self._nchannels * self._sampwidth) + self._datalength = self._nframes * self._nchannels * self._sampwidth + try: + self._form_length_pos = self._file.tell() + except (AttributeError, OSError): + self._form_length_pos = None + self._file.write(struct.pack(' zipimporter object + + Create a new zipimporter instance. 'archivepath' must be a path to + a zipfile, or to a specific path inside a zipfile. For example, it can be + '/tmp/myimport.zip', or '/tmp/myimport.zip/mydirectory', if mydirectory is a + valid directory inside the archive. + + 'ZipImportError is raised if 'archivepath' doesn't point to a valid Zip + archive. + + The 'archive' attribute of zipimporter objects contains the name of the + zipfile targeted. + """ + + # Split the "subdirectory" from the Zip archive path, lookup a matching + # entry in sys.path_importer_cache, fetch the file directory from there + # if found, or else read it from the archive. + def __init__(self, path): + if not isinstance(path, str): + import os + path = os.fsdecode(path) + if not path: + raise ZipImportError('archive path is empty', path=path) + if alt_path_sep: + path = path.replace(alt_path_sep, path_sep) + + prefix = [] + while True: + try: + st = _bootstrap_external._path_stat(path) + except (OSError, ValueError): + # On Windows a ValueError is raised for too long paths. + # Back up one path element. + dirname, basename = _bootstrap_external._path_split(path) + if dirname == path: + raise ZipImportError('not a Zip file', path=path) + path = dirname + prefix.append(basename) + else: + # it exists + if (st.st_mode & 0o170000) != 0o100000: # stat.S_ISREG + # it's a not file + raise ZipImportError('not a Zip file', path=path) + break + + try: + files = _zip_directory_cache[path] + except KeyError: + files = _read_directory(path) + _zip_directory_cache[path] = files + self._files = files + self.archive = path + # a prefix directory following the ZIP file path. + self.prefix = _bootstrap_external._path_join(*prefix[::-1]) + if self.prefix: + self.prefix += path_sep + + + # Check whether we can satisfy the import of the module named by + # 'fullname', or whether it could be a portion of a namespace + # package. Return self if we can load it, a string containing the + # full path if it's a possible namespace portion, None if we + # can't load it. + def find_loader(self, fullname, path=None): + """find_loader(fullname, path=None) -> self, str or None. + + Search for a module specified by 'fullname'. 'fullname' must be the + fully qualified (dotted) module name. It returns the zipimporter + instance itself if the module was found, a string containing the + full path name if it's possibly a portion of a namespace package, + or None otherwise. The optional 'path' argument is ignored -- it's + there for compatibility with the importer protocol. + """ + mi = _get_module_info(self, fullname) + if mi is not None: + # This is a module or package. + return self, [] + + # Not a module or regular package. See if this is a directory, and + # therefore possibly a portion of a namespace package. + + # We're only interested in the last path component of fullname + # earlier components are recorded in self.prefix. + modpath = _get_module_path(self, fullname) + if _is_dir(self, modpath): + # This is possibly a portion of a namespace + # package. Return the string representing its path, + # without a trailing separator. + return None, [f'{self.archive}{path_sep}{modpath}'] + + return None, [] + + + # Check whether we can satisfy the import of the module named by + # 'fullname'. Return self if we can, None if we can't. + def find_module(self, fullname, path=None): + """find_module(fullname, path=None) -> self or None. + + Search for a module specified by 'fullname'. 'fullname' must be the + fully qualified (dotted) module name. It returns the zipimporter + instance itself if the module was found, or None if it wasn't. + The optional 'path' argument is ignored -- it's there for compatibility + with the importer protocol. + """ + return self.find_loader(fullname, path)[0] + + + def get_code(self, fullname): + """get_code(fullname) -> code object. + + Return the code object for the specified module. Raise ZipImportError + if the module couldn't be found. + """ + code, ispackage, modpath = _get_module_code(self, fullname) + return code + + + def get_data(self, pathname): + """get_data(pathname) -> string with file data. + + Return the data associated with 'pathname'. Raise OSError if + the file wasn't found. + """ + if alt_path_sep: + pathname = pathname.replace(alt_path_sep, path_sep) + + key = pathname + if pathname.startswith(self.archive + path_sep): + key = pathname[len(self.archive + path_sep):] + + try: + toc_entry = self._files[key] + except KeyError: + raise OSError(0, '', key) + return _get_data(self.archive, toc_entry) + + + # Return a string matching __file__ for the named module + def get_filename(self, fullname): + """get_filename(fullname) -> filename string. + + Return the filename for the specified module. + """ + # Deciding the filename requires working out where the code + # would come from if the module was actually loaded + code, ispackage, modpath = _get_module_code(self, fullname) + return modpath + + + def get_source(self, fullname): + """get_source(fullname) -> source string. + + Return the source code for the specified module. Raise ZipImportError + if the module couldn't be found, return None if the archive does + contain the module, but has no source for it. + """ + mi = _get_module_info(self, fullname) + if mi is None: + raise ZipImportError(f"can't find module {fullname!r}", name=fullname) + + path = _get_module_path(self, fullname) + if mi: + fullpath = _bootstrap_external._path_join(path, '__init__.py') + else: + fullpath = f'{path}.py' + + try: + toc_entry = self._files[fullpath] + except KeyError: + # we have the module, but no source + return None + return _get_data(self.archive, toc_entry).decode() + + + # Return a bool signifying whether the module is a package or not. + def is_package(self, fullname): + """is_package(fullname) -> bool. + + Return True if the module specified by fullname is a package. + Raise ZipImportError if the module couldn't be found. + """ + mi = _get_module_info(self, fullname) + if mi is None: + raise ZipImportError(f"can't find module {fullname!r}", name=fullname) + return mi + + + # Load and return the module named by 'fullname'. + def load_module(self, fullname): + """load_module(fullname) -> module. + + Load the module specified by 'fullname'. 'fullname' must be the + fully qualified (dotted) module name. It returns the imported + module, or raises ZipImportError if it wasn't found. + """ + code, ispackage, modpath = _get_module_code(self, fullname) + mod = sys.modules.get(fullname) + if mod is None or not isinstance(mod, _module_type): + mod = _module_type(fullname) + sys.modules[fullname] = mod + mod.__loader__ = self + + try: + if ispackage: + # add __path__ to the module *before* the code gets + # executed + path = _get_module_path(self, fullname) + fullpath = _bootstrap_external._path_join(self.archive, path) + mod.__path__ = [fullpath] + + if not hasattr(mod, '__builtins__'): + mod.__builtins__ = __builtins__ + _bootstrap_external._fix_up_module(mod.__dict__, fullname, modpath) + exec(code, mod.__dict__) + except: + del sys.modules[fullname] + raise + + try: + mod = sys.modules[fullname] + except KeyError: + raise ImportError(f'Loaded module {fullname!r} not found in sys.modules') + _bootstrap._verbose_message('import {} # loaded from Zip {}', fullname, modpath) + return mod + + + def get_resource_reader(self, fullname): + """Return the ResourceReader for a package in a zip file. + + If 'fullname' is a package within the zip file, return the + 'ResourceReader' object for the package. Otherwise return None. + """ + try: + if not self.is_package(fullname): + return None + except ZipImportError: + return None + if not _ZipImportResourceReader._registered: + from importlib.abc import ResourceReader + ResourceReader.register(_ZipImportResourceReader) + _ZipImportResourceReader._registered = True + return _ZipImportResourceReader(self, fullname) + + + def __repr__(self): + return f'' + + +# _zip_searchorder defines how we search for a module in the Zip +# archive: we first search for a package __init__, then for +# non-package .pyc, and .py entries. The .pyc entries +# are swapped by initzipimport() if we run in optimized mode. Also, +# '/' is replaced by path_sep there. +_zip_searchorder = ( + (path_sep + '__init__.pyc', True, True), + (path_sep + '__init__.py', False, True), + ('.pyc', True, False), + ('.py', False, False), +) + +# Given a module name, return the potential file path in the +# archive (without extension). +def _get_module_path(self, fullname): + return self.prefix + fullname.rpartition('.')[2] + +# Does this path represent a directory? +def _is_dir(self, path): + # See if this is a "directory". If so, it's eligible to be part + # of a namespace package. We test by seeing if the name, with an + # appended path separator, exists. + dirpath = path + path_sep + # If dirpath is present in self._files, we have a directory. + return dirpath in self._files + +# Return some information about a module. +def _get_module_info(self, fullname): + path = _get_module_path(self, fullname) + for suffix, isbytecode, ispackage in _zip_searchorder: + fullpath = path + suffix + if fullpath in self._files: + return ispackage + return None + + +# implementation + +# _read_directory(archive) -> files dict (new reference) +# +# Given a path to a Zip archive, build a dict, mapping file names +# (local to the archive, using SEP as a separator) to toc entries. +# +# A toc_entry is a tuple: +# +# (__file__, # value to use for __file__, available for all files, +# # encoded to the filesystem encoding +# compress, # compression kind; 0 for uncompressed +# data_size, # size of compressed data on disk +# file_size, # size of decompressed data +# file_offset, # offset of file header from start of archive +# time, # mod time of file (in dos format) +# date, # mod data of file (in dos format) +# crc, # crc checksum of the data +# ) +# +# Directories can be recognized by the trailing path_sep in the name, +# data_size and file_offset are 0. +def _read_directory(archive): + try: + fp = _io.open_code(archive) + except OSError: + raise ZipImportError(f"can't open Zip file: {archive!r}", path=archive) + + with fp: + try: + fp.seek(-END_CENTRAL_DIR_SIZE, 2) + header_position = fp.tell() + buffer = fp.read(END_CENTRAL_DIR_SIZE) + except OSError: + raise ZipImportError(f"can't read Zip file: {archive!r}", path=archive) + if len(buffer) != END_CENTRAL_DIR_SIZE: + raise ZipImportError(f"can't read Zip file: {archive!r}", path=archive) + if buffer[:4] != STRING_END_ARCHIVE: + # Bad: End of Central Dir signature + # Check if there's a comment. + try: + fp.seek(0, 2) + file_size = fp.tell() + except OSError: + raise ZipImportError(f"can't read Zip file: {archive!r}", + path=archive) + max_comment_start = max(file_size - MAX_COMMENT_LEN - + END_CENTRAL_DIR_SIZE, 0) + try: + fp.seek(max_comment_start) + data = fp.read() + except OSError: + raise ZipImportError(f"can't read Zip file: {archive!r}", + path=archive) + pos = data.rfind(STRING_END_ARCHIVE) + if pos < 0: + raise ZipImportError(f'not a Zip file: {archive!r}', + path=archive) + buffer = data[pos:pos+END_CENTRAL_DIR_SIZE] + if len(buffer) != END_CENTRAL_DIR_SIZE: + raise ZipImportError(f"corrupt Zip file: {archive!r}", + path=archive) + header_position = file_size - len(data) + pos + + header_size = _unpack_uint32(buffer[12:16]) + header_offset = _unpack_uint32(buffer[16:20]) + if header_position < header_size: + raise ZipImportError(f'bad central directory size: {archive!r}', path=archive) + if header_position < header_offset: + raise ZipImportError(f'bad central directory offset: {archive!r}', path=archive) + header_position -= header_size + arc_offset = header_position - header_offset + if arc_offset < 0: + raise ZipImportError(f'bad central directory size or offset: {archive!r}', path=archive) + + files = {} + # Start of Central Directory + count = 0 + try: + fp.seek(header_position) + except OSError: + raise ZipImportError(f"can't read Zip file: {archive!r}", path=archive) + while True: + buffer = fp.read(46) + if len(buffer) < 4: + raise EOFError('EOF read where not expected') + # Start of file header + if buffer[:4] != b'PK\x01\x02': + break # Bad: Central Dir File Header + if len(buffer) != 46: + raise EOFError('EOF read where not expected') + flags = _unpack_uint16(buffer[8:10]) + compress = _unpack_uint16(buffer[10:12]) + time = _unpack_uint16(buffer[12:14]) + date = _unpack_uint16(buffer[14:16]) + crc = _unpack_uint32(buffer[16:20]) + data_size = _unpack_uint32(buffer[20:24]) + file_size = _unpack_uint32(buffer[24:28]) + name_size = _unpack_uint16(buffer[28:30]) + extra_size = _unpack_uint16(buffer[30:32]) + comment_size = _unpack_uint16(buffer[32:34]) + file_offset = _unpack_uint32(buffer[42:46]) + header_size = name_size + extra_size + comment_size + if file_offset > header_offset: + raise ZipImportError(f'bad local header offset: {archive!r}', path=archive) + file_offset += arc_offset + + try: + name = fp.read(name_size) + except OSError: + raise ZipImportError(f"can't read Zip file: {archive!r}", path=archive) + if len(name) != name_size: + raise ZipImportError(f"can't read Zip file: {archive!r}", path=archive) + # On Windows, calling fseek to skip over the fields we don't use is + # slower than reading the data because fseek flushes stdio's + # internal buffers. See issue #8745. + try: + if len(fp.read(header_size - name_size)) != header_size - name_size: + raise ZipImportError(f"can't read Zip file: {archive!r}", path=archive) + except OSError: + raise ZipImportError(f"can't read Zip file: {archive!r}", path=archive) + + if flags & 0x800: + # UTF-8 file names extension + name = name.decode() + else: + # Historical ZIP filename encoding + try: + name = name.decode('ascii') + except UnicodeDecodeError: + name = name.decode('latin1').translate(cp437_table) + + name = name.replace('/', path_sep) + path = _bootstrap_external._path_join(archive, name) + t = (path, compress, data_size, file_size, file_offset, time, date, crc) + files[name] = t + count += 1 + _bootstrap._verbose_message('zipimport: found {} names in {!r}', count, archive) + return files + +# During bootstrap, we may need to load the encodings +# package from a ZIP file. But the cp437 encoding is implemented +# in Python in the encodings package. +# +# Break out of this dependency by using the translation table for +# the cp437 encoding. +cp437_table = ( + # ASCII part, 8 rows x 16 chars + '\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f' + '\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f' + ' !"#$%&\'()*+,-./' + '0123456789:;<=>?' + '@ABCDEFGHIJKLMNO' + 'PQRSTUVWXYZ[\\]^_' + '`abcdefghijklmno' + 'pqrstuvwxyz{|}~\x7f' + # non-ASCII part, 16 rows x 8 chars + '\xc7\xfc\xe9\xe2\xe4\xe0\xe5\xe7' + '\xea\xeb\xe8\xef\xee\xec\xc4\xc5' + '\xc9\xe6\xc6\xf4\xf6\xf2\xfb\xf9' + '\xff\xd6\xdc\xa2\xa3\xa5\u20a7\u0192' + '\xe1\xed\xf3\xfa\xf1\xd1\xaa\xba' + '\xbf\u2310\xac\xbd\xbc\xa1\xab\xbb' + '\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556' + '\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510' + '\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f' + '\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567' + '\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b' + '\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580' + '\u03b1\xdf\u0393\u03c0\u03a3\u03c3\xb5\u03c4' + '\u03a6\u0398\u03a9\u03b4\u221e\u03c6\u03b5\u2229' + '\u2261\xb1\u2265\u2264\u2320\u2321\xf7\u2248' + '\xb0\u2219\xb7\u221a\u207f\xb2\u25a0\xa0' +) + +_importing_zlib = False + +# Return the zlib.decompress function object, or NULL if zlib couldn't +# be imported. The function is cached when found, so subsequent calls +# don't import zlib again. +def _get_decompress_func(): + global _importing_zlib + if _importing_zlib: + # Someone has a zlib.py[co] in their Zip file + # let's avoid a stack overflow. + _bootstrap._verbose_message('zipimport: zlib UNAVAILABLE') + raise ZipImportError("can't decompress data; zlib not available") + + _importing_zlib = True + try: + from zlib import decompress + except Exception: + _bootstrap._verbose_message('zipimport: zlib UNAVAILABLE') + raise ZipImportError("can't decompress data; zlib not available") + finally: + _importing_zlib = False + + _bootstrap._verbose_message('zipimport: zlib available') + return decompress + +# Given a path to a Zip file and a toc_entry, return the (uncompressed) data. +def _get_data(archive, toc_entry): + datapath, compress, data_size, file_size, file_offset, time, date, crc = toc_entry + if data_size < 0: + raise ZipImportError('negative data size') + + with _io.open_code(archive) as fp: + # Check to make sure the local file header is correct + try: + fp.seek(file_offset) + except OSError: + raise ZipImportError(f"can't read Zip file: {archive!r}", path=archive) + buffer = fp.read(30) + if len(buffer) != 30: + raise EOFError('EOF read where not expected') + + if buffer[:4] != b'PK\x03\x04': + # Bad: Local File Header + raise ZipImportError(f'bad local file header: {archive!r}', path=archive) + + name_size = _unpack_uint16(buffer[26:28]) + extra_size = _unpack_uint16(buffer[28:30]) + header_size = 30 + name_size + extra_size + file_offset += header_size # Start of file data + try: + fp.seek(file_offset) + except OSError: + raise ZipImportError(f"can't read Zip file: {archive!r}", path=archive) + raw_data = fp.read(data_size) + if len(raw_data) != data_size: + raise OSError("zipimport: can't read data") + + if compress == 0: + # data is not compressed + return raw_data + + # Decompress with zlib + try: + decompress = _get_decompress_func() + except Exception: + raise ZipImportError("can't decompress data; zlib not available") + return decompress(raw_data, -15) + + +# Lenient date/time comparison function. The precision of the mtime +# in the archive is lower than the mtime stored in a .pyc: we +# must allow a difference of at most one second. +def _eq_mtime(t1, t2): + # dostime only stores even seconds, so be lenient + return abs(t1 - t2) <= 1 + + +# Given the contents of a .py[co] file, unmarshal the data +# and return the code object. Return None if it the magic word doesn't +# match, or if the recorded .py[co] metadata does not match the source, +# (we do this instead of raising an exception as we fall back +# to .py if available and we don't want to mask other errors). +def _unmarshal_code(self, pathname, fullpath, fullname, data): + exc_details = { + 'name': fullname, + 'path': fullpath, + } + + try: + flags = _bootstrap_external._classify_pyc(data, fullname, exc_details) + except ImportError: + return None + + hash_based = flags & 0b1 != 0 + if hash_based: + check_source = flags & 0b10 != 0 + if (_imp.check_hash_based_pycs != 'never' and + (check_source or _imp.check_hash_based_pycs == 'always')): + source_bytes = _get_pyc_source(self, fullpath) + if source_bytes is not None: + source_hash = _imp.source_hash( + _bootstrap_external._RAW_MAGIC_NUMBER, + source_bytes, + ) + + try: + _bootstrap_external._validate_hash_pyc( + data, source_hash, fullname, exc_details) + except ImportError: + return None + else: + source_mtime, source_size = \ + _get_mtime_and_size_of_source(self, fullpath) + + if source_mtime: + # We don't use _bootstrap_external._validate_timestamp_pyc + # to allow for a more lenient timestamp check. + if (not _eq_mtime(_unpack_uint32(data[8:12]), source_mtime) or + _unpack_uint32(data[12:16]) != source_size): + _bootstrap._verbose_message( + f'bytecode is stale for {fullname!r}') + return None + + code = marshal.loads(data[16:]) + if not isinstance(code, _code_type): + raise TypeError(f'compiled module {pathname!r} is not a code object') + return code + +_code_type = type(_unmarshal_code.__code__) + + +# Replace any occurrences of '\r\n?' in the input string with '\n'. +# This converts DOS and Mac line endings to Unix line endings. +def _normalize_line_endings(source): + source = source.replace(b'\r\n', b'\n') + source = source.replace(b'\r', b'\n') + return source + +# Given a string buffer containing Python source code, compile it +# and return a code object. +def _compile_source(pathname, source): + source = _normalize_line_endings(source) + return compile(source, pathname, 'exec', dont_inherit=True) + +# Convert the date/time values found in the Zip archive to a value +# that's compatible with the time stamp stored in .pyc files. +def _parse_dostime(d, t): + return time.mktime(( + (d >> 9) + 1980, # bits 9..15: year + (d >> 5) & 0xF, # bits 5..8: month + d & 0x1F, # bits 0..4: day + t >> 11, # bits 11..15: hours + (t >> 5) & 0x3F, # bits 8..10: minutes + (t & 0x1F) * 2, # bits 0..7: seconds / 2 + -1, -1, -1)) + +# Given a path to a .pyc file in the archive, return the +# modification time of the matching .py file and its size, +# or (0, 0) if no source is available. +def _get_mtime_and_size_of_source(self, path): + try: + # strip 'c' or 'o' from *.py[co] + assert path[-1:] in ('c', 'o') + path = path[:-1] + toc_entry = self._files[path] + # fetch the time stamp of the .py file for comparison + # with an embedded pyc time stamp + time = toc_entry[5] + date = toc_entry[6] + uncompressed_size = toc_entry[3] + return _parse_dostime(date, time), uncompressed_size + except (KeyError, IndexError, TypeError): + return 0, 0 + + +# Given a path to a .pyc file in the archive, return the +# contents of the matching .py file, or None if no source +# is available. +def _get_pyc_source(self, path): + # strip 'c' or 'o' from *.py[co] + assert path[-1:] in ('c', 'o') + path = path[:-1] + + try: + toc_entry = self._files[path] + except KeyError: + return None + else: + return _get_data(self.archive, toc_entry) + + +# Get the code object associated with the module specified by +# 'fullname'. +def _get_module_code(self, fullname): + path = _get_module_path(self, fullname) + for suffix, isbytecode, ispackage in _zip_searchorder: + fullpath = path + suffix + _bootstrap._verbose_message('trying {}{}{}', self.archive, path_sep, fullpath, verbosity=2) + try: + toc_entry = self._files[fullpath] + except KeyError: + pass + else: + modpath = toc_entry[0] + data = _get_data(self.archive, toc_entry) + if isbytecode: + code = _unmarshal_code(self, modpath, fullpath, fullname, data) + else: + code = _compile_source(modpath, data) + if code is None: + # bad magic number or non-matching mtime + # in byte code, try next + continue + modpath = toc_entry[0] + return code, ispackage, modpath + else: + raise ZipImportError(f"can't find module {fullname!r}", name=fullname) + + +class _ZipImportResourceReader: + """Private class used to support ZipImport.get_resource_reader(). + + This class is allowed to reference all the innards and private parts of + the zipimporter. + """ + _registered = False + + def __init__(self, zipimporter, fullname): + self.zipimporter = zipimporter + self.fullname = fullname + + def open_resource(self, resource): + fullname_as_path = self.fullname.replace('.', '/') + path = f'{fullname_as_path}/{resource}' + from io import BytesIO + try: + return BytesIO(self.zipimporter.get_data(path)) + except OSError: + raise FileNotFoundError(path) + + def resource_path(self, resource): + # All resources are in the zip file, so there is no path to the file. + # Raising FileNotFoundError tells the higher level API to extract the + # binary data and create a temporary file. + raise FileNotFoundError + + def is_resource(self, name): + # Maybe we could do better, but if we can get the data, it's a + # resource. Otherwise it isn't. + fullname_as_path = self.fullname.replace('.', '/') + path = f'{fullname_as_path}/{name}' + try: + self.zipimporter.get_data(path) + except OSError: + return False + return True + + def contents(self): + # This is a bit convoluted, because fullname will be a module path, + # but _files is a list of file names relative to the top of the + # archive's namespace. We want to compare file paths to find all the + # names of things inside the module represented by fullname. So we + # turn the module path of fullname into a file path relative to the + # top of the archive, and then we iterate through _files looking for + # names inside that "directory". + from pathlib import Path + fullname_path = Path(self.zipimporter.get_filename(self.fullname)) + relative_path = fullname_path.relative_to(self.zipimporter.archive) + # Don't forget that fullname names a package, so its path will include + # __init__.py, which we want to ignore. + assert relative_path.name == '__init__.py' + package_path = relative_path.parent + subdirs_seen = set() + for filename in self.zipimporter._files: + try: + relative = Path(filename).relative_to(package_path) + except ValueError: + continue + # If the path of the file (which is relative to the top of the zip + # namespace), relative to the package given when the resource + # reader was created, has a parent, then it's a name in a + # subdirectory and thus we skip it. + parent_name = relative.parent.name + if len(parent_name) == 0: + yield relative.name + elif parent_name not in subdirs_seen: + subdirs_seen.add(parent_name) + yield parent_name